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

DUNIN7-M4 — INFRASTRUCTURE CHANGE REQUEST

CR-2026-211 — "Couldn't reach the Companion" becomes true always: the converse catches keep the sentence's true branches and lose its false ones (v0.1)

Version. 0.1 · Date. 2026-08-14 · Author. Claude Code (drafting, from [B-96] + its scoping note) / Marvin Percival (approving). Target. /Users/dunin7/loomworks (surface only — the scoping established the branch is status-class; the converse residue carries no stable codes and needs none; no server prerequisite). Baseline. Surface 00ed2f2. CC confirms at Step 0. Companion to. [B-96] (build list v0.75, corrected entry), scoping-notes/loomworks-b96-converse-failure-vocabulary-scoping-note-v0_1, standing note v0.4 third revision. Ordering. RANKS ABOVE CR-2026-210 per the Operator; 210 holds until this ships. Charter. Autonomous regime. Status. DRAFT — awaiting the gate. Do not execute.


0. Settled — the Operator's rulings. Do not re-raise.

  1. Two sites, both wrapping /operator/converse: useConversation.ts:448 and ChatView.tsx:535. Nothing else — dev-auth is nothing; the second-tier cause-discarding sites are not this CR.
  2. One shared helper is permitted and is NOT the fenced mechanism — ruled in the scoping: one route's vocabulary, two callers, three branches, returns a string. The line it must not cross: a code→message mapping table or a route parameter. If a third route ever wants it, that is a new ruling, not an import.
  3. FallbackAffordance is the precedent for extracting a usable server detail where one exists.
  4. The corrected failure vocabulary (scoping §1) is the ground truth: five shapes; the false members are the 500-bug and the 422-surface-bug; no_credential is NOT a member (the route composes a 200 around it).

1. The changes

A. The shared helper. converseFailureMessage(err: unknown): string (home: src/lib/api/converse.ts, next to the call both sites wrap):

B. The two catch sites call it. Each site's non-401 arm becomes setError(converseFailureMessage(err)) / setLocalSendError(...) respectively. The 401 arms are untouched (their silent-return/redirect behavior is not this CR — and not CR-210's §B either; converse 401s are session-expiry, correctly redirected).

C. Nothing else. No engine change; no change to the armored 200-composed server paths; no shared component; no registry; [B-95]/CR-2026-210 untouched behind this per the ranking.

2. Consequences named, so they are chosen

3. Tests — each observed failing first

Helper unit tests (all three branches) plus per-site component/hook tests:

  1. ApiError 500 body {"detail": "Internal Server Error"} through each site → the generic renders; the unreachability sentence does NOT render (the guard, both sites).
  2. ApiError 422 with a field-error array → the generic renders; no unreachability sentence.
  3. Thrown TypeError (fetch failure) → the unreachability sentence DOES render — the true branch is pinned, not just the false ones removed.
  4. ApiError 502 → the unreachability sentence renders (the engine-down case).
  5. ApiError 500 with a real string detail (a future engine error) → the detail renders (FallbackAffordance shape).
  6. 401 arms unchanged (existing behavior re-asserted).

Existing converse/ChatView suites 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 the two catch arms + the rewrite's engine-down status (probe the proxy with the engine stopped in dev — observed, not assumed); tests written, observed failing. | Auto | | 2 | The helper + the two call sites. | Auto | | 3 | vitest; tsc. | Auto | | 4 | Eye-test: with the dev engine STOPPED, send a turn → the unreachability sentence (true branch, observed live); engine up, force a 500 (dev-only route or mocked) if practical — else the 500/422 branches ride on the tests. | Auto | | A | Checkpoint. | Checkpoint | | 5 | Push; watch the run; redeploy; post-deploy smoke on the conversation surface. | Auto |

6. Acceptance gate

  1. The unreachability sentence renders ONLY when nothing came back or the proxy answered for a down engine (tests 3–4; eye-test observed).
  2. Reached-server failures render honest ignorance or the server's own usable detail — never unreachability (tests 1–2, 5 — the guard, both sites).
  3. The helper stays inside the ruled line (no code table, no route parameter — reviewed at checkpoint).
  4. Suite green; tsc clean; run watched; redeployed.
  5. seed-mutability recorded.

7. Kickoff prompt


Execute CR-2026-211 v0.1 at change-requests/cr-2026-211-loomworks-
converse-error-truth-v0_1.md in loomworks-record. Surface repo only
(/Users/dunin7/loomworks). Confirm baseline first.

§0: two sites, one route, one string-returning helper — the line is
a code table or a route parameter. Status-class branching; the
corrected vocabulary (500/422 are the false members, no_credential
is not one) is ground truth. Pin the TRUE branch too: thrown fetch
must still say "couldn't reach."

DUNIN7 — Done In Seven LLC — Miami, Florida CR-2026-211 — the converse catches tell the truth — v0.1 — 2026-08-14 The sentence keeps its true branches and loses its false ones.