4b8e3f04eeb0f0638bd1bce96cd76ee53cb4b7f0
7 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
| 4b8e3f04ee |
MVP M7: Socrates can propose model changes — review impact + Accept/Reject
The dock now has a "propose" button that asks Socrates to suggest one
high-value structural change. He returns a structured payload (reasoning
+ ops + impactSummary) which renders as a ProposalCard inline in the
thread. Accept routes the ops through the same useApply() pipeline as
user-originated edits (optimistic local + persisted POST + server
reconciliation).
apps/web/lib/llm/prompts/socrates/propose.md
- Promoted verbatim from phase-0/src/prompts/.
apps/web/lib/llm/proposeChange.ts
- Server-side proposeChange() — calls the LLM with character + propose
prompts + trimmed model + active issues. JSON Schema uses oneOf per
op kind (Phase 0 lesson: small models need this to produce real op
shapes instead of cramming everything into the kind name).
- normalizeOps() converts the LLM-emitted op shapes into canonical
ModelOp[] (fills in property ids/multiplicity defaults, expands
satisfiedBy[] into RequirementRelation[], splits PropertyType union
per kind).
apps/web/lib/sysml/impact.ts
- Pure pre-apply impact analysis. Runs the ops through the same
applyOps() reducer locally, then diffs:
- structural: added / removed / changed elements (per kind)
- validation: issues created vs resolved (by canonical issue key)
- dep-graph blast radius (closure of touched element ids on the
post-apply graph)
Headline stats summarized as deltas (+1 block, −1 assoc, etc.) for
the proposal card.
apps/web/app/api/projects/[projectId]/socrates/propose
- POST: returns { reasoning, ops, impactSummary, meta }. Pure read of
the model — does not apply anything; client must POST /apply with the
same ops to commit.
apps/web/components/socrates/ProposalCard.tsx
- In-dock card: PROPOSAL tag + delta stats / reasoning / collapsible
ops list / Impact section (added/removed/changed) / Validation diff
(resolves ✓ / creates ⚠) / Accept + Reject. Disabled when impact
analysis flagged the apply as illegal.
apps/web/components/socrates/SocratesDock.tsx
- New "propose" button between the input field and send. Renders
ProposalCard for assistant turns of role "proposal". Accept calls
useApply(); on success the card collapses to a "✓ Applied" system
bubble. On apply failure the dock shows the structured error
messages.
- New "system" bubble role for apply confirmations + dismissals.
apps/web/components/diagram-canvas/DiagramCanvas.tsx
- Bug fix: new blocks/constraints arriving via the model→RF sync (e.g.
from accepted proposals) are now positioned to the right of the
existing layout instead of stacking at (0, 0) offscreen.
apps/web/lib/sync/ModelStore.tsx
- Bug fix / observability: background-POST failures and version
mismatches now log to the console with structured context instead of
being silently swallowed. The server's authoritative state still
replaces the optimistic local state on response, but the user can now
see why their accept appeared to do nothing (typically: page tab
was at version N but server had advanced to N+1).
|
|||
| 78faca9968 |
MVP M6: live Socrates dock — LLM gateway + persisted thread + reply loop
The dock now talks to a real LLM. On first load it asks Socrates for an
opening turn that's grounded in the project's actual SysML model + active
validation issues. User replies and option-picks send turns through the
same channel. Thread + every message persist in SQLite so refresh keeps
the conversation.
apps/web/lib/llm
- gateway.ts: LLMGateway interface (chat + ChatOptions). Two adapters:
- lmstudio: OpenAI SDK against LMSTUDIO_BASE_URL (default
http://localhost:1234/v1)
- anthropic: @anthropic-ai/sdk against claude-sonnet-4-6 (set
ANTHROPIC_API_KEY when LLM_PROVIDER=anthropic)
Provider chosen via LLM_PROVIDER env (default: lmstudio).
- chatJSON(): JSON-mode helper with parse-error repair-retry — the same
defensive pattern proven against gemma-4-e4b in Phase 0.
- prompts.ts: server-only loader that caches .md prompts.
- prompts/socrates/character.md + review.md: ported verbatim from
phase-0/src/prompts/ (Phase 0 corpus validated these 10/10).
- socrates.ts: sendUserTurn() — builds the system prompt (character +
review + project context with trimmed model + active issues), runs
chatJSON against the gateway, persists user + assistant turns,
returns the structured turn. SocratesTurn schema is { text, options? }
with up to 3 numbered options matching the prototype.
apps/web/prisma
- SocratesThread + SocratesMessage tables. Auto-create one open thread
per project on first load.
apps/web/app/api/projects/[projectId]/socrates
- GET: returns active thread + parsed messages.
- POST: body { text }. Empty text triggers an opening turn. Persists user
+ assistant turns, returns assistant turn + provider metadata.
apps/web/components/socrates/SocratesDock.tsx
- Replaces the static thread prop with a projectId. Loads from API on
mount, auto-triggers an opening turn if the thread is empty, sends
user replies via POST. Numbered options click-to-pick or 1–3 keyboard
shortcut (skipped when focus is in an input). Status line shows the
active provider + model. Optimistic-local: user message appears
instantly, "thinking…" placeholder shows while the LLM works, errors
surface inline.
apps/web/.env.example + .env.local
- LLM_PROVIDER, LMSTUDIO_BASE_URL/MODEL/API_KEY, ANTHROPIC_API_KEY/MODEL.
- .env.local committed only with the local default (no real secrets);
user supplies their own per-machine.
What's not yet here (next iterations):
- Streaming responses (currently waits for full response, ~5-15s)
- Multi-thread switcher (one auto-thread per project)
- Socrates-proposes-ops flow (M7)
|
|||
| 5d4236a980 |
MVP M5.9: SQLite persistence via Prisma + optimistic-local sync
State now survives a page refresh. Every applied op writes a fresh model
snapshot + a changelog row inside one transaction; the editor route
server-component loads the latest snapshot and seeds it as the initial
ModelStore state. Optimistic-local apply on the client + background POST
to /api/projects/[id]/apply gives the UI an instant feel without giving
up the server-as-truth contract.
apps/web/prisma
- schema.prisma: Project / ModelSnapshot / ChangelogEntry. SQLite for dev
(file:./dev.db). The same schema swaps to Postgres by changing the
provider line + DATABASE_URL.
apps/web/lib/db
- client.ts: PrismaClient singleton with hot-reload guard.
- repo.ts: loadProject() auto-seeds Aristotle from fromFixture();
applyOpsToProject() runs the pure applyOps() server-side inside a
$transaction, writes the snapshot + changelog atomically, returns
{ applied, model, version, idMapping, errors }. Optimistic-concurrency
via expectedVersion → returns the server model on mismatch so the
client can resync without losing its tab.
apps/web/app/api/projects/[projectId]
- route.ts (GET): returns latest { model, version }
- apply/route.ts (POST): body is { ops, expectedVersion?, reason? }
apps/web/lib/sync/ModelStore.tsx
- Now takes initialModel + initialVersion + projectId. apply() updates
local state immediately, then POSTs in the background. On response the
authoritative server model + version replace the optimistic state
(handles tempId resolution from the server). Network errors keep the
optimistic state; the next successful apply reconciles.
apps/web/app/editor/[projectId]/page.tsx
- Server component now: awaits loadProject(projectId), passes initialModel
+ initialVersion + projectId to EditorShell. EditorShell falls back to
the fixture path when those props are absent (legacy callers / tests).
package.json
- pnpm.onlyBuiltDependencies allowlists prisma + @prisma/client + @prisma/engines
- db:push / db:generate / db:reset scripts
.gitignore
- apps/web/prisma/dev.db + dev.db-journal excluded.
Pinned to prisma@6 (prisma@7 dropped url from schema in favor of the
new adapter pattern; not worth the churn for MVP).
|
|||
| 384cbb4ae9 |
MVP M5 (in-memory): bidirectional sync via ModelOp + applyOps + ModelStore
Both canvases now share a canonical SysMLModel through React context.
Renames in the diagram inspector ripple to every chip in the narrative
referencing the same refId, and double-clicking a chip emits an update op
that re-renders the diagram block. Validation re-runs on every successful
apply. State is in-memory; M5.9 adds Postgres persistence.
apps/web/lib/sync (new)
- ops.ts: ModelOp alphabet (18 op kinds across block / property /
association / constraint / requirement / relation, add/update/remove for
each). tempId() helper + per-kind constructors.
- applyOps.ts: pure (model, ops) → { model, idMapping, errors, applied }
reducer. Atomic per batch. Cascades on remove-block (drops incident
associations + constraint applies-to + requirement satisfiers). tempId
resolution rewrites to canonical ids on duplicate-id collision.
- ModelStore.tsx: React provider exposing { model, apply, issues,
issuesByElement }. Validation memoized on every model change.
Editor refactor
- EditorShell wraps in ModelStoreProvider. Initial model derived from
fixture (+ optional ?break= corruptions). Validation now lives in the
store, not duplicated here.
- LeftRail consumes useModel(): Model section lists real blocks +
constraints (sorted by kind), Requirements section lists real
requirements with traced/untraced status from r.relations.
Diagram refactor (the tricky piece)
- React Flow now owns ephemeral state via useNodesState / useEdgesState.
Positions, drag-in-progress, selection are all RF-internal.
- Model → RF: a useEffect runs on model change, applies targeted setNodes
updates only for elements whose semantic data (label, kind, properties)
changed. Object identity preserved for unchanged nodes — fixes the
"re-render storm on drag" + RF measurement-cache loss.
- RF → Model: onNodesChange / onEdgesChange / onConnect / onDrop emit
ops via useApply(). Constraint-applies edges decompose into
updateConstraint ops. deleteKeyCode={[Backspace, Delete]}.
- onNodesChange now handles type:'remove' too (was missing — that's why
selecting a block + Del removed only the edges, leaving the block).
Chip refactor
- ChipView resolves displayed label from useModel() via refId lookup
(block / requirement / association / property). Double-click chip →
inline rename input → emit update-{block,requirement,association} op.
All other chips with the same refId update on the next render.
- Slash-menu inserted chips have refId=null and skip the rename
affordance (until M6 wires real model element resolution).
Removed obsolete components/diagram-canvas/fixtureToFlow.ts; replaced
with modelToFlow.ts. Bumped CSS for the chip-rename input.
|
|||
| 2052a280b1 |
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). |
|||
| a0566ce64c |
MVP M1–M3: visual port, TipTap text editor, React Flow diagram
apps/web — Next.js 16 + TypeScript + React 19 (no Tailwind) M1 Visual port - Manuscript theme + base styles ported verbatim from design-source - 100dvh layout with internal scroll regions (rail / narrative / dock thread) - TopBar, LeftRail (collapsible sections + collapsed strip), CanvasHeader, StatusBar, EditorShell composing the dual-canvas - SocratesDock (subtle/default/prominent) with bubbles + numbered options - Sigil (Σ + laurel) used across editor and seed screens - SeedScreen with emerging-seed rail, mini-graph, confidence bar, thread, typing indicator, input row - Routes: / (homepage), /editor/[projectId], /seed - Aristotle fixture in lib/fixtures (ported from data.js) M2 Real text editor - TipTap (StarterKit + custom Chip atom inline node + ReactNodeViewRenderer) - Slash-menu insertion via @tiptap/suggestion with arrow / number-key shortcuts - ChipFocusContext bridges narrative ↔ diagram hover/selection across the TipTap render boundary - fixtureToDoc converts the fixture narrative → ProseMirror JSON M3 Real diagram - React Flow (@xyflow/react) custom node (block / actor / constraint / system) matching prototype's softened look - Custom edge with bezier path + label renderer (association / composition / constraint variants) - Drag-create from a left-side palette via HTML5 DnD + screenToFlowPosition - NodeInspector panel: edit kind / label / properties (or expression for constraints); delete cascades to incident edges - Bidirectional focus highlighting between chips (narrative) and blocks (diagram) - Removed redundant legend (stereotype labels on nodes serve same purpose) State for M3 lives in component memory; persistence + bidirectional sync land in M4–M5. |
|||
| f1c4566576 |
Initial commit — design docs + Phase 0 validation harness
Sets up the Socrata project repo with: docs/ — strategy and design documents - idea.md: full product vision - implementation-plan.md: Phase 0 + Phase 1 MVP plan - phase-0-validation.md: 2-week validation experiment strategy - phase-0-plan.md: concrete Phase 0 build plan - phase-0-results.md: Phase 0 gate outcome — GO for MVP - sysml-modeling.md: metamodel + SE discipline + validation rules - socrates.md: agent character, surfaces, modes, prompts, lifecycle - sync.md: bidirectional text↔diagram sync engineering - design-source/: HTML/CSS/JS handoff bundle from Claude Design phase-0/ — validated harness (CLI, no UI, no DB) - LM Studio (local OpenAI-compatible) generation + detection + judge - PlantUML rendering for SysML model visualization - 10-seed corpus (8 working + 2 holdouts) - 5 corpus runs with iteration history in reports/ - Final gate: 10/10 pass, mean 4.32/5, holdouts validated Phase 1 MVP scope and milestones documented in implementation-plan.md. |