Test 1178 — Codex upgrade recovery and topology audit

Historical baseline output follows. The board #762 review follow-up at the end
records the current run; prior measurements are not claims of new execution.

Layer 1: pure recovery + existing thread lifecycle
bun test v1.3.14 (0d9b296a)

src/codex-copresence-resume-timeout.test.ts:
(pass) Codex co-presence resume timeout > --new-session suppresses the recorded co-presence thread [0.12ms]
(pass) Codex co-presence resume timeout > default grows by 120 seconds per started GiB and caps at 15 minutes [3.50ms]
(pass) Codex co-presence resume timeout > missing or ambiguous rollout keeps the 300 second base [0.41ms]
(pass) Codex co-presence resume timeout > valid environment override wins; invalid values warn and fall back [0.28ms]
(pass) Codex co-presence resume timeout > budget retains rollout bytes for timeout diagnostics even with an override [0.43ms]
(pass) Codex co-presence resume timeout > large-frame ceiling is finite, size-aware, and bridge readiness covers both attempts [0.14ms]
(pass) --new-session co-presence wiring > Windows and POSIX both suppress the recorded and pending thread [2.33ms]
(pass) --new-session co-presence wiring > both launchers give bridge and TUI the finite payload/recovery budget [2.43ms]

src/codex-copresence-rpc.test.ts:
(pass) co-presence RPC recovery deadline > Node recovery lifts the fixed WebSocket frame ceiling for Codex 0.133 [0.06ms]
(pass) co-presence RPC recovery deadline > a fresh launch is deferred to the TUI and never creates a thread over RPC [2.78ms]
(pass) co-presence RPC recovery deadline > a slow fake app-server can finish thread/resume inside the configured deadline [94.35ms]
(pass) co-presence RPC recovery deadline > the same slow server still fails closed when its deadline is too small [31.78ms]
(pass) co-presence RPC recovery deadline > metadata verification shares the recovery deadline instead of a fixed 15 second timeout [113.15ms]
(pass) co-presence RPC recovery deadline > falls back once when a real Codex shape rejects excludeTurns behind experimentalApi [1.46ms]
(pass) co-presence RPC recovery deadline > does not downgrade unrelated -32600 resume failures [0.48ms]
(pass) co-presence RPC recovery deadline > a broken large-payload connection fails immediately with its transport reason [0.66ms]

src/opencode-agent-node-pair.test.ts:
(pass) OpenCode agent-node release pairing > pins the exact versions being released together [0.11ms]
(pass) OpenCode agent-node release pairing > rejects latest 2.4.x-style help and accepts the RFC-029 capability [0.06ms]
(pass) OpenCode agent-node release pairing > codex bridge ignores stale PATH globals and resolves only the immutable pair [0.21ms]
(pass) OpenCode agent-node release pairing > codex capability is explicit and fails closed when absent [0.05ms]
(pass) OpenCode agent-node release pairing > admits only the exact preview package identity with safe file modes [5.13ms]
(pass) OpenCode agent-node release pairing > skips an exact project-local impersonator and selects the later global package [4.53ms]

src/codex-recovery-resource-gate.test.ts:
(pass) Codex legacy recovery resource gate > uses the measured 9x memory envelope with a 4 GiB floor [0.11ms]
(pass) Codex legacy recovery resource gate > the largest accepted frame still gets a finite memory reservation [0.03ms]
(pass) Codex legacy recovery resource gate > uses a distinct single recovery lane and a rollout-sized memory floor [0.56ms]
(pass) Codex legacy recovery resource gate > warns when the recovery quota is unavailable off Linux [1.90ms]
(pass) Codex legacy recovery resource gate > a live holder renews past TTL; SIGKILL permits takeover within one TTL [1211.71ms]
(pass) Codex legacy recovery resource gate > both native launchers hold the recovery lease through TUI attribution [4.66ms]

src/codex-copresence-env.test.ts:
(pass) Codex co-presence config environment > carries config values while launcher identity wins [0.47ms]
(pass) Codex co-presence config environment > renders values without placing them in tmux arguments [0.23ms]
(pass) Codex co-presence config environment > rejects keys which could become shell syntax [0.23ms]
(pass) Codex co-presence config environment > rejects PATH and loader hooks, including Windows case variants [0.23ms]
(pass) Codex co-presence config environment > Windows case variants cannot replace launcher-owned identity [0.09ms]
(pass) Codex co-presence config environment > all three private env files are removed without consulting config PATH [6.17ms]
(pass) Codex co-presence config environment > the launcher rejects reserved config.env before either platform stops the old generation [2.36ms]

