MVP M4: SysML metamodel + validator + dependency graph + UI surfacing

apps/web/lib/sysml — pure engine (no UI deps)
- model.ts: canonical types ported from phase-0 (Block, Property,
  Association, Constraint, Requirement, SysMLModel)
- validate.ts: 12 rules grouped Structural (S1–S5) / Semantic (M1–M5) /
  Traceability (T1–T2). Pure function returning element-anchored issues.
  Cycle detection via DFS with canonical-key dedup. T3 deferred to Phase 1.5
  (depends on narrative chip references).
- depgraph.ts: directed graph over Blocks / Properties / Constraints /
  Requirements with `dependentsOf()` and `neighborhood()` for impact analysis
  (substrate for M7).
- fromFixture.ts: loose FixtureData → strict SysMLModel converter (interim
  until M5 unifies state).
- breaks.ts: 8 named model corruptions (S1/S2/S3/M2/M3/M5/T1/T2) for
  ?break=... demonstrations.

UI integration in EditorShell + LeftRail + DiagramCanvas + IssuesPanel
- Floating IssuesPanel (bottom-right) with severity-grouped counts and
  collapse. Click an issue to focus its anchor across the rail and diagram.
- LeftRail: severity dots on each block in the Model section and on each
  requirement entry. Tooltips show full issue text.
- DiagramCanvas: red/blue/dotted ring around offending blocks via
  issuesByElement → BlockNode.data.issueSeverity.
- ?break=S1-dangling,M2-cycle,M5-dup-property,... query param injects
  corruptions for verification of the "done when" criterion.
- Suspense boundary added at the editor route for useSearchParams.

Two pre-existing TipTap typing fixes (editor.storage cast through unknown).
This commit is contained in:
2026-04-28 23:22:24 +02:00
parent a0566ce64c
commit 2052a280b1
14 changed files with 1131 additions and 45 deletions

View File

@@ -1,8 +1,16 @@
import { Suspense } from "react";
import { EditorShell } from "../../../components/editor/EditorShell";
import { aristotleFixture } from "../../../lib/fixtures/aristotle";
// M1: every projectId resolves to the Aristotle fixture.
// M4M5 wire this to a real database lookup.
//
// Suspense boundary required because EditorShell uses useSearchParams (for
// the M4 ?break=... demo of validation rules).
export default async function EditorPage(_props: { params: Promise<{ projectId: string }> }) {
return <EditorShell data={aristotleFixture} />;
return (
<Suspense fallback={null}>
<EditorShell data={aristotleFixture} />
</Suspense>
);
}