// Canonical SysML metamodel types — adapted from phase-0/src/types.ts // (the Phase 0 validation that ran 10/10 against the corpus). // // See docs/sysml-modeling.md §6 for the design rationale and §3 for what we // deliberately don't model. export type BlockKind = "block" | "actor" | "constraint" | "system"; /** Review state for AI-suggested model elements. Default (when absent) is * "accepted" — equivalent to a user-confirmed element. * * suggested — added by the analyzer in the latest run, awaits keep/discard * accepted — confirmed (user kept, or pre-existing before merge-with-review) * deprecated — analyzer didn't see in the latest run, awaits keep/discard * * `reviewPinned` flips on when the user explicitly keeps a deprecated * element so future merges leave it alone. */ export type ReviewStatus = "suggested" | "accepted" | "deprecated"; export type PropertyType = | { kind: "string" } | { kind: "number" } | { kind: "boolean" } | { kind: "enum"; values: string[] }; export type Multiplicity = "0..1" | "1" | "0..*" | "1..*"; export interface Property { id: string; name: string; type: PropertyType; multiplicity: Multiplicity; description?: string; } export interface Block { id: string; label: string; kind: BlockKind; stereotypes: string[]; properties: Property[]; description?: string; /// If this block was created by dragging a taxonomy / glossary term onto /// the canvas (or matched to one by the model analyze pass), this points /// at the source TaxonomyTerm. Drives the "linked" indicator in sidebars. linkedTermId?: string; /** Review state. Absent = "accepted". */ reviewStatus?: ReviewStatus; /** User explicitly kept despite analyzer not seeing it → don't re-deprecate. */ reviewPinned?: boolean; } export type AssociationKind = | "association" | "composition" | "aggregation" | "generalization" | "constraintApplies"; export interface Association { id: string; fromBlockId: string; toBlockId: string; label: string; kind: AssociationKind; multiplicity?: { from: Multiplicity; to: Multiplicity }; /// Term that conceptually names this relationship (e.g. "tutors", /// "evaluates"). Set by the analyze pass or by a Promote action; cleared /// when the term is deleted (cross-validation rule X1). linkedTermId?: string; reviewStatus?: ReviewStatus; reviewPinned?: boolean; } export interface Constraint { id: string; label: string; expression: string; appliesTo: string[]; /// Term that conceptually names this rule (e.g. "Daily Cap", "Eligibility"). linkedTermId?: string; reviewStatus?: ReviewStatus; reviewPinned?: boolean; } export type RequirementRelation = | { kind: "satisfy"; blockId: string } | { kind: "derive"; fromReqId: string } | { kind: "verify"; experimentId: string }; export interface Requirement { id: string; tag: string; text: string; relations: RequirementRelation[]; /// Term this requirement is "about" (e.g. REQ-001 ‘personalization’ links to /// the term Personalization). Distinct from `relations.satisfy` which links /// to a block — a requirement can be about a concept that has no block yet. linkedTermId?: string; reviewStatus?: ReviewStatus; reviewPinned?: boolean; } export interface SysMLModel { systemOfInterestId?: string; blocks: Block[]; associations: Association[]; constraints: Constraint[]; requirements: Requirement[]; }