Test 1178 — Codex upgrade recovery and topology audit

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

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

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

src/codex-copresence-recovery.test.ts:
(pass) Codex co-presence recovery > resume requires exact thread identity and persisted history [0.85ms]
(pass) Codex co-presence recovery > stub resume failure is fail-closed and never calls thread/start [0.53ms]
(pass) Codex co-presence recovery > backup preserves config and session state but excludes credentials [8.81ms]
(pass) Codex co-presence recovery > active state writer is quiesced before the authoritative snapshot [0.40ms]
(pass) Codex co-presence recovery > recursive snapshot rejects symlinks instead of following state outside CODEX_HOME [1.37ms]
(pass) Codex co-presence recovery > audit exposes topology without config secrets [0.19ms]

 14 pass
 0 fail
 44 expect() calls
Ran 14 tests across 3 files. [89.00ms]

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 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 && cp bin/anet.cjs dist/bin/anet.cjs
Bundled 3 modules in 9ms

  client.js  5.79 KB  (entry point)

Bundled 223 modules in 98ms

  cli.js  1.22 MB  (entry point)

Bundled 230 modules in 57ms

  node-server.js  0.29 MB  (entry point)

Bundled 600 modules in 183ms

  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...
PASS: typecheck and production bundle

Witnessed-red contract
Mutation proven by unit stub: thread/read returns exact id with empty history; test rejects and call trace is only thread/resume,thread/read (no 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.
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)
preview.45 is immutable/already published. This release candidate bumps agent-network to preview.46, agent-node to preview.34, and commhub-server to preview.30; run all release gates before publishing. Do not overwrite an existing version or move latest.
RESULT: PASS
