clay-borg/workplans/CB-WP-0035-a-session-that-answers-nothing.md

79 lines
2.8 KiB
Markdown
Raw Normal View History

---
id: CB-WP-0035
kind: product
title: "A session that answers nothing"
status: done
---
# Purpose
```
structural tier S (one endpoint, one rejection handler; no rule, no
dependency, no artifact contract moved)
chaos d8 = 4 → no override
declared tier S
```
**Declaration 6 of chaos window 3.**
## The report, and it is one defect not two
> *"I think the server timing out is not noticed by the client. And 'Play
> Again' does not report that the session is no longer available."*
Both come from the same three characters. The script's request was
```js
fetch(...).then(...).then(...) // and no .catch
```
so when the process had exited the promise **rejected, the chain never
ran, and not even the status line moved.** A dead server and a click that
did nothing were indistinguishable — which is why `play again` read as a
dead button and the linger timeout was invisible.
**A request that cannot be answered is information**, and it was being
thrown away.
## Task: notice, and say so
```task
id: CB-WP-0035-T01
status: done
priority: high
```
**Controls:**
- **both paths** — pressing a control, and noticing without pressing
anything, which is the timeout case;
- **both pages**, because the timeout matters most *during play*;
- the heartbeat **carries the token**, or a refusal every five seconds
would seal a live session;
- mutation-proven: remove the `.catch` and the status goes empty, which is
the reported symptom exactly.
**Done 2026-08-07.** `.catch(gone)`, plus a 5-second heartbeat against a
new `/alive` — its own path so a beat costs a few bytes rather than a
whole re-render. It **does not extend the linger**: that deadline is
absolute, so a tab left open still cannot hold the process open.
**The harness had the same hole as the code.** `jsrun`'s `fetch` stub had
no `.catch`, so the branch that notices a dead server would have been
unreachable in every test — *the very defect the stub's own comment
records from CB-WP-0024*, where a then-chain that never invoked its
callbacks let "the page still looks live" survive. Teaching the stub
`__failing`, `.catch` and a recorded `setInterval` was the fix; writing
the script defensively instead would have repeated the trap.
**The first cut wired the heartbeat into the ending page only.** An unused
variable caught it — luck, not a control — so `every_page_can_tell_the_
session_has_gone` now asserts it on both.
## Not done here
- **Five seconds is a guess.** Nothing measures how long a player tolerates
a dead-looking page, and this pass did not find out.
- **A brief network stall seals the page**, the same as a dead server. For
a loopback-only server that is nearly always the truth, but it is a
false positive waiting for the first non-loopback deployment.