room.js: codex round 3 - a stamp only a real installation earns
S3-13 is the last door onto S3-9. installedAt was recorded before
setConfiguration and a throw from it was swallowed, so the restart that
followed re-gathered on the *previous* servers — and a new fragment
then read as proof and stamped the connection onto a list it had never
taken: listAt moved, renewed went true, and nothing was shown. The
stamp moves after the call returns now, and a refused configuration
restarts nothing, proves nothing and stamps nothing. It asks for a
fresh generation instead, whose rebuilt connection takes ice.servers at
construction and keeps §4.6's counters, and the run ends stuck.
S3-14: icestuck asserted a lifetime counter, which cannot say how many
restarts a verdict followed — a later run raises it while the first
verdict is still on the screen. renewals[peer].lastRun carries the
run's own budget and how it ended, captured with the verdict itself;
the scenario asserts that run spent three and ended stuck, and keeps
the cumulative count as a second, weaker line.
markRenewalStuck is gone: every verdict goes through one function that
records what the run spent and then says it on the tile, so the two
cannot drift.
69 node tests, each new one seen failing first. make browser twice: 26
of 27 with icestuck lost at assertPair to the known ICE stall — one
side still `gathering`, having produced no candidate — then 27/27 in
418.69s, carrying the new assertion's own evidence:
{"attempts":3,"verdict":"stuck"}.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CzvnuKhLzudvq7jZ7SvVt2
4 files changed,
+121
−16
docs/superpowers/reviews/2026-09-10-slice-3-record.md+16 −0internal/meet/static/room.js+45 −10test/browser/call.mjs+9 −5test/browser/negotiation.test.mjs+51 −1
diff --git a/docs/superpowers/reviews/2026-09-10-slice-3-record.md b/docs/superpowers/reviews/2026-09-10-slice-3-record.md| index d8efde7..ab66fb5 100644 |
| --- a/docs/superpowers/reviews/2026-09-10-slice-3-record.md |
| +++ b/docs/superpowers/reviews/2026-09-10-slice-3-record.md |
| @@ -330,3 +330,19 @@ within milliseconds of the first verdict, which made `icestuck`'s |
| again says nothing a gate can wait on. |
| Commit: `d62e93b..15b2f38`. |
| + |
| +## Codex round 3 |
| + |
| +Read at `9bb0994`. S3-8, S3-10, S3-11 and S3-12 closed; S3-9 half-closed |
| +through one remaining door, S3-13. One P3 on a test claim. One commit. |
| + |
| +| Finding | What it was | Disposition | Test | |
| +| --- | --- | --- | --- | |
| +| S3-13 (P2) | The last door onto S3-9: `installedAt` was recorded before `setConfiguration`, and a throw from it was swallowed. The restart that followed re-gathered on the *previous* servers, and a new fragment then read as proof and stamped the connection onto a list it had never taken — `listAt` moved, `renewed` went true, no trouble was shown | Fixed. The stamp moves only after `setConfiguration` returns. A refused configuration restarts nothing, proves nothing and stamps nothing: it asks for a fresh generation, whose rebuilt connection takes `ice.servers` at construction and keeps §4.6's counters, and the run ends `stuck` | `a configuration the browser refuses is not a credential the connection has` | |
| +| S3-14 (P3) | `icestuck` asserted `attempts >= 3` against a lifetime counter, which cannot say how many restarts *this* verdict followed — a later run raises it while the first verdict is still on the screen | Fixed. `renewals[peer].lastRun = {attempts, verdict}` is captured with the verdict itself; the scenario asserts that run spent three and ended `stuck`, and keeps the cumulative count as a second, weaker line | `a run records the budget it spent, alongside the lifetime count`, `a proven run says so, and says what it cost`, and `icestuck` | |
| + |
| +`markRenewalStuck` is gone: every verdict now goes through one function |
| +that records what the run spent and then says it on the tile, so the |
| +two cannot drift. |
| + |
| +Commit: `9bb0994..2d0a1eb`. |
diff --git a/internal/meet/static/room.js b/internal/meet/static/room.js| index 02633a6..966d3de 100644 |
| --- a/internal/meet/static/room.js |
| +++ b/internal/meet/static/room.js |
| @@ -1644,10 +1644,6 @@ |
| markTrouble(id, 'expired', expiring); |
| } |
| - function markRenewalStuck(id, attempted) { |
| - markTrouble(id, 'stuck', attempted); |
| - } |
| - |
| // markTrouble is where a verdict becomes a sentence, and `upTo` is |
| // the list the verdict is about: the credential that expired, or the |
| // one a restart could not install. A connection proven onto something |
| @@ -1670,7 +1666,13 @@ |
| function renewalRecord(id) { |
| let record = renewals.get(id); |
| if (!record) { |
| - record = { attempts: 0, renewed: false, ufrag: '', at: 0, running: false, trouble: '' }; |
| + record = { |
| + attempts: 0, renewed: false, ufrag: '', at: 0, running: false, trouble: '', |
| + // The last run's own budget and how it ended, for a reader who |
| + // needs to know a verdict followed three restarts and not one — |
| + // which `attempts`, counting a whole life, cannot answer. |
| + lastRun: null, |
| + }; |
| renewals.set(id, record); |
| } |
| return record; |
| @@ -1702,6 +1704,12 @@ |
| // verdict at the bottom: it is the list the restarts failed on, and |
| // not necessarily the newest one the call holds. |
| let lastInstalled = ice.fetchedAt; |
| + // What *this* run spends, as against `record.attempts`, which is |
| + // the connection's whole life. A verdict has to be readable |
| + // alongside the budget it followed, and a lifetime counter cannot |
| + // say that: a later run raises it while the first verdict is still |
| + // on the screen (Codex S3-14). |
| + let spent = 0; |
| try { |
| for (let attempt = 1; attempt <= RENEW_ATTEMPTS; attempt++) { |
| if (state.stopped || pcs.get(id) !== entry) return false; |
| @@ -1712,6 +1720,7 @@ |
| // follow-up began — which is a number that says nothing and |
| // that a gate cannot wait on. |
| record.attempts += 1; |
| + spent += 1; |
| // The generation this connection is on now, and the count of |
| // exchanges it has completed: the two things the proof is |
| // measured against. |
| @@ -1726,14 +1735,27 @@ |
| // schedule (Codex S3-9). |
| const installed = ice.servers; |
| const installedAt = ice.fetchedAt; |
| - lastInstalled = installedAt; |
| try { |
| entry.pc.setConfiguration({ iceServers: installed }); |
| entry.floor = !installed.length; |
| } catch { |
| - // An older browser without setConfiguration: the restart still |
| - // re-gathers, on the servers this connection was built with. |
| + // The connection is still on the list it had, and a restart |
| + // from here would re-gather on *those* servers — and could |
| + // prove, on a new fragment, and be stamped onto a list it |
| + // never took (Codex S3-13). Nothing is stamped, nothing is |
| + // restarted, and a fresh generation is asked for instead: a |
| + // rebuilt connection takes `ice.servers` at construction, |
| + // which is the only way left to put these servers under this |
| + // pair, and it keeps §4.6's counters. |
| + requestReset(id); |
| + await sleep(RENEW_WINDOW); |
| + if (state.stopped || pcs.get(id) !== entry) return false; |
| + verdict(id, record, spent, 'stuck', lastInstalled); |
| + return false; |
| } |
| + // Installed, and only now is this the list the run is working |
| + // on. The stamp moves after the call returns, never before it. |
| + lastInstalled = installedAt; |
| try { |
| entry.pc.restartIce(); |
| } catch { |
| @@ -1748,7 +1770,7 @@ |
| requestReset(id); |
| await sleep(RENEW_WINDOW); |
| if (state.stopped || pcs.get(id) !== entry) return false; |
| - markRenewalStuck(id, installedAt); |
| + verdict(id, record, spent, 'stuck', installedAt); |
| return false; |
| } |
| const proven = await proveRenewed(id, entry, oldUfrag, from, Date.now() + RENEW_WINDOW); |
| @@ -1759,11 +1781,12 @@ |
| record.at = Date.now(); |
| entry.listAt = installedAt; |
| record.trouble = ''; |
| + record.lastRun = { attempts: spent, verdict: 'renewed' }; |
| renderTiles(); |
| return true; |
| } |
| } |
| - markRenewalStuck(id, lastInstalled); |
| + verdict(id, record, spent, 'stuck', lastInstalled); |
| return false; |
| } finally { |
| record.running = false; |
| @@ -1775,6 +1798,15 @@ |
| } |
| } |
| + // verdict records how a run ended and what it spent getting there, |
| + // and then says it on the tile. `spent` is this run's own budget; |
| + // `record.attempts` keeps counting across runs for whoever wants the |
| + // whole story. |
| + function verdict(id, record, spent, kind, upTo) { |
| + record.lastRun = { attempts: spent, verdict: kind }; |
| + markTrouble(id, kind, upTo); |
| + } |
| + |
| // followUp is that debt, paid once the attempt that incurred it has |
| // let go of the record. `attempted` is the newest list that run had |
| // to work with: only a list newer than *that* is a debt, which is |
| @@ -2858,6 +2890,9 @@ |
| // a credential that was perfectly good. |
| expired: r.trouble === 'expired', |
| trouble: r.trouble || '', |
| + // What the run that reached that verdict spent getting there, |
| + // which `attempts` — a whole life's worth — cannot say. |
| + lastRun: r.lastRun, |
| }; |
| } |
| return out; |
diff --git a/test/browser/call.mjs b/test/browser/call.mjs| index 7ec2faa..d5c1558 100644 |
| --- a/test/browser/call.mjs |
| +++ b/test/browser/call.mjs |
| @@ -1193,12 +1193,16 @@ const run = { |
| check(verdict.expired === false, |
| 'and it is not marked expired: a replacement credential was in hand throughout (' + |
| JSON.stringify(verdict) + ')'); |
| - // Three or more: a connection that cannot renew keeps being |
| - // handed the next list as it arrives, and every restart it |
| - // spends counts. What matters is that the first verdict came |
| - // after a whole budget, not after one attempt. |
| + // The run that reached this verdict, not the connection's whole |
| + // life: `attempts` keeps counting as later runs take later lists, |
| + // so it cannot say how many restarts *this* verdict followed |
| + // (Codex S3-14). `lastRun` is captured with the verdict itself. |
| + check(verdict.lastRun && verdict.lastRun.attempts === 3 && |
| + verdict.lastRun.verdict === 'stuck', |
| + 'the run that gave up spent the whole three-restart budget (' + |
| + JSON.stringify(verdict.lastRun) + ')'); |
| check(verdict.attempts >= 3, |
| - 'it spent the whole three-restart budget first (' + verdict.attempts + ')'); |
| + 'and the lifetime count is at least that (' + verdict.attempts + ')'); |
| check(verdict.renewed === false, 'and nothing was proven (' + JSON.stringify(verdict) + ')'); |
| const notes = await tileNotes(pageA); |
| check(notes.includes(ICE_STUCK) && !notes.includes(ICE_EXPIRED), |
diff --git a/test/browser/negotiation.test.mjs b/test/browser/negotiation.test.mjs| index e8d1698..637b67f 100644 |
| --- a/test/browser/negotiation.test.mjs |
| +++ b/test/browser/negotiation.test.mjs |
| @@ -1011,7 +1011,7 @@ const machine = new Function( |
| 'bareRestart', 'RENEW_ATTEMPTS', 'RENEW_WINDOW', 'RENEW_POLL', 'console', |
| [lift(src, 'renewOne'), lift(src, 'renewOutdated'), lift(src, 'retryRenewal'), |
| lift(src, 'followUp'), |
| - lift(src, 'renewalRecord'), lift(src, 'markTrouble'), lift(src, 'markRenewalStuck'), |
| + lift(src, 'renewalRecord'), lift(src, 'markTrouble'), lift(src, 'verdict'), |
| lift(src, 'markRelayExpired'), lift(src, 'proveRenewed'), lift(src, 'renewedUfrag'), |
| lift(src, 'selectedPairUfrag'), lift(src, 'selectedUfrag'), lift(src, 'statsList'), |
| lift(src, 'sleep')].join('\n') + |
| @@ -1241,3 +1241,53 @@ test('a proof stamps the list it installed, not one that arrived while it was pr |
| await new Promise((resolve) => setTimeout(resolve, 600)); |
| assert.equal(them.listAt, m.ice.fetchedAt, 'and it is renewed onto the newer list afterwards'); |
| }); |
| + |
| + |
| +test('a configuration the browser refuses is not a credential the connection has', async () => { |
| + // Codex S3-13. setConfiguration throws — an older browser, or one |
| + // that will not take the list mid-call — and the connection stays on |
| + // the servers it had. Restarting from there re-gathers on *those* |
| + // servers, and a new fragment would then have been read as proof and |
| + // stamped the connection onto a list it never took. Nothing is |
| + // stamped, nothing is restarted, and a rebuild is asked for instead: |
| + // a fresh connection takes the current list at construction. |
| + const them = renewable(['old0', 'old0', 'new1']); |
| + them.pc.setConfiguration = () => { throw new Error('refused'); }; |
| + const m = mesh({ them }); |
| + |
| + assert.equal(await m.renewOne('them'), false); |
| + assert.deepEqual(them.calls, [], 'a refused configuration must not be followed by a restart'); |
| + assert.equal(them.listAt, 0, 'and nothing may be stamped: this connection took no new list'); |
| + const record = m.renewals.get('them'); |
| + assert.equal(record.renewed, false); |
| + assert.equal(record.trouble, 'stuck'); |
| + assert.deepEqual(m.resets, ['them'], 'a rebuild is the only way left to give it these servers'); |
| +}); |
| + |
| +test('a run records the budget it spent, alongside the lifetime count', async () => { |
| + // Codex S3-14. `attempts` counts a whole connection's life, so a |
| + // follow-up run raises it while the first verdict is still on the |
| + // screen: it cannot say how many restarts *this* verdict followed. |
| + const them = renewable(['old0']); |
| + const m = mesh({ them }); |
| + |
| + await m.renewOne('them'); |
| + const first = m.renewals.get('them'); |
| + assert.deepEqual(first.lastRun, { attempts: 3, verdict: 'stuck' }); |
| + assert.equal(first.attempts, 3); |
| + |
| + // A second run: the lifetime count moves on, and the run's own does |
| + // not pretend to be it. |
| + await m.renewOne('them'); |
| + const second = m.renewals.get('them'); |
| + assert.deepEqual(second.lastRun, { attempts: 3, verdict: 'stuck' }, |
| + 'the run spent three, whatever the lifetime count now reads'); |
| + assert.equal(second.attempts, 6, 'and the lifetime count is still there for whoever wants it'); |
| +}); |
| + |
| +test('a proven run says so, and says what it cost', async () => { |
| + const them = renewable(['old0', 'old0', 'new1']); |
| + const m = mesh({ them }); |
| + assert.equal(await m.renewOne('them'), true); |
| + assert.deepEqual(m.renewals.get('them').lastRun, { attempts: 1, verdict: 'renewed' }); |
| +}); |