DUNIN7 · LOOMWORKS · RECORD
record.dunin7.com
Status Current
Path change-requests/cr-2026-210-loomworks-commit-error-truth-v0_1.md

DUNIN7-M4 — INFRASTRUCTURE CHANGE REQUEST

CR-2026-210 — The commit ceremony says what the server said: the catch block stops manufacturing a cause, both directions (v0.1)

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.


0. Settled — the Operator's rulings, 2026-08-14. Do not re-raise.

  1. Site 1 only. 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.
  2. The 401 case is IN scope, not a footnote. A failed passkey tap returns 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.
  3. FallbackAffordance is the precedent — follow it, don't invent a shape. (FallbackAffordance.tsx:60-67: extract the server's message from err.detail, render it; generic fallback only when nothing usable came back.)
  4. The rule (standing note v0.4): a failure message is a composed claim — composed against the server's stated cause, or confessing ignorance. Never specific and wrong.

1. The changes

A. The catch branches on the server's stated cause. CommitCeremony.handleCommit's catch, replacing the two-way collapse:

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.

2. Consequences named, so they are chosen

3. Tests — each observed failing first

Component tests driving mocked commitWithPasskey rejections through CommitCeremony:

  1. 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).
  2. ApiError 401, detail "commit_attestation_failed: …" → the passkey sentence renders; no redirect fired.
  3. ApiError 401, detail "step_up_required" → the passkey sentence renders.
  4. Bare ApiError 401 → redirect to sign-in (behavior preserved, now component-driven).
  5. ApiError 500 with a detail string → the server's detail renders (FallbackAffordance shape); no passkey sentence.
  6. ApiError with no usable body → honest ignorance; no passkey sentence.
  7. Plain Error (ceremony throw) → "didn't complete" fallback unchanged.

Existing CommitCeremony.test.tsx and banner tests re-run.

4. seed-mutability impact

Kind C expected: surface error-rendering only; nothing reads or writes the seed. Checked at Step 1; a null finding is an entry.

5. Build steps

| 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 |

6. Acceptance gate

  1. E0128's banner, live: the server's not_converged message, not the passkey sentence.
  2. The four 401 shapes each land in their ruled branch (tests); expired-session redirect preserved.
  3. The guard test (wrong-message-does-NOT-render) in the suite.
  4. Suite green; tsc clean; run watched; redeployed; eye-tested.
  5. seed-mutability recorded.

7. Kickoff prompt


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.