Finish the Add-Matter stepper: the editable Step-5 frame drives matter create
At Step 5, Atlas drafts a Matter Frame; counsel reviews and edits it, then commits. The editable substrate had been built and "verified" — but only in the dev harness. The live wizard still rendered the read-only path and created the matter from the raw Step-2 draft, so Atlas's proposal and every Step-5 edit were silently discarded. This finishes the stepper: the editable frame's "Save & Next" is the single commit, and it creates the matter from what counsel actually sees and edits.
Bug-1379 first: why the frame never even appeared
Wiring the editable frame is moot if it never settles. Step 5 hung in
the skeleton because of a wire contract mismatch, not
StrictMode (the rail was healthy). The brain
(runtime.py::_turn_event → _wrap_proposal)
puts the proposal on the wire as a nested object —
turn.proposal.{description, core_dispute, prayers, value_amount_minor}
plus the truth-boundary envelope. But the app's
proposalFromWire (src/lib/agents/atlas-rail.ts)
read them flat (turn.proposal_description). The
#85 flatten only flattened the brain's generation DTO; the
wire AtlasTurn still nests.
next_message_mdis top-level → the chat drawer rendered "the frame is ready".proposalFromWirereadundefined→proposal = null→ the card stayed inThinkingStateforever.- Broker passes frames through verbatim — the runtime's nested shape IS the wire. App-side bug.
Fix: proposalFromWire reads the nested proposal.
The rail tests were rewritten to the real nested wire shape —
they were green before only because they mirrored the app's wrong
assumption ("fixtures lie in your favor").
The gap: done at the component boundary, not the system boundary
| Surface | Before | After |
|---|---|---|
| Step 5 render | read-only (live wizard never passed editableSnapshot) | editable substrate once Atlas settles |
| Commit | chrome footer "Save & create" on raw Step-2 frame | substrate "Save & Next" on the edited frame (one button) |
| What gets created | Atlas's proposal + edits discarded | edited narrative / value / type / forum reach create |
The wiring
-
add-matter-wizard.tsx— buildseditableSnapshot(the same scope the rail uses), and passes it + matter-type / forum / label options +onCommittoStep5ReviewForm.onCommitmaps the committed editable frame to the create payload and callsonCreate. -
New
editable-frame-commit.ts—commitPayloadFromEditableFrame(edited, ctx). The edited prose / value / matter-type / forum drive create (matter-type & forum NAME resolved back to the firm-catalog id, with a wizard fallback).clientId+ parties + team +openedOn/caseRefcome from wizard state — the editable model drops entity ids (toEditableMatterFrameseedsclient.id = "client"). -
add-matter-chrome.tsx— ashowNextflag; the wizard passesshowNext={!isLast}so Step 5 shows exactly one commit control.
Edit-now, persist-later decided
prayers, proceeding / filing types, the separate
coreDispute, and party renames are editable but NOT sent
to create — WizardMatterInput has no field for them yet
(same precedent as the Team step, which already isn't persisted). A
backend follow-up.
The proof: the full chain, tested two ways
-
Integration test
(
edit-to-create-chain.test.ts) walks that whole chain and asserts the edited narrative + value land incoreDisputeText/valueAtStakeAmount— the exact cross-hop boundary #1379 hid in. Plus a unit test for the mapper. 20+ tests green. -
Live, real wizard, LIVE DB — all 5 steps, Step 5
settled into the editable frame, footer showed only "Back" (one
commit button), edited the description to a marker, "Save & Next"
→
POST /app/matters201. The server action receivedcoreDisputeText= the edited marker (not the Step-2 draft); the marker is present in the refetch (persisted), andM-0004appeared in the register.
Files
| File | Change |
|---|---|
src/lib/agents/atlas-rail.ts | Bug-1379: read the nested wire proposal |
src/components/add-matter/editable-frame-commit.ts | new — edited frame → create payload |
src/components/add-matter/add-matter-wizard.tsx | wire editableSnapshot + options + onCommit; showNext |
src/components/add-matter/add-matter-chrome.tsx | showNext flag (one commit button) |
*.test.ts (rail, mapper, chain) | nested wire fixtures + mapper + full-chain integration |
Files changed
8 file(s), +427 / -48 lines
| Path | Role | + | - |
|---|---|---|---|
| src/components/add-matter/add-matter-chrome.tsx | Component | +14 | -8 |
| src/components/add-matter/add-matter-wizard.tsx | Component | +47 | -6 |
| src/components/add-matter/edit-to-create-chain.test.ts | Component | +121 | -0 |
| src/components/add-matter/editable-frame-commit.test.ts | Component | +107 | -0 |
| src/components/add-matter/editable-frame-commit.ts | Component | +82 | -0 |
| src/lib/agents/__tests__/atlas-rail-store.test.ts | Library | +4 | -4 |
| src/lib/agents/__tests__/atlas-rail.test.ts | Library | +21 | -11 |
| src/lib/agents/atlas-rail.ts | Library | +31 | -19 |