Version. 0.1 · Date. 2026-08-14 · Author. Claude Code (drafting, from [B-95] + the B-95 scoping note) / Marvin Percival (approving).
Target. /Users/dunin7/loomworks (surface only — the scoping established no server prerequisite: 49 stable domain codes exist; not_converged is one; the commit path's 401s carry stable detail prefixes). Baseline. Surface 00ed2f2. CC confirms at Step 0.
Companion to. [B-95] (build list v0.73), scoping-notes/loomworks-b95-error-cause-sweep-scoping-note-v0_1, standing note v0.4 third revision, completion record CR-2026-207 v0.3.
Charter. Autonomous regime. Status. DRAFT — awaiting the gate. Do not execute.
CommitCeremony.tsx is this CR's whole scope. [B-96] (the couldn't-reach sites, ranked above this item) is its own item; dev-auth's site is nothing. The fence holds: no general error-rendering mechanism, however tempting four sites make it look.401 commit_attestation_failed; api() redirects all 401s to /signin; the ceremony renders nothing on 401 — so the one case where "try your passkey again" is TRUE is the one case that never shows it, and the Operator is bounced to sign-in mid-ceremony. The same defect from the other side. The CR distinguishes a failed tap from an expired session, or it fixes half of it.FallbackAffordance.tsx:60-67: extract the server's message from err.detail, render it; generic fallback only when nothing usable came back.)
A. The catch branches on the server's stated cause. CommitCeremony.handleCommit's catch, replacing the two-way collapse:
409 with detail.error === "not_converged" → render the server's own message (the open-finding count sentence), plus the state-true pointer the banner context supports: the findings must be addressed before committing (the divergent path exists server-side; whether the banner OFFERS it is deliberately out of scope — this CR makes the message true, not the ceremony richer).401 whose detail carries commit_attestation_failed: or commit_challenge_invalid: → the passkey sentence — "Couldn't commit — try your passkey again." — which is then TRUE, rendered for the first time in its own true case. No redirect.401 step_up_required → the passkey sentence likewise (a fresh tap is exactly the remedy).401 (expired session) → today's behavior preserved: redirect to sign-in.ApiError → the FallbackAffordance extraction: render the server's detail/message when present, else honest ignorance ("Couldn't commit — something went wrong. Try again."). Never the passkey sentence.ApiError (the ceremony itself threw — cancelled/failed tap client-side) → unchanged: "The passkey step didn't complete. Tap to try again."
B. The 401s reach the component. The three calls in lib/api/commit-ceremony.ts (challenge, instantiate, instantiate-divergent) pass bypassUnauthorizedRedirect: true; the component performs the expired-session redirect itself in the bare-401 branch (behavior-identical for real session expiry — the redirect moves, it does not disappear).
C. Nothing else. No api() change (the bypass flag exists — Step 1 verifies its semantics); no new strings module beyond the added messages in the component or strings.ts per house convention; no engine change; [B-94]'s sentence splice untouched (its own item); no shared error helper — the fence.
api() — one hop later, same destination).not_converged message is engine prose rendered verbatim on the surface — accepted: it is the truthful sentence, and FallbackAffordance set the precedent. If its wording ever needs surface tailoring, that is a wording amendment, not a mechanism.
Component tests driving mocked commitWithPasskey rejections through CommitCeremony:
ApiError 409 body {error: "not_converged", message: "Seed has 1 open finding(s)…"} → the server's message renders; the passkey sentence does NOT render (the guard).ApiError 401, detail "commit_attestation_failed: …" → the passkey sentence renders; no redirect fired.ApiError 401, detail "step_up_required" → the passkey sentence renders.ApiError 401 → redirect to sign-in (behavior preserved, now component-driven).ApiError 500 with a detail string → the server's detail renders (FallbackAffordance shape); no passkey sentence.ApiError with no usable body → honest ignorance; no passkey sentence.Error (ceremony throw) → "didn't complete" fallback unchanged.
Existing CommitCeremony.test.tsx and banner tests re-run.
Kind C expected: surface error-rendering only; nothing reads or writes the seed. Checked at Step 1; a null finding is an entry.
| Step | What | Mode |
|---|---|---|
| 0 | Pre-flight — baseline, clean tree. | Auto |
| 1 | Verify bypassUnauthorizedRedirect semantics in api(); verify the three 401 detail spellings against engine code; tests written, observed failing. | Auto |
| 2 | The catch rework + the bypass flags. | Auto |
| 3 | vitest; tsc. | Auto |
| 4 | Eye-test on E0128 live: click Make it official → the not_converged message renders (the passkey sentence gone). If a failed-tap probe is practical (cancel the tap), observe branch 7; the 401 branches ride on the tests otherwise. | Auto |
| A | Checkpoint. | Checkpoint |
| 5 | Push; watch the run; redeploy; post-deploy eye-test on E0128. | Auto |
Execute CR-2026-210 v0.1 at change-requests/cr-2026-210-loomworks-
commit-error-truth-v0_1.md in loomworks-record. Surface repo only
(/Users/dunin7/loomworks). Confirm baseline first.
§0: site 1 only; the 401 case is in scope (failed tap ≠ expired
session, or it fixes half); FallbackAffordance is the shape; the
fence holds — no general mechanism. The guard test is the point:
assert the wrong message does NOT render.
DUNIN7 — Done In Seven LLC — Miami, Florida CR-2026-210 — the commit ceremony says what the server said — v0.1 — 2026-08-14 Composed against the server's stated cause, or confessing ignorance — never specific and wrong, in either direction.