Say which hop broke: the two fault screens v1 actually needs #59

Closed
opened 2026-07-30 16:57:06 +00:00 by aiko · 1 comment
Owner

When something breaks, say which hop broke and what it actually said. The v1 slice of #15.

#15 holds five fault screens; three of them cannot be built correctly yet, and the two that can are the two you hit while simply trying to use her. This is those two, at the fidelity v1 needs — which is legible, not complete.

In scope

6a — the socket will not open. どこにも繋がらない, primary action アドレスを直す. Fully client-side and real: the client knows its own socket state. This is what a wrong address, a go-between that is not running, or a machine that is asleep looks like. Today it presents as error.websocketNotOpen with no explanation and no action.

5a — she could not answer. 上流が応答しない, primary action 再同調. #15's reality audit already downgraded this from a live health readout to a post-failure state built from the error message the go-between sends — the client cannot know the upstream is down before a turn. That downgrade stands, and is what makes it buildable now.

Both need only the parts that carry information:

  • the station bar degraded, with a status word
  • a headline in her voice, never an error code
  • which hop broke, one line per hop
  • a folded 詳細 holding the real error text the go-between sent — inventing likely causes the client cannot substantiate is explicitly ruled out by #15's audit
  • one primary action that is not a dead end

Deliberately not in scope

The full six-part card treatment, the texture block, and the other three faults, all of which stay in #15:

  • 6b auth refused — describes typing a key. ADR-0011 removes that model; build with #26 / #40.
  • 6c no Live2D model — its action 姿を選ぶ assumes a picker that #52 establishes needs rebuilding first.
  • the reconnect toast — needs #57 to give it a gap length to name.

Acceptance

  • A go-between that is not running produces 6a with a usable action, not error.websocketNotOpen.
  • A run that fails produces 5a naming the upstream hop, with the go-between's own error text under 詳細.
  • Neither screen invents a cause it cannot substantiate.
  • Verified by stopping the go-between and by making a turn fail, not by a test that sets a flag.
  • #15 — the rest of the taxonomy, v2
  • #57 — reconnect, which 6a should not paper over
  • #41 — the wayfinder map this is scoped against
When something breaks, say which hop broke and what it actually said. The v1 slice of `#15`. `#15` holds five fault screens; three of them cannot be built correctly yet, and the two that can are the two you hit while simply trying to use her. This is those two, at the fidelity v1 needs — which is *legible*, not *complete*. ## In scope **6a — the socket will not open.** `どこにも繋がらない`, primary action `アドレスを直す`. Fully client-side and real: the client knows its own socket state. This is what a wrong address, a go-between that is not running, or a machine that is asleep looks like. Today it presents as `error.websocketNotOpen` with no explanation and no action. **5a — she could not answer.** `上流が応答しない`, primary action `再同調`. `#15`'s reality audit already downgraded this from a live health readout to a **post-failure** state built from the `error` message the go-between sends — the client cannot know the upstream is down before a turn. That downgrade stands, and is what makes it buildable now. Both need only the parts that carry information: - the station bar degraded, with a status word - a headline in her voice, never an error code - **which hop broke**, one line per hop - a folded `詳細` holding the **real error text the go-between sent** — inventing likely causes the client cannot substantiate is explicitly ruled out by `#15`'s audit - one primary action that is not a dead end ## Deliberately not in scope The full six-part card treatment, the texture block, and the other three faults, all of which stay in `#15`: - **6b auth refused** — describes typing a key. ADR-0011 removes that model; build with `#26` / `#40`. - **6c no Live2D model** — its action `姿を選ぶ` assumes a picker that `#52` establishes needs rebuilding first. - **the reconnect toast** — needs `#57` to give it a gap length to name. ## Acceptance - A go-between that is not running produces 6a with a usable action, not `error.websocketNotOpen`. - A run that fails produces 5a naming the upstream hop, with the go-between's own error text under `詳細`. - Neither screen invents a cause it cannot substantiate. - Verified by **stopping the go-between and by making a turn fail**, not by a test that sets a flag. ## Related - `#15` — the rest of the taxonomy, `v2` - `#57` — reconnect, which 6a should not paper over - `#41` — the wayfinder map this is scoped against
Author
Owner

Done in 3f9fd61 (implementation 376bd0c), merged to main.

What was built

6a — the socket will not open. どこにも繋がらない, from this client's own socket phase and nothing else. The chain says ✕ 中継局 socket 開けない / — 上流 HERMES 確認できない — hop two is unverifiable rather than blamed, because with our own socket shut it is. 詳細 carries the address we are dialling, which is the fact アドレスを直す acts on. The key opens 調整 on , where the address row actually is (settings-sheet.tsx:483 Station) — it opened 電波 until I pressed it in a running client and landed on the tuning tab with no field.

5a — she could not answer. 上流が応答しない, from the pushed upstream-status and from a turn that did not come back. 詳細 quotes the go-between verbatim or says 中継局からの説明はない. No likely-cause list, per this issue's constraint and #15's audit; and nothing anywhere reads the error text to decide anything — ADR-0011's rule applied to the go-between's prose as well as hers (fault-wired.test.ts asserts it).

Both stand down for 調整 and for an outstanding 承認, so neither primary key buries its own destination.

Acceptance

  • A go-between that is not running produces 6a with a usable action, not error.websocketNotOpen
  • A run that fails produces 5a naming the upstream hop, with the go-between's own error text under 詳細
  • Neither screen invents a cause it cannot substantiate
  • Verified by stopping the go-between and by making a turn fail

What "verified" means here

Run against a real client, not a flag:

  • With nothing listening on 12393, 6a drew, the station bar went 圏外, and アドレスを直す opened 調整 on 局 with the address field pre-filled.
  • Against a socket pushing upstream-status {"attached": false} and answering a text-input with the real report_undelivered string, 5a drew with No adapter is attached, so the turn was not delivered. under 詳細, and 再同調 dropped, redialled and cleared it.

That second peer was a 30-line stand-in sending the two frames copied from conversation_handler.py:79 and websocket_handler.py:145. I did not run the Hermes stack.

Two defects the run found that reading would not have

Both silent, both fixed here:

  1. 6a blinked out on every reconnect attempt. two-hop calls a CONNECTING socket unknown, which is right for a cold start and for onboarding's calm 同調中 — but the backoff spends most of its time there, and an unreachable host holds CONNECTING for the whole TCP timeout. The screen appeared and vanished on a loop. faultAfter now holds it across a retry that has not landed.
  2. A refused turn recorded nothing. The go-between answers an undelivered turn in the same round trip, before the send's own setAiState reaches the message handler's closure — so 詳細 said it had been told nothing while it had just been told exactly what happened. AiStateContext now publishes aiStateRef, written by the setter, for handlers that cannot wait for a render.

Not done

  • The dock's keys stay live under 6a. The screen covers the stage, not the dock, so pressing 押して送信 or 送信 underneath still raises the error.websocketNotOpen toast. The acceptance is met — that toast is no longer what you get — but it is still reachable, and the toast is deliberately kept because it is the pal's only fault signal at 0.7×.
  • A run that fails while both hops report reached draws 5a with a chain saying both hops are up. That is the honest pair (the headline is what she could not do; the hops are what is proven), but it will read oddly until something proves the upstream failed.
  • The pal's なぜ button still has no handler (pal-container.tsx passes no onWhy), so 7e's way into the full screen is unwired. Pre-existing, out of this slice.

Gate: 801 vitest across 54 files (was 770/50), 18 Rust. npx tsc --noEmit is at 584 errors, none in any file this touched.

Done in `3f9fd61` (implementation `376bd0c`), merged to `main`. ## What was built **6a — the socket will not open.** `どこにも繋がらない`, from this client's own socket phase and nothing else. The chain says `✕ 中継局 socket 開けない` / `— 上流 HERMES 確認できない` — hop two is unverifiable rather than blamed, because with our own socket shut it is. 詳細 carries the address we are dialling, which is the fact `アドレスを直す` acts on. The key opens 調整 on **局**, where the address row actually is (`settings-sheet.tsx:483` `Station`) — it opened 電波 until I pressed it in a running client and landed on the tuning tab with no field. **5a — she could not answer.** `上流が応答しない`, from the pushed `upstream-status` and from a turn that did not come back. 詳細 quotes the go-between **verbatim** or says `中継局からの説明はない`. No likely-cause list, per this issue's constraint and `#15`'s audit; and nothing anywhere reads the error text to decide anything — ADR-0011's rule applied to the go-between's prose as well as hers (`fault-wired.test.ts` asserts it). Both stand down for 調整 and for an outstanding 承認, so neither primary key buries its own destination. ## Acceptance - [x] A go-between that is not running produces 6a with a usable action, not `error.websocketNotOpen` - [x] A run that fails produces 5a naming the upstream hop, with the go-between's own error text under 詳細 - [x] Neither screen invents a cause it cannot substantiate - [x] Verified by stopping the go-between and by making a turn fail ## What "verified" means here Run against a real client, not a flag: - With nothing listening on 12393, 6a drew, the station bar went `圏外`, and `アドレスを直す` opened 調整 on 局 with the address field pre-filled. - Against a socket pushing `upstream-status {"attached": false}` and answering a `text-input` with the real `report_undelivered` string, 5a drew with **`No adapter is attached, so the turn was not delivered.`** under 詳細, and `再同調` dropped, redialled and cleared it. That second peer was a 30-line stand-in sending the two frames copied from `conversation_handler.py:79` and `websocket_handler.py:145`. I did not run the Hermes stack. ## Two defects the run found that reading would not have Both silent, both fixed here: 1. **6a blinked out on every reconnect attempt.** `two-hop` calls a CONNECTING socket unknown, which is right for a cold start and for onboarding's calm 同調中 — but the backoff spends most of its time there, and an unreachable host holds CONNECTING for the whole TCP timeout. The screen appeared and vanished on a loop. `faultAfter` now holds it across a retry that has not landed. 2. **A refused turn recorded nothing.** The go-between answers an undelivered turn in the *same round trip*, before the send's own `setAiState` reaches the message handler's closure — so 詳細 said it had been told nothing while it had just been told exactly what happened. `AiStateContext` now publishes `aiStateRef`, written by the setter, for handlers that cannot wait for a render. ## Not done - **The dock's keys stay live under 6a.** The screen covers the stage, not the dock, so pressing 押して送信 or 送信 underneath still raises the `error.websocketNotOpen` toast. The acceptance is met — that toast is no longer *what you get* — but it is still reachable, and the toast is deliberately kept because it is the pal's only fault signal at 0.7×. - **A run that fails while both hops report reached** draws 5a with a chain saying both hops are up. That is the honest pair (the headline is what she could not do; the hops are what is proven), but it will read oddly until something proves the upstream failed. - **The pal's `なぜ` button still has no handler** (`pal-container.tsx` passes no `onWhy`), so 7e's way into the full screen is unwired. Pre-existing, out of this slice. Gate: **801 vitest across 54 files** (was 770/50), **18 Rust**. `npx tsc --noEmit` is at 584 errors, none in any file this touched.
aiko closed this issue 2026-07-30 19:53:57 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
aiko/denpa#59
No description provided.