// Convert the loose Aristotle FixtureData (M1 prototype port) into a strict // SysMLModel that the validator and dep-graph can operate on. import type { SysMLModel, Block, Association, Constraint, Requirement, AssociationKind, } from "./model"; import type { FixtureData } from "../fixtures/aristotle"; export function fromFixture(data: FixtureData): SysMLModel { const constraintBlocks = data.blocks.filter(b => b.kind === "constraint"); const constraints: Constraint[] = constraintBlocks.map(b => ({ id: b.id, label: b.label, expression: "{ tenancy = institutional }", // The fixture infers "what does this constraint apply to?" from its // outgoing/incoming `kind: "constraint"` associations. appliesTo: data.associations .filter(a => a.kind === "constraint" && (a.from === b.id || a.to === b.id)) .map(a => (a.from === b.id ? a.to : a.from)), })); const blocks: Block[] = data.blocks .filter(b => b.kind !== "constraint") .map(b => ({ id: b.id, label: b.label, kind: b.kind, stereotypes: [b.kind], properties: b.properties.map((name, i) => ({ id: `p${i + 1}`, name, type: { kind: "string" } as const, multiplicity: "0..1" as const, })), })); const associations: Association[] = data.associations .filter(a => a.kind !== "constraint") .map(a => ({ id: a.id, fromBlockId: a.from, toBlockId: a.to, label: a.label, kind: mapAssocKind(a.kind), })); // Requirements come from the narrative; the fixture chips reference REQ-001..003 // but the requirements themselves aren't stored. Fabricate them here so the // validator has something meaningful to operate on. const requirements: Requirement[] = [ { id: "req-001", tag: "REQ-001", text: "Must never output a complete solution to a graded problem.", relations: [{ kind: "satisfy", blockId: "aristotle" }], }, { id: "req-002", tag: "REQ-002", text: "Response latency under 1.2s P50 to preserve flow.", relations: [{ kind: "satisfy", blockId: "aristotle" }], }, { id: "req-003", tag: "REQ-003", text: "Operates within FERPA boundaries; coursework never leaves institutional tenancy.", // Intentionally untraced in the fixture — see the rail's "untraced" dot relations: [], }, ]; return { blocks, associations, constraints, requirements, }; } function mapAssocKind(k: "association" | "composition" | "constraint"): AssociationKind { if (k === "constraint") return "constraintApplies"; return k; }