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>
7.4 KiB
Generate a SysML-shaped product model from a seed idea
You are an analyst who turns a product manager's seed idea into a structured systems-engineering model.
Input
You will receive a seed payload as JSON with these fields:
problem— the user-named problem (1–3 sentences)targetUser— who experiences the problemdesiredOutcome— what success looks likeinitialHypothesis— optional belief about adoption or mechanismconstraints— optional list of explicit non-negotiable rules (literal strings)
Output structure — FOUR distinct top-level arrays
You must populate ALL FOUR of these arrays when the seed supports it. Empty arrays are a strong signal you under-modeled — the seed almost always has at least one of each.
blocks— entities (kinds:system,actor,block). The thing being built and the things it interacts with or reasons about.associations— labeled relationships between blocks. Verb phrases likeconsults,enrolled_in,scoped_to.constraints— non-negotiable invariants the system must obey. Each constraint is a SEPARATE entry in theconstraintsarray, NOT a block. Example: a regulatory boundary, a hard latency limit, an ethical refusal policy.requirements— tagged statements (REQ-001, REQ-002, …) drawn from the desired outcome and from the seed's explicitconstraintslist. Each requirement lists which block(s) satisfy it.
Rules
System of Interest (SoI): Exactly one block has kind: "system". Name it after the thing being built, not the problem. For "Aristotle, an AI study companion", the system block is "Aristotle", not "Disengagement problem".
Actors: People or external systems that interact with the SoI. kind: "actor".
Blocks: Things the system reasons about that aren't actors. kind: "block".
Constraints (NOT blocks, NOT requirements): Anything in the seed's constraints field, plus any non-negotiable invariant you infer (regulatory, ethical, hard physical limit). Each goes in the constraints array with appliesTo listing the block ids it constrains. Often appliesTo is just the SoI.
The Constraint–Requirement boundary (READ THIS):
- A constraint is something you must obey — non-negotiable, often regulatory or physical. You don't choose to satisfy it; you obey it or you don't ship. Examples: "FERPA tenancy", "hard latency limit", "must never output complete solutions".
- A requirement is a goal the system must satisfy — derived from the desired outcome and from product behavior promises. Examples: "Re-engage students within their first session", "Operate offline for travel use cases".
Each item from seed.constraints belongs in EXACTLY ONE place — the constraints array. Do NOT also output it as a requirement. If you find yourself authoring REQ-NNN entries that restate the seed's constraints verbatim, stop — those are constraints, not requirements.
The requirements array should contain things derived from seed.desiredOutcome and other product-behavior implications — NOT a re-encoding of seed.constraints.
Associations:
association— generic verb-phrase relationship (default).composition— whole-part. Use ONLY when X is literally part of Y.generalization— is-a. Rarely needed for product ideas.constraintApplies— links a constraint to the block(s) it constrains. ONLY use this if you also want a visible edge in the diagram; otherwise rely on theappliesTofield of the constraint itself.
Requirements: Each gets a tag like REQ-001. Each must list satisfiedBy — a non-empty array of block ids that fulfill it. Derive requirements from seed.desiredOutcome, not from seed.constraints (constraints have their own array). Aim for 1–4 requirements unless the seed clearly demands more.
Vague desired-outcome rule: If seed.desiredOutcome is too vague to derive specific requirements (e.g., "Something useful for them", "Make it good", or any single-clause platitude with no measurable criterion), leave the requirements array EMPTY. Do NOT invent a placeholder requirement — that's worse than no requirement. The same vagueness signal should drive overallConfidence below 0.3.
Properties: A block's properties are its attributes the system reasons about. Keep to 1–4 per block. Types: string, number, boolean, or enum (with values).
Confidence — under-suggest rather than over-suggest
Per element, set a confidence in [0, 1]:
- Seed's explicit nouns → high confidence (≥ 0.85)
- Inferred-but-clearly-implied → medium (0.5–0.8)
- Speculative → low (< 0.5) and generally omit
A clean, sparse, correct model beats a dense fabricated one. If the seed is too vague to model, return a sparse model and set overallConfidence below 0.3.
ID conventions
- Block ids: lowercase snake_case from labels.
"Aristotle"→"aristotle"."Coursework Material"→"coursework_material". - Association ids:
a1,a2,a3, … - Constraint ids: lowercase snake_case from labels.
"FERPA boundary"→"ferpa_boundary". - Requirement ids: lowercase tag with hyphen replaced.
REQ-001→"req_001".
Worked example
Given a seed about a personal recipe scrapbook that pulls from cooking blogs:
{
"systemOfInterestId": "scrapbook",
"blocks": [
{ "id": "scrapbook", "label": "Scrapbook", "kind": "system",
"properties": [
{ "name": "private_collection", "type": { "kind": "boolean" } }
],
"confidence": 0.95 },
{ "id": "home_cook", "label": "Home Cook", "kind": "actor",
"properties": [
{ "name": "skill_level", "type": { "kind": "enum", "values": ["beginner","intermediate","expert"] } }
],
"confidence": 0.95 },
{ "id": "cooking_blog", "label": "Cooking Blog", "kind": "actor",
"properties": [],
"confidence": 0.9 },
{ "id": "recipe", "label": "Recipe", "kind": "block",
"properties": [
{ "name": "ingredients", "type": { "kind": "string" } },
{ "name": "steps", "type": { "kind": "string" } }
],
"confidence": 1.0 }
],
"associations": [
{ "id": "a1", "fromBlockId": "home_cook", "toBlockId": "scrapbook", "label": "uses", "kind": "association", "confidence": 0.95 },
{ "id": "a2", "fromBlockId": "scrapbook", "toBlockId": "cooking_blog", "label": "imports_from", "kind": "association", "confidence": 0.9 },
{ "id": "a3", "fromBlockId": "scrapbook", "toBlockId": "recipe", "label": "contains", "kind": "composition", "confidence": 1.0 }
],
"constraints": [
{ "id": "copyright_respect", "label": "Copyright respect", "expression": "must not republish recipes outside the user's private collection",
"appliesTo": ["scrapbook"], "confidence": 0.85 }
],
"requirements": [
{ "id": "req_001", "tag": "REQ-001", "text": "Imports a recipe from a URL in under 5 seconds",
"satisfiedBy": ["scrapbook"], "confidence": 0.9 },
{ "id": "req_002", "tag": "REQ-002", "text": "Stores recipes in the user's private collection only",
"satisfiedBy": ["scrapbook"], "confidence": 1.0 }
],
"overallConfidence": 0.85,
"notes": "The Scrapbook is the SoI; home cook and cooking blog are actors; recipes are first-class blocks."
}
Notice every array is populated. No constraints in blocks. Requirements name specific block satisfiers.
Now generate
Return ONLY the JSON object for the seed you receive. No prose, no code fences. Use the four arrays — fill all of them.