Files
Socrates/apps/web/lib/sysml/model.ts
dtoro b55425cc68 Pivot to text-first column-stack workspace + merge-with-review across AI artifacts
Workspace
- Pivot from "set of open panes" to a Finder-style miller column stack:
  TopBar / LeftSidebar / [section → entity → entity ...] / pinned text editor.
  openPanesStore is now an ordered Column[] with pushFrom / closeFrom /
  setStack; only one top-level section is rooted at a time.
- New entity column panes: Term, Block, Association, Constraint,
  Requirement, Finding. Click-through navigation truncates deeper
  columns automatically.
- LeftSidebar surfaces a pending-count chip per section (single-glance
  navigation cue) and spins its analyze ↻ via SVG Spinner whenever the
  LLM is working — including server-initiated runs caught by the runs
  poll, not just user-triggered ones.

Analyze pipeline + persistence
- Unified `concepts` pass (taxonomy + glossary in one LLM call) replaces
  the two-pass setup. Server still accepts ?section=taxonomy|glossary
  and normalizes them for back-compat.
- model / requirements / detection (assumptions, risks, inconsistencies)
  + cross-layer validation rules (X1–X4: stale term link, unlinked
  formalism, undefined linked term, prose-only term).
- Persistence: NarrativeDocument, ModelSnapshot, ChangelogEntry,
  TaxonomyTerm, RequirementEntry, Finding, AnalysisRun. Re-runs MERGE
  instead of replace: gentle update on existing items, suggested on new,
  deprecated on missing — same idiom for every artifact kind. User pins
  preserve "kept" decisions across re-analyses.
- Migrations: pivot_text_first, add_requirement_linked_term,
  term_review_state, review_state_for_reqs_and_findings,
  add_term_definition_pinned.

Concept ↔ ontology integration
- linkedTermId on Block / Association / Constraint / Requirement.
  PromoteToolbar lets the user formalize a concept inline: + Block /
  + Association / + Constraint / + Requirement, all routed through
  applyOps so undo/redo and SSE work for free.
- decideElement op for in-canvas keep/discard on review-pending model
  elements.

User-authored definitions
- TermColumn definition is click-to-edit. Save (Cmd-Enter / blur),
  Cancel (Esc), Reset to AI suggestion when pinned.
- definitionPinned flag on TaxonomyTerm: future Analyze runs leave the
  user's text alone. setTermDefinition repo function + POST
  /api/projects/[id]/terms/[termId]/definition endpoint.
- mergeTaxonomySuggestion + applyGlossaryDefinitions both pin-aware.

UX/UI
- StatusChip: single component for all state idioms (suggested,
  deprecated, accepted, dismissed, resolved, severity, validation code,
  confidence, warn). Replaces 5+ ad-hoc badge classes.
- PaneControls (PaneViewTabs + PaneFilterChip): separates view-mode
  toggles from filter chips so toggling Pending no longer flips you off
  the current view.
- PaneEmpty: unified empty-state with title + hint + action.
- PaneDrawer: collapsible groups for Pending / Discarded review; cards
  group as Kept (top) → Pending (bottom drawer) → Discarded (Findings
  only, hidden when empty). Restore action recovers dismissed/resolved
  findings.
- ConceptCard unifies Tree and A–Z views in Concepts; only Tree parents
  carry the chevron (no empty placeholder offset).
- Type + spacing tokens (--text-xs..xl, --space-1..6, --lh-tight/ui/
  prose, --radius-*) replace every ad-hoc value.
- Buttons standardized to body sans 500 (was a mishmash of mono / display).
- Card shells unified across Concepts / Requirements / Findings.

Cleanup
- Removed: LeftRail, FindingsPanel, IssuesPanel, SocratesDock,
  ProposalCard, SlashMenu, SlashExtension, slashSuggestion,
  CanvasHeader, TaxonomyPane, GlossaryPane, TermDetail (popover; now
  TermColumn).
- Section ids in openPanesStore: dropped taxonomy/glossary, added
  concepts. localStorage migration runs on hydrate.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 00:12:06 +02:00

112 lines
3.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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[];
}