oficina / meet Public

gate: and nothing followed it

probehang asserted that the target was dropped on time, which was true
of a client that dropped it on time and then ran a reset on a fresh
thirty seconds behind the test's back (Codex S2-17). It now asserts what
the claim actually needs: the offer was the only thing A posted, it is
still the only thing ten seconds later, and A holds that target marked
stale — waiting for a roster rather than quietly asking again.

Against the round-3 client that reads 2 POSTs and no mark at all.

Dispositions in the record (§2d), which also records why the client now
tells a gate three things about itself that no DOM shows: boot, rosters
and stale. A client that has stopped asking for something looks exactly
like one that never needed to.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CzvnuKhLzudvq7jZ7SvVt2
Paul Campbell pushed by paul@keymail.dev 2215f26c3f01143f218f0d1baac53359a805649d parent 50953f4
2 files changed, +41 −0
  • docs/superpowers/reviews/2026-09-10-slice-2-record.md +25 −0
  • test/browser/call.mjs +16 −0

CI passed — run details

diff --git a/docs/superpowers/reviews/2026-09-10-slice-2-record.md b/docs/superpowers/reviews/2026-09-10-slice-2-record.md
index 6a68232..7f604a2 100644
--- a/docs/superpowers/reviews/2026-09-10-slice-2-record.md
+++ b/docs/superpowers/reviews/2026-09-10-slice-2-record.md
@@ -253,6 +253,31 @@ should re-check that list before reusing `stale`.
---
+## 2d. Codex round 4, and what happened to it
+
+Judged at `2f7f63f`. S2-16 closed. S2-15 half-closed through one P2, and
+it is the same lesson a third time: the budget was threaded correctly
+and then leaked out of the one exit nobody was watching.
+
+| # | Finding | Disposition | Commit | Test |
+|---|---|---|---|---|
+| S2-17 (P2) | An aborted probe or a transient refusal that exhausted `waitWithin` returned from `recoverFromNotFound` without releasing the target. The queue survived, the pump's `finally` restarted it, its expired head called `unreachable`, and that opened a reset with a fresh thirty seconds — measured at 31000 for the offer and 61000 for the reset, with zero stale-target calls. Separately `resetLoop` checked the participation before every attempt but never the clock, so one POST could go out past the deadline meant to cover it | **Fixed.** Both loops round back to a single check at the top, which is now the only place a spent budget is answered, and it answers by releasing the target — queue dropped before the pump is released, connection closed, mark set, next roster settles it. `resetLoop` gained the same check. `createPC` clears the mark, because building a connection to a peer means the server has just named it. | `50953f4` | a pure node test — the recovery lifted out of the shipped source, given fifty milliseconds and a probe that never answers, must release its target once and ask for nothing — and `probehang`, which now asserts the offer was the *only* thing A posted, that it is still the only thing ten seconds later, and that A holds the target marked stale |
+
+Verified failing first: the node test with the exhaustion `return`
+restored — "the target must be released exactly once"; `probehang`
+against `room.js` at `2f7f63f` — **2** POSTs where the fixed client
+makes 1, and no stale mark at all. The second POST is the fingerprint of
+the finding: it is the reset that ran on a fresh thirty seconds while
+the scenario's earlier assertions were all passing.
+
+`state()` gained the stale set for this, which is the third thing the
+gate has needed the client to say about itself (`boot`, `rosters`,
+`stale`). Each is a fact no DOM shows and no HTTP answer reveals: a
+client that has *stopped* asking for something looks exactly like one
+that never had to.
+
+---
+
## 3. Carried items, and where they go
**Next fix wave**
diff --git a/test/browser/call.mjs b/test/browser/call.mjs
index cf70f90..8b4e9e4 100644
--- a/test/browser/call.mjs
+++ b/test/browser/call.mjs
@@ -197,6 +197,7 @@ function state(page) {
admitted: s.admitted,
terminal: s.terminal,
rosters: s.rosters,
+ stale: s.stale,
peers: s.peers,
frames: Object.fromEntries(s.frames),
signaling: Object.fromEntries(s.signaling),
@@ -1188,6 +1189,21 @@ const run = {
const took = Date.now() - firstAt;
check(took >= 25000 && took <= 35000,
'and it did so on the message’s own clock, not a fresh one (' + took + ' ms; a restarted clock lands near 50000)');
+
+ // Dropping the target on time is not the claim. The claim is that
+ // the recovery ended there — and a client that let the queue restart
+ // would look identical at this instant while a reset ran on a fresh
+ // thirty seconds behind it (Codex S2-17). So: the one message this
+ // scenario sent is the only thing A ever posted, and it is still the
+ // only thing ten seconds later.
+ const atDrop = signals;
+ check(atDrop === 1, 'the offer was the only thing A posted (' + atDrop + ')');
+ check(dropped && dropped.stale && dropped.stale.includes(pair.b.peer),
+ 'and A holds the target marked stale, waiting for a roster rather than asking again (' +
+ JSON.stringify(dropped && dropped.stale) + ')');
+ await sleep(10000);
+ check(signals === atDrop, 'nothing followed it, ten seconds on (' + atDrop + ' → ' + signals + ')');
+
const after = await state(pageA);
check(after.peer === pair.a.peer && !after.terminal,
'A is still in the call with its own peer id — ' + (await diagnose(pageA, 'member A')));