fix(host): keep the inbox warning's Realtime report to the attempt it names
Summary
This addresses Devin's three findings on #4064, which merged through the queue as the review arrived. #4064 added what Realtime's client last reported to the account inbox's 30-second warning. This keeps that report accurate and tests it the way the repository's rules ask.
-
No stale report after a seal failure (thread). What the client reported was cleared only when a new seal arrived. So if the next seal fetch failed, a warning saying "the service sealed nothing" could also carry a closed client's diagnostic. It is now cleared on a failed seal too, so the report always describes the attempt whose end
stoppednames. -
The event name is pattern-checked (thread). A diagnostic's event is now held to the same pattern as its code (
TROUBLE_WORD, lowercase words and underscores) before it reaches the host's line and Sentry. An entry whose event doesn't match is dropped. Realtime's client draws its events from a fixed catalog, so this is defence in depth, and it has no test: no entry the real client can emit fails the pattern, andtroubleOfis private. -
The refused-seal test checks what a caller sees (thread). It used to check only the fake service's call log. It now shows recovery:
- Realtime's real client throws on the first seal (not a compact JWE).
- The service then seals properly, and a stubbed socket admits every part with a result of no rows.
- The follow publishes one empty account inbox.
This also covers "an empty part counts as delivered" with the real client.
Evidence
pnpm exec vitest run src/compose-coding-agents.test.ts(packages/host): 13 passed, against 12 on main.- New test: no stale report. It fails on main's
account-inbox-follow.ts(1 failed, 12 passed): its warning carried; Realtime's client last reported connection_lostbeside "the service sealed nothing (unanswered)". - The real-client tests (admission refused, and recovery after a refused seal) both fail on the file from before #4064.
- New test: no stale report. It fails on main's
pnpm check:affected:- lint, oxlint, knip and
discarded-effectpassed. - Tests passed for host (418), panel (all three shards) and web (502).
- Desktop's
app-menu.test.tsfails only because the sandbox can't download the Electron binary. CI runs it. - Typecheck passed for host, panel, web and desktop, run one package at a time after the concurrent run was killed for memory.
- lint, oxlint, knip and
Invariants
-
./scripts/check.shgreen. (CI) - JSON Schema goldens unchanged (
LUKE_UPDATE_FIXTURESnot run, or diff explained line by line). (CI) - Every behavior change has, in this PR, the test that fails without it; no change-detector test added. The event-name pattern is the exception: no entry the real client can emit fails it, so there is no test (see Summary). (manual; root
AGENTS.md"Testing") - Gateway envelope goldens unchanged. (CI)
- No new
astype assertion outside the allowlisted files. (CI via anti-slop) - No
Effect.runPromise/runSync/runForkoutside the runtime edges and the shimstools/anti-slop/effect-edges.jsonrecords. (CI viaanti-slop/no-run-promise-outside-edges) - No new
setTimeout/setInterval/new Promise/AbortController/fs.watchoutside the filestools/anti-slop/effect-edges.jsonrecords. (CI viaanti-slop/no-raw-async-primitives) - No Effect, Stream, or Layer written as a statement and dropped; a description a caller walks past runs nothing. (CI via
pnpm discarded-effect) - Test count equals the pre-PR count or the delta is listed: +1 in
packages/host/src/compose-coding-agents.test.ts(12 โ 13). - Each hand-rolled file the PR replaces is deleted, or the part that remains is a permanent adaptor named in root
AGENTS.md's "The permanent adaptors" with the reason it stays. - Every
CLAUDE.md/AGENTS.mdsentence naming a changed part is edited; root pair stays byte-identical. (CI for pair existence; manual for wording) -
PRIVACY.mdunchanged, or the change is a product decision called out in the PR body. - Package
package.jsondeps match what the sources import by bare specifier;effectviacatalog:. (CI) - Barrel/door rule: no Node-reaching Effect module (
@effect/platform-node,effect/sql) behind a barrel the renderer or a web function opens. (CI: existing renderernode:grep plus a new grep for those specifiers in renderer bundles)
Notes
- Neon Realtime on production is still down (see #4064). Nothing here changes that.
Made with Luke