Commit Graph

2 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).
2026-04-30 00:27:29 +02:00
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)
2026-04-29 07:50:34 +02:00