test(server): record Codex replay fixtures on gpt-6-luna and Codex 0.156.1 - #13533
Conversation
The recorder started threads with `{}`, so Codex used the hub's default
model and a later revert reloaded the thread with a model-mismatch
warning. It now sends `cwd` and `model` on thread/start, resume and fork
the way the adapter does, resumes with `excludeTurns`, forks at the
source's latest turn, and terminates commands an interrupt left running.
Replay ignores the thread params, so recordings still match.
The default model is gpt-6-luna. Scenarios can pin their own model where
gpt-6-luna does not exercise the path (multi-agent v1, apply_patch
approvals, marker recall). New queued_turn and web_search scenarios
replace 0.120 probe captures. todo_list enables `tools.update_plan`,
which Codex 0.156 leaves off by default.
Recordings now replace the home directory and hostname with neutral
values, and Codex no longer inherits the vp shim's VP_TOOL_RECURSION,
which made every `node` the agent ran fail.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…156.1 The hub no longer serves gpt-5.4, and most Codex transcripts came from Codex 0.120-0.137. Re-record the 23 fixtures the recorder supports live on Codex 0.156.1. The shared Codex model is now gpt-6-luna, and the per-fixture overrides from the rollback and subagent_v2 recordings are gone. subagent and subagent_continue stay on gpt-5.6-luna so the multi-agent v1 collabAgentToolCall path keeps live coverage. A few fixtures pin gpt-6-sol or gpt-5.6-terra where gpt-6-luna skips the behaviour under test. Assertions that pinned the old model's wording (agent paths, plan heading, question id, subagent phrasing, reasoning item count) now check the behaviour instead. The resume check follows the adapter's excludeTurns request, and the interrupt check derives the terminated process from the recording. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Macroscope skipped reviewing this pull request. Per-review cost limit exceeded (workspace setting). This review would cost an estimated $99.19, which exceeds your per-review limit of $15.00. The top 3 files driving up this estimate:
Tip To get this pull request reviewed, you can:
|
ApprovabilityVerdict: Would Approve Macroscope's review found this PR approvable — This PR refreshes Codex replay fixtures and improves the test-only recording harness for deterministic machine identity, model selection, thread lifecycle, and new scenarios. It does not alter production request paths or product defaults. Not approved because:
Review your spending limits in Billing settings, or comment |
Thread transfer impact✅ Thread transfer remains within every enforced ceiling.
Baseline: unavailable · PR result: Scenario and decoded snapshot size10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.
Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed. |
…play fixtures The recorded remoteControl/status/changed frames carried this machine's Codex installation id, in 23 transcripts. Account/updated frames carried the plan type, and inbound cwd values named the local worktree layout. The recorder now replaces any installationId with a fixed placeholder UUID, any planType with "unknown", and the checkout root with /home/replay-user/t3code, alongside the home and hostname it already scrubbed. The same substitutions are applied to every committed Codex transcript, including the hand-authored and constructed ones, which also drops their old /Users paths. Outbound frames are byte-identical, so replay matching is unchanged; the adapter reads none of these values. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
9604f00
into
t3code/codex-turn-mapping
…156.1 (#13533) Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The hub no longer serves
gpt-5.4, the model every Codex replay fixture was pinned to, and most of those transcripts came from Codex 0.120–0.137. This re-records every Codex fixture the recorder can reproduce, live on Codex 0.156.1, and moves the default Codex test model togpt-6-luna.What changed
Default model.
CODEX_MODEL_SELECTIONand the recorder's default are nowgpt-6-luna. The per-fixturegpt-5.6-sol(subagent_v2, subagent_v2_nested) andgpt-5.6-luna(thread_rollback) overrides from #13505/#13508 are gone, because all three now record on the default.Recorder (
record-codex-app-server-replay-fixture.ts):thread/start,thread/resumeandthread/forknow sendcwdandmodel, the way the adapter does. The old{}started threads on the hub's default model, and a revert then reloaded them with a model-mismatch warning. Replay ignores these fields, so the transcripts still match. Resume also sendsexcludeTurns: true, and fork sends the source's latestlastTurnId, again matching the adapter.--modelstill overrides it. Plan mode now takes the collaboration-mode model from the run instead of a hardcodedgpt-5.4.queued_turnandweb_search. Both fixtures were 0.120 probe captures the recorder couldn't reproduce.turn/interrupt, the recorder terminates any command still running (thread/backgroundTerminals/terminate), as the adapter does.VP_TOOL_RECURSIONfrom thevpnode shim. With it set, everynodethe agent ran failed with "Recursion detected". That turned the mid-tool interrupt recording into a command that failed instantly instead of one that was interrupted.codexReplayRecordingRecords.tsscrubs every frame before writing: the checkout root becomes/home/replay-user/t3code, the home directory/home/replay-user, the hostnamereplay-host, anyinstallationIdvalue00000000-0000-4000-8000-000000000000, and anyplanTypevalueunknown. The adapter reads none of these.proplan type, and old/Users/…and worktree paths. Outbound frames were checked to be byte-identical before and after, so replay matching is unaffected.Per-fixture models. Each one is set in both the recorder scenario and the fixture's
modelSelection:subagent,subagent_continuegpt-5.6-lunacollabAgentToolCall) path covered.gpt-6-lunaruns v2, and these are the only v1 recordings.tool_call_read_only_on_requestgpt-6-solgpt-6-lunarefuses the write without trying it, so no approval is ever requested.tool_call_restricted_granulargpt-5.6-terragpt-5.6-terrausesapply_patch, which raises the file-change approval this fixture exists for.thread_fork_native_siblings,thread_merge_back_continue,thread_merge_back_siblingsgpt-6-solgpt-6-lunaoften repeats an earlier acknowledgement ("merge source stored") instead of the markers.ThreadMergeBack.integration.test.tsusesgpt-6-solfor its Codex variant to match.Multi-agent versions, checked in the recorded frames:
subagent: v1.collabAgentToolCallspawnAgent ×2 and wait, nosubAgentActivity.subagent_continue: v1. spawnAgent, thenresumeAgentandsendInputinto the same child thread.subagent_v2: v2.subAgentActivitystarted/completed. The child'sturn/startedcomes after the parent's activity, soassertSubagentActivityPrecedesChildTurnsstill expects 1 child turn.subagent_v2_nested: v2 at three levels, 3 child turns.Recorder config for two scenarios:
todo_listsetstools.update_plan.enabled: true. Codex 0.156 only registersupdate_planwhen this is on (resolve_update_plan_enableddefaults to false). Without it the model reports "Plan update unavailable" and emits noturn/plan/updated. The adapter doesn't set it either, so for real users the todo list is empty on 0.156. That's a separate decision and is not changed here.subagentsetsskills.include_instructions: false. Without it, the children read the recording user's skill files into the transcript.Assertion changes
Where an assertion pinned wording the model picked, I loosened it to the behaviour. None of these hides an adapter change.
subagentchecks that each child got one file and reported something only that file contains (effect-codex-app-server,../../tsconfig.base.json). It used to match gpt-5.4's exact prompt and result phrasing.subagent_v2/_nestedcheck that each agent path extends its parent's by one segment. They used to pin the names the old model picked (/root/hello,relay_one/relay_two). The nested answers areHello.at every level now.subagent_continueuses a follow-up prompt without@hooke, a nickname the old recording happened to get, and itsCodexReplayFixturesexpectations use the recorder's method labels instead of hand-written role labels.web_searchnow expectsuser, assistant, assistant. gpt-6 posts a commentary message ("I'll check current ticket pricing…") before the answer, and the adapter projects commentary as an assistant message by design.proposed_planchecks that the plan mentions replay and fixtures instead of the headingDeterministic Replay Fixtures. The expected incoming label is nowitem/plan/delta: the model streams the plan and gives no separate agent message.plan_questions: the model named its questionschema_preference. The Codex variant asserts that id, and the input answers it. Grok and ACP keep their recordedschema_vs_ui_flexibilitythrough the shared helper.thread_rollbackno longer expects two reasoning items in the post-rollback turn, since this recording has none. The rollback checks are unchanged: turn 2 hidden, the recall has turn 1 and not turn 2.tool_call_restricted_granularno longer requires acommand_executionitem; the file-change approval is the behaviour under test. Theturn/diff/updatedanditem/fileChange/outputDeltaexpectations are gone because the recorder opts out ofturn/diff/updatedand 0.156 sends no output delta forapply_patch.turn_interrupt_mid_toolchecks that the terminate request names the thread andprocessIdof the command that was running. It used to hardcode the old recording's ids. It now also accepts the terminate going out beforeturn/completed: the adapter runs termination alongside the completion wait, and in this recording it lands first.provider_thread_resume: since the adapter resumes withexcludeTurns, the resume response has no turns. The check now asserts that the request setsexcludeTurns, that the response is empty, and that the second answer still repeats the first. That last check proves the history reached the model.Not re-recorded
thread_fork_native: hand-authored, with synthetic thread ids.ThreadFork.integration.test.tsdepends on those ids.thread_fork_native_prior_turn: a normalized 0.124 live probe that forks at an earlier turn. The recorder has no step that forks at a chosen turn.delegated_task_status: constructed from composed frames forOrchestratorMcpToolkit.integration.test.ts. The recorder can't drive the MCP delegation it needs.queued_cancelled_while_active: reuses thequeued_turntranscript, which is re-recorded.ThreadFork.integration.test.tsandOrchestratorMcpToolkit.integration.test.tsstill pingpt-5.4, because their three transcripts do.Reviewer note: the adapter's
excludeTurnson resume is only covered byOrchestratorReplayRecovery. If it regresses, that test fails with a 60s await timeout instead of a frame mismatch.Bugs found
No adapter bug. Two findings for follow-up, not changed here:
update_planis off by default in Codex 0.156, so T3 users on 0.156 get no todo lists unless the adapter enables it (see above).vpnode shim setsVP_TOOL_RECURSION, and any child that inherits it breaksnodeinside agent shells. The recorder now strips it. A T3 server started throughvplikely passes it on to Codex the same way. I did not check that.Line delta: +2637 / −2841 across 41 files. Code and tests account for +281 / −127 in 16 files; the other 25 files are transcripts.
Verification
T3_CODEX_BIN=<codex 0.156.1> node apps/server/scripts/record-codex-app-server-replay-fixture.ts --scenario <name>, run frompackages/effect-codex-app-server. None needed a rate-limit retry.vp test runinapps/serveronOrchestratorReplayFixtures.integration(all providers),OrchestratorReplayRecovery.integration,CodexReplayFixtures.integration,ThreadFork.integration,ThreadMergeBack.integration,OrchestratorReplayFixtures.contract,CodexAdapterV2.test,OrchestratorMcpToolkit.integration,ProviderSwitch.integration,ClaudeReplayFixtures.integration: 10 files, 287 tests passed.ProviderSwitchandClaudeReplayFixtures(which don't read Codex transcripts): 8 files, 220 tests passed. A scratch live recording ofsimpleafter the scrub carried the placeholder installation id,planType: "unknown",replay-hostand/home/replay-user/t3codepaths, and no local strings.vp exec tsc --noEmit -p .inapps/server: noerror TSorwarning TS.vp run knip:check: passes.vp linton the touched files: only the unusedassertUserMessagesExcludeinfixtures/shared.ts, which is already on the base branch./Users/paths,.t3/worktrees, the installation id,"planType":"pro", hub and tailnet hostnames and IPs, the machine name, emails, JWTs and bearer strings. The only hit isgit@github.comin a recorded git remote. Codex also reports the checked-out branch, the commit sha andhttps://github.com/pingdotgg/t3code.gitingitInfo; all of that is public.Model: Claude Opus 5.5 (Claude Code)
🤖 Generated with Claude Code