clay-borg/workplans/CB-WP-0035-a-session-that-answers-nothing.md
tegwick 31ae160043
Some checks failed
ci / check (push) Failing after 4s
specs/Ornamentation.md: what a game is besides its rules
Draws one boundary — between what the rules can see and everything else a
player experiences — and says which repository each belongs to as the
simulator grows.

"Ritual" becomes ornamentation: half the category is material rather than
ceremonial, and "decoration" is already spoken for in this repo (a control
that cannot fail). The test is not "does it have a decision in it" —
choosing who deals is a decision and is ornamental. The test is whether
the state hash moves, which reuses the instrument that already binds a
trial note to a position.

The part that earns its place in clay-borg rather than in a renderer's
stylesheet: calling something ornamentation is a CLAIM THAT IT DOES NOT
MATTER, and this project's register is a list of times that claim was
wrong — quantity in Tokens.csv, F18's four unread files, SOLVE offered
where it could not act. So a declaration carries a falsifier, and
"provisional" is a state it must say out loud.

Downstream may read, may not decide — ADR-0007 D5 restated at the
repository boundary. The port to clay-animate is deliberately NOT
designed: no consumer exists, and an interface built against an imagined
client is the same defect as a gate that cannot go red.

Five invariants, four checkable today. I3 — same seed and decisions
produce a byte-identical recording at any pace through any renderer — is
the falsifier for the whole split.

CB-WP-0036 re-declared from L to M accordingly: the animation architecture
that made it L has moved to clay-animate. No code had been written and the
tier-L review had not been run, which is the only reason it could be
re-scoped rather than unwound.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 21:16:34 +02:00

2.9 KiB

id kind title status state_hub_workstream_id
CB-WP-0035 product A session that answers nothing done c4c2fcbe-064c-4c19-b851-1d8d5940a24c

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

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

id: CB-WP-0035-T01
status: done
priority: high
state_hub_task_id: "7be2de30-1077-439a-8e70-a20031e9ba97"

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.