src/codex-pending-thread-restart.test.ts:
(pass) #602 decidePendingThreadAtStart > no candidate → nothing to do [0.28ms]
(pass) #602 decidePendingThreadAtStart > marker on disk and bound to it but no rollout → drop the unmaterialized fresh candidate [0.20ms]
(pass) #602 decidePendingThreadAtStart > marker on disk and bound to it with a rollout → migrate the materialized candidate [0.15ms]
(pass) #602 decidePendingThreadAtStart > marker on disk but candidate bound to another marker → refuse (unchanged) [0.05ms]
(pass) #602 decidePendingThreadAtStart > marker untrustworthy (corrupt/mode/owner) → refuse, never dropped [0.05ms]
(pass) #602 decidePendingThreadAtStart > the bug: marker removed by a clean stop, thread never had a rollout → drop, start fresh [0.04ms]
(pass) #602 decidePendingThreadAtStart > marker gone but the thread HAS a rollout → refuse (never adopt an unproven binding) [0.07ms]
(pass) #602 decidePendingThreadAtStart > marker gone and the rollout search failed → refuse [0.06ms]
(pass) #602 decidePendingThreadAtStart > marker gone and the candidate is malformed → refuse [0.12ms]
(pass) #602 decidePendingThreadAtStart > rollout lookup is asked for exactly the candidate's thread id [0.10ms]
(pass) #602 findThreadRollouts > no sessions dirs at all → no rollouts (not an error) [0.72ms]
(pass) #602 findThreadRollouts > finds the exact thread under sessions/YYYY/MM/DD and archived_sessions, ignores other threads [0.98ms]
(pass) #602 findThreadRollouts > an empty rollout file still counts (the thread was persisted) [1.20ms]
(pass) #602 findThreadRollouts > an unreadable directory makes the whole lookup fail (caller refuses) [0.17ms]
(pass) #602 stopped-generation config reconciliation > Windows start -> stop -> start drops an unmaterialized pending thread before relaunch [0.21ms]
(pass) #602 stopped-generation config reconciliation > a materialized stopped-generation candidate remains fail-closed [0.07ms]
(pass) #602 wiring in anet node start > the decision runs before the previous generation is reaped and before any session starts [0.11ms]
(pass) #602 wiring in anet node start > only a MISSING marker maps to `missing`; every other refusal cause stays untrustworthy [0.06ms]
(pass) #602 wiring in anet node start > rollouts are looked up in the CODEX_HOME this launch hands to the app-server [0.04ms]
(pass) #602 wiring in anet node start > refuse still exits before any reap/start [0.07ms]
(pass) #602 wiring in anet node start > Windows uses the same pending-thread decision and persists a dropped unmaterialized candidate [0.16ms]

src/codex-copresence-thread.test.ts:
(pass) co-presence thread lifecycle > restart resumes the persisted conversation [0.08ms]
(pass) co-presence thread lifecycle > only a node with no persisted thread creates one [0.06ms]

src/codex-copresence-recovery.test.ts:
(pass) Codex co-presence recovery > resume requires exact thread identity and persisted history [0.41ms]
(pass) Codex co-presence recovery > stub resume failure is fail-closed and never calls thread/start [0.32ms]
(pass) Codex co-presence recovery > resume and verification never hydrate the full thread history [0.32ms]
(pass) Codex co-presence recovery > backup preserves config and session state but excludes credentials [4.60ms]
(pass) Codex co-presence recovery > active state writer is quiesced before the authoritative snapshot [0.36ms]
(pass) Codex co-presence recovery > recursive snapshot rejects symlinks instead of following state outside CODEX_HOME [1.58ms]
(pass) Codex co-presence recovery > a rollout larger than 2 GiB is copied and hashed without a whole-file Buffer [11491.67ms]
(pass) Codex co-presence recovery > audit exposes topology without config secrets [0.38ms]
(pass) Codex co-presence recovery > launcher is fail-closed unless the operator explicitly skips recovery backup [4.16ms]

 67 pass
 0 fail
 171 expect() calls
Ran 67 tests across 8 files. [13.08s]
bun test v1.3.14 (0d9b296a)

src/runtime/codex-app-server-client.test.ts:
(pass) CodexAppServerClient — dispatch correctness (RFC-030 §7 + bug fix) > reverse request (method + id) routes to `reverse_request`, NOT orphan_response [13.18ms]
(pass) CodexAppServerClient — dispatch correctness (RFC-030 §7 + bug fix) > reverse request also fires `reverse:<method>` targeted event [7.70ms]
(pass) CodexAppServerClient — dispatch correctness (RFC-030 §7 + bug fix) > notification (method + no id) routes to method-keyed event [7.69ms]
(pass) CodexAppServerClient — dispatch correctness (RFC-030 §7 + bug fix) > response (id + result) resolves the matching pending request [7.55ms]
(pass) CodexAppServerClient — dispatch correctness (RFC-030 §7 + bug fix) > response (id + error) rejects with codex-formatted Error [7.97ms]
(pass) CodexAppServerClient — dispatch correctness (RFC-030 §7 + bug fix) > orphan response (id present, no matching pending) fires `orphan_response` [7.33ms]
(pass) CodexAppServerClient — dispatch correctness (RFC-030 §7 + bug fix) > malformed messages fire `malformed` [7.63ms]
(pass) CodexAppServerClient — dispatch correctness (RFC-030 §7 + bug fix) > parse errors on non-JSON payload fire `parse_error` [7.24ms]
(pass) CodexAppServerClient — dispatch correctness (RFC-030 §7 + bug fix) > request timeout rejects the pending promise and cleans up the entry [42.87ms]
(pass) CodexAppServerClient — dispatch correctness (RFC-030 §7 + bug fix) > close rejects any in-flight request cleanly (no unhandled rejection) [2.66ms]
(pass) CodexAppServerClient — dispatch correctness (RFC-030 §7 + bug fix) > respondToReverseRequest emits a well-formed response envelope [11.80ms]
(pass) CodexAppServerClient — dispatch correctness (RFC-030 §7 + bug fix) > errorReverseRequest emits a JSON-RPC error envelope [8.15ms]
(pass) CodexAppServerClient — bounded large-frame transport > passes a finite maxPayload to the bridge transport [0.43ms]
(pass) CodexAppServerClient — bounded large-frame transport > a transport error rejects an in-flight request immediately [1.39ms]
(pass) CodexAppServerClient — dead shared endpoint diagnostics (#455) > wraps an empty TypeError with endpoint and remediation [0.13ms]
(pass) CodexAppServerClient — dead shared endpoint diagnostics (#455) > scrubs nested causes and bearer credentials independently of runtime shape [0.17ms]
(pass) CodexAppServerClient — dead shared endpoint diagnostics (#455) > synchronous WebSocket constructor failure uses the same safe boundary [0.48ms]
(pass) CodexAppServerClient — dead shared endpoint diagnostics (#455) > real dead loopback with query credential rejects/emits without leaking it [0.77ms]

src/runtime/codex-app-server-bridge.test.ts:
(pass) CodexAppServerBridge — bootstrap + task mapping > bootstrap sends initialize + initialized + thread/resume in order [7.37ms]
(pass) CodexAppServerBridge — bootstrap + task mapping > empty threadId → bootstrap creates a thread (thread/start) and adopts its id [5.78ms]
(pass) CodexAppServerBridge — bootstrap + task mapping > deferred co-presence binds the unique TUI-owned thread without thread/start [6.15ms]
(pass) CodexAppServerBridge — bootstrap + task mapping > deferred binding ignores non-user events and deduplicates the same TUI id [7.12ms]
(pass) CodexAppServerBridge — bootstrap + task mapping > deferred binding times out fail-closed without creating a thread [15.78ms]
(pass) CodexAppServerBridge — bootstrap + task mapping > deferred binding rejects two distinct user-owned thread identities [7.56ms]
(pass) CodexAppServerBridge — bootstrap + task mapping > deferred candidate persists before resume and restart retries only that exact id [7.37ms]
(pass) CodexAppServerBridge — bootstrap + task mapping > a successful resume does not promote a fresh candidate until its exact rollout materializes [16.69ms]
(pass) CodexAppServerBridge — bootstrap + task mapping > an acknowledged but never-materialized fresh candidate fails closed and is never thread_ready [40.65ms]
(pass) CodexAppServerBridge — bootstrap + task mapping > stale threadId with no rollout → resume fails, bootstrap falls back to thread/start [6.35ms]
(pass) CodexAppServerBridge — bootstrap + task mapping > startTaskTurn returns the server-assigned turnId and marks bridge working [4.10ms]
(pass) CodexAppServerBridge — bootstrap + task mapping > turn/completed for OUR turn fires task_reply mapped back to the task_id [13.12ms]
(pass) CodexAppServerBridge — bootstrap + task mapping > only exact owned-turn item events emit task_activity [14.44ms]
(pass) CodexAppServerBridge — bootstrap + task mapping > a command before the identity echo is still a side effect; text is not [44.97ms]
(pass) CodexAppServerBridge — bootstrap + task mapping > authenticated Dashboard native /goal text reaches the shared thread unchanged and replies [14.24ms]
(pass) CodexAppServerBridge — bootstrap + task mapping > clientUserMessageId rebinds a task when a goal successor replaces the turn/start response id [49.12ms]
(pass) CodexAppServerBridge — bootstrap + task mapping > client-id ownership observed before the RPC response wins without reversing task event order [22.12ms]
(pass) CodexAppServerBridge — bootstrap + task mapping > real bridge + runtime bounds a deferred terminal when exact client identity never arrives [31.85ms]
(pass) CodexAppServerBridge — bootstrap + task mapping > real bridge + runtime bounds an unresolved turn/start through the left-FIFO fallback [56.91ms]
(pass) CodexAppServerBridge — bootstrap + task mapping > agentMessage/delta accumulates when server omits finalText [14.93ms]
(pass) CodexAppServerBridge — bootstrap + task mapping > turn/completed for a HUMAN-TUI-initiated turn is dropped (§7.5) [14.86ms]
(pass) CodexAppServerBridge — bootstrap + task mapping > events for a DIFFERENT thread are dropped (defense in depth) [13.95ms]
(pass) CodexAppServerBridge — bootstrap + task mapping > startTaskTurn refuses a second task while one is active [2.98ms]
(pass) CodexAppServerBridge — bootstrap + task mapping > turn/completed with an error field fires task_error, NOT task_reply [12.86ms]
(pass) CodexAppServerBridge — bootstrap + task mapping > turn/completed with interrupted status cannot become a successful reply [14.26ms]
(pass) CodexAppServerBridge — approvals (waiting_human) §7.6 > reverse-request approval records waiting_human and sends NO response [14.56ms]
(pass) CodexAppServerBridge — approvals (waiting_human) §7.6 > serverRequest/resolved clears waiting_human and status recovers [23.81ms]
(pass) CodexAppServerBridge — approvals (waiting_human) §7.6 > multiple concurrent approvals: bridge stays waiting_human until all resolve [34.57ms]
(pass) CodexAppServerBridge — two-client race for idle > only one bridge wins turn/start; the other observes and does not reply [20.59ms]
(pass) CodexAppServerBridge — authenticated Dashboard steering > reconnect recovers an active human turn and keeps it steerable [4.20ms]
(pass) CodexAppServerBridge — authenticated Dashboard steering > reconnect provenance keeps an orphaned network turn FIFO-only [23.62ms]
(pass) CodexAppServerBridge — authenticated Dashboard steering > reconnect provenance ignores leading whitespace before the network prefix [4.29ms]
(pass) CodexAppServerBridge — authenticated Dashboard steering > reconnect stays FIFO-only when real-wire active history omits userMessage [3.09ms]
(pass) CodexAppServerBridge — authenticated Dashboard steering > uses exact turn/steer contract and maps the human turn final answer [23.84ms]
(pass) CodexAppServerBridge — authenticated Dashboard steering > an interrupted human turn surfaces the steered Dashboard row as an error, not a reply [26.21ms]
(pass) CodexAppServerBridge — authenticated Dashboard steering > multiple Dashboard rows steer one human turn while ordinary agent work stays queued [37.67ms]
(pass) CodexAppServerBridge — authenticated Dashboard steering > steer mismatch fails closed and preserves the task in the normal FIFO [36.38ms]
(pass) CodexAppServerBridge — authenticated Dashboard steering > turn completion cannot attribute a task before turn/steer acceptance [36.78ms]
(pass) CodexAppServerBridge — authenticated Dashboard steering > reconciliation recovers a missed human turn completion and exact steered reply [15.98ms]
(pass) CodexAppServerBridge — sync claim + FIFO queue (通信龙) > concurrent startTaskTurn: exactly ONE turn/start reaches the server even with a slow response [55.10ms]
(pass) CodexAppServerBridge — sync claim + FIFO queue (通信龙) > submitTask queues the second task and drains it after turn/completed (order preserved) [116.68ms]
(pass) CodexAppServerBridge — sync claim + FIFO queue (通信龙) > cancelQueuedTask removes only the named FIFO row before it can execute [56.00ms]
(pass) CodexAppServerBridge — sync claim + FIFO queue (通信龙) > thread/read recovers a completed owned turn while a successor keeps the thread active [106.06ms]
(pass) CodexAppServerBridge — sync claim + FIFO queue (通信龙) > thread/read uses clientUserMessageId to recover a replacement turn when all live item events were lost [3.86ms]
(pass) CodexAppServerBridge — sync claim + FIFO queue (通信龙) > slow full-history fallback recovers when both terminal and successor notifications are lost [2.94ms]
(pass) CodexAppServerBridge — sync claim + FIFO queue (通信龙) > full history never attributes a different completed turn to the owned task [3.61ms]
(pass) CodexAppServerBridge — sync claim + FIFO queue (通信龙) > thread/read never recovers an interrupted turn as success [3.39ms]
(pass) CodexAppServerBridge — sync claim + FIFO queue (通信龙) > drain losing the idle race requeues at the FRONT and retries on next idle [164.17ms]
(pass) CodexAppServerBridge — #1930 queued rows are re-checked before their turn > reproduction: a row queued behind a live turn, then acked on the Hub, still starts a turn when the gate is absent [25.21ms]
(pass) CodexAppServerBridge — #1930 queued rows are re-checked before their turn > with the gate saying the row is no longer pending, no turn starts and task_skipped is emitted [24.15ms]
(pass) CodexAppServerBridge — #1930 queued rows are re-checked before their turn > a row the gate confirms as pending starts exactly as before [24.85ms]
(pass) CodexAppServerBridge — #1930 queued rows are re-checked before their turn > the gate is fail-open: a throwing check starts the row [25.18ms]
(pass) CodexAppServerBridge — #1930 queued rows are re-checked before their turn > a skipped row does not block the row behind it [25.02ms]
(pass) CodexAppServerBridge — #1930 queued rows are re-checked before their turn > the gate is never consulted for a row that starts immediately [4.95ms]
(pass) CodexAppServerBridge — never hydrates full thread history > startup resume sends excludeTurns:true [3.01ms]
(pass) CodexAppServerBridge — never hydrates full thread history > deferred TUI-thread resume sends excludeTurns:true [1.84ms]
(pass) CodexAppServerBridge — never hydrates full thread history > recovery and reconciliation read one bounded page, never includeTurns:true [4.70ms]
(pass) CodexAppServerBridge — never hydrates full thread history > pre-0.151 codex: capability rejection falls back to the legacy calls once [2.65ms]
(pass) CodexAppServerBridge — never hydrates full thread history > a resume timeout is retried with excludeTurns, never downgraded to full hydration [64.64ms]
(pass) CodexAppServerBridge — never hydrates full thread history > an unrelated resume error is not treated as missing excludeTurns support [3.56ms]

src/runtime/codex-app-server/resume-timeout.test.ts:
(pass) startup thread/resume deadline > a slow resume inside the budget succeeds and keeps the thread [304.37ms]
(pass) startup thread/resume deadline > a timed-out resume retries once and succeeds on the loaded thread [154.79ms]
(pass) startup thread/resume deadline > resume timing out on every attempt fails loudly and never starts a new thread [205.11ms]
(pass) startup thread/resume deadline > the bridge default is 300 s, not the client's generic 30 s [3.42ms]
(pass) startup thread/resume deadline > non-timeout resume errors are not retried [2.45ms]
(pass) startup thread/resume deadline > a transport close during resume fails closed and never starts a replacement thread [2.83ms]
(pass) runtime wiring > ANET_CODEX_RESUME_TIMEOUT_MS reaches the bridge and each timeout is logged with the thread id [261.76ms]
(pass) resolveResumeTimeoutMs > unset or blank → default [0.11ms]
(pass) resolveResumeTimeoutMs > whole milliseconds are honoured, including the timer maximum [0.14ms]
(pass) resolveResumeTimeoutMs > invalid values fall back to the default with one warning per distinct value [0.33ms]
(pass) resolveRecoveryMaxPayloadBytes > rollout-derived payload sizing is finite and capped [0.05ms]
(pass) resolveRecoveryMaxPayloadBytes > uses a finite launcher ceiling and rejects unlimited or oversized values [0.22ms]
(pass) resolveRecoveryMaxPayloadBytes > runtime gives the bridge the bounded payload option [0.19ms]
(pass) co-presence materialization deadline wiring > one ANET_CODEX_RESUME_TIMEOUT_MS-derived value governs resume and fresh-thread materialization [0.14ms]
(pass) cli startup failure path > reports the node offline before exiting, since it already registered [1.59ms]

 93 pass
 0 fail
 346 expect() calls
Ran 93 tests across 3 files. [2.62s]

Layer 2: production wiring invariants
PASS: launcher uses exact resume/read verification, quiesced private snapshot, and audit projection
PASS: stale PATH globals are ignored; codex bridge selects exact paired preview.34 and fails closed on identity/capability drift

Layer 3: typecheck + production bundle
$ tsc --noEmit
$ bun build src/client.ts --outdir dist/src --target node --minify && bun build src/daemon-capability-display.ts --outdir dist/src --target browser --minify && bun build bin/cli.ts --outdir dist/bin --target node --minify --external @sleep2agi/commhub-server --external bun:sqlite --external '../../server/*' && bun build src/node-server.ts --outdir dist/src --target node --minify && bun build src/im/feishu/worker.ts --outdir dist/src/im/feishu --target node --minify --external @larksuiteoapi/node-sdk && tsc --emitDeclarationOnly --declaration --outDir dist && bun x javascript-obfuscator dist/bin/cli.js --output dist/bin/cli.js --compact true --string-array true --string-array-encoding base64 && bun x javascript-obfuscator dist/src/client.js --output dist/src/client.js --compact true --string-array true && bun x javascript-obfuscator dist/src/node-server.js --output dist/src/node-server.js --compact true --string-array true && node scripts/stamp-node-server-version.mjs && cp bin/anet.cjs dist/bin/anet.cjs
Bundled 3 modules in 6ms

  client.js  5.79 KB  (entry point)

Bundled 1 module in 4ms

  daemon-capability-display.js  5.34 KB  (entry point)

Bundled 305 modules in 126ms

  cli.js  1.57 MB  (entry point)

Bundled 248 modules in 66ms

  node-server.js  0.33 MB  (entry point)

Bundled 601 modules in 169ms

  worker.js  2.30 MB  (entry point)


[javascript-obfuscator-cli] Obfuscating file: dist/bin/cli.js...

[javascript-obfuscator-cli] Obfuscating file: dist/src/client.js...

[javascript-obfuscator-cli] Obfuscating file: dist/src/node-server.js...
stamped /repo/agent-network/dist/src/node-server.js with 2.3.0-preview.155
PASS: typecheck and production bundle

Layer 3b: real Codex 0.133 large-rollout resume under Node
PASS: codex 0.133 resumed 256.0 MiB rollout under Node in 41933ms
WITNESSED_RED true Node 22 + Codex 0.133 rejects a 256 MiB rollout at a mutated 100 MiB ceiling
Bundled 101 modules in 42ms

  .test-codex-copresence-rpc.mjs  0.70 MB  (entry point)


Layer 4: witnessed-red mutations
WITNESSED_RED src/codex-copresence-recovery.test.ts: WHOLE_FILE_READ_LIMIT
WITNESSED_RED src/codex-copresence-rpc.test.ts: metadata verification shares the recovery deadline
WITNESSED_RED src/codex-copresence-rpc.test.ts: Node recovery lifts the fixed WebSocket frame ceiling
WITNESSED_RED src/codex-copresence-rpc.test.ts: a broken large-payload connection fails immediately
WITNESSED_RED src/runtime/codex-app-server/resume-timeout.test.ts: runtime gives the bridge the bounded payload option
WITNESSED_RED src/runtime/codex-app-server-client.test.ts: a transport error rejects an in-flight request immediately
WITNESSED_RED src/runtime/codex-app-server/resume-timeout.test.ts: rollout-derived payload sizing is finite and capped
WITNESSED_RED src/runtime/codex-app-server/resume-timeout.test.ts: a transport close during resume fails closed and never starts a replacement thread
WITNESSED_RED src/codex-recovery-resource-gate.test.ts: both native launchers hold the recovery lease through TUI attribution
WITNESSED_RED src/codex-recovery-resource-gate.test.ts: a live holder renews past TTL; SIGKILL permits takeover within one TTL
WITNESSED_RED src/codex-copresence-resume-timeout.test.ts: both launchers give bridge and TUI the finite payload/recovery budget
WITNESSED_RED src/codex-copresence-resume-timeout.test.ts: both launchers give bridge and TUI the finite payload/recovery budget
WITNESSED_RED src/codex-copresence-rpc.test.ts: falls back once when a real Codex shape rejects excludeTurns
WITNESSED_RED src/codex-copresence-recovery.test.ts: resume requires exact thread identity and persisted history
WITNESSED_RED src/runtime/codex-app-server-bridge.test.ts: an acknowledged but never-materialized fresh candidate
WITNESSED_RED src/runtime/codex-app-server/resume-timeout.test.ts: one ANET_CODEX_RESUME_TIMEOUT_MS-derived value governs resume and fresh-thread materialization
WITNESSED_RED src/codex-pending-thread-restart.test.ts: marker on disk and bound to it but no rollout
WITNESSED_RED src/codex-copresence-resume-timeout.test.ts: --new-session suppresses the recorded co-presence thread
WITNESSED_RED src/codex-pending-thread-restart.test.ts: Windows uses the same pending-thread decision
WITNESSED_RED src/codex-pending-thread-restart.test.ts: Windows start -> stop -> start drops an unmaterialized pending thread
WITNESSED_RED src/codex-copresence-recovery.test.ts: launcher is fail-closed unless the operator explicitly skips recovery backup
WITNESSED_RED src/codex-copresence-recovery.test.ts: recursive snapshot rejects symlinks
WITNESSED_RED src/codex-copresence-env.test.ts: rejects PATH and loader hooks

Witnessed-red contract
Mutation proven by unit stub: thread/read returns exact id with empty explicit history; metadata-only verification never hydrates full turns and never calls thread/start.
Mutation proven by paired-runtime stub: preview.33 differs from the required preview.34; the resolution plan has allowPathGlobal=false and an exact non-floating spec. Missing codex-app-server help is rejected.
Mutation proven by active-writer stub: snapshot throws if reached while writer=true; production Windows and POSIX call the shared quiesceThenSnapshot boundary exactly once each.
Mutation proven by recursive state fixture: nested session files have relative path + byte size + sha256; a symlink to outside CODEX_HOME is rejected instead of copied.
Mutation proven by >2 GiB sparse rollout: restoring a whole-file read fails while the streaming sparse copy passes.
Mutation proven by slow fake app-server: reducing the derived thread/resume deadline below its response delay fails closed.
Mutation proven by payload-ceiling and close stubs: Node recovery uses a finite rollout-sized WebSocket ceiling, and a closed launcher or bridge transport rejects immediately with its real reason instead of waiting for the recovery deadline.
Mutation proven by recovery quota: both native launchers keep one rollout-sized host lease through exact bridge/TUI attribution; the bridge payload derivation retains its finite upper bound and a transport close never creates a replacement thread.
Measured production-shape reference (Node 22 + Codex 0.133, 500 MiB rollout): exact-thread launcher/bridge/TUI recovery completed in 143 s; peak cgroup memory was 4191 MiB. The gate reserves 9x rollout bytes with a 4 GiB floor, and the live-holder/SIGKILL fixture proves its single lane remains exclusive beyond TTL while crash takeover stays bounded.
Real Codex 0.133 large-rollout E2E: the production Node bundle resumes and verifies the exact 256 MiB persisted thread without a 1006 payload disconnect.
Mutation proven by acknowledged-without-rollout fake: a deferred candidate cannot become codexThreadId until its exact rollout exists; the materialization retry shares ANET_CODEX_RESUME_TIMEOUT_MS.
Mutation proven by --new-session selector: restoring the recorded thread id makes the co-presence test fail; both native platform lanes also discard pending candidates.
Mutation proven by Windows pending-state wiring: an unmaterialized stopped-generation candidate is deleted and that deletion is persisted before snapshot/start.
Mutation proven by recovery fail-closed wiring and partial-backup cleanup: both launchers require a backup unless explicitly skipped; failed directories are removed.
Mutation proven by config.env filtering: PATH and loader hooks are rejected case-insensitively before any stage starts.
Real private-tmux E2E (test535): a fake config.env provider key is present in app-server, bridge, and TUI /proc environments; deleting the merge is witnessed red. Values never enter argv and the 0600 source file is gone after launch.
Identity boundary: config-recovery.json is redacted non-credential metadata only. The original config.json and CODEX_HOME remain in place and are never replaced or cleared.

Release gate (report-only; this Draft does not publish or bump versions)
This change does not bump or publish any package. A release must be built later from an exact main SHA after merge.
RESULT: PASS

Board #762 review follow-up — 2026-10-08
Base: origin/main a625706e1365a65255ab36739ed8f8147fc7f495, merged normally.
Scope: POSIX recovery lease lifetime only; no package version, Hub, or production changes.
Ready means bridge READY plus exact TUI process-tree/port attribution, not paint.
The prior CLI attribution predicates, diagnostics and process.exit paths remain intact.

Docker: tests/test1178-codex-upgrade-recovery/Dockerfile (Node 22, Codex 0.133.0,
Bun 1.3.14); run --cpus=2 --network none. No host tmux or real node touched.
Initial run stopped at layer 1: the new delayed waiter correctly returned
outcome=waited, while the test incorrectly expected ok. Corrected that expectation;
all assertions forbidding admission before attribution were already passing.
Reused the same image with only that test file mounted read-only; no product change
between the initial and successful run. Full existing test1178 then completed rc=0.

Layer 1: launcher 75 pass / 0 fail / 215 expects; bridge 93 pass / 0 fail / 346 expects.
Layer 2: production wiring invariants PASS.
Layer 3: typecheck and production bundle PASS.
Layer 3b: real Node + Codex 0.133 exact 256.0 MiB rollout resume PASS in 41812 ms.
Mutation: real Node + Codex 0.133 with 100 MiB receive ceiling WITNESSED_RED.
Layer 4: 25 unit/wiring mutations WITNESSED_RED (26 total including the Node case).

Lease tests cover both launch orders, live foreground launcher after readiness,
heartbeat/slot cleanup, failure and exit hooks, idempotence, delayed attribution,
and failed attribution. The delayed-attribution fixture uses the real gate and
slot files with injected healthy resource readings, not real TUI processes.
It finishes paint while keeping connection unresolved, observes a waiting second
recovery, then resolves attribution and observes outcome=waited with the lane freed.
WITNESSED_RED: removing successful release fails the foreground cleanup case.
WITNESSED_RED: removing await p.requireTuiConnected() fails the delayed-attribution case.
Existing lease heartbeat and launcher wiring mutations remain red.

Static Docker checks, each rc=0:
- doc symbol pins: docs and docs-site; zero drift, skips explicitly reported.
- doc source pins: zero broken pins, baseline unchanged.
- mutation pins: 691 checked; unchecked 4 <= existing baseline 4.
git diff --check: rc=0.

No new test745 full-unit or test535 private-tmux E2E rerun is claimed in this follow-up.
Earlier footer references to test535 and historical peak memory remain prior evidence.
No production measurement or deployment is implied by this isolated verification.
Current follow-up RESULT: PASS (source verification only; CI/release gates still apply).
