Guardian approval-review sub-session reused as user's main thread — judge base_instructions leak, main thread keeps replying {"outcome":"allow"}
Bug: Guardian approval-review sub-session gets reused as the user's main conversation thread (judge prompt leaks into user turns)
Codex version: recent 0.144.x
Surface: Codex VS Code extension / Desktop
Provider: custom model_provider (OpenAI-compatible, wire_api = "responses")
Summary
When "Approve for me" (auto-review guardian) is enabled, Codex spawns a hidden
guardian approval-review sub-agent session whose base_instructions are the
fixed "You are judging one planned coding-agent action..." judge prompt.
A guardian sub-session ends up persisted and later resumed/displayed as the
user's own main conversation thread. Because a session's base_instructions
are fixed at creation and never re-derived on resume (seecore/src/session/mod.rs: precedence is config.base_instructions →conversation_history session_meta.base_instructions → model default), the
judge prompt stays pinned as the system instruction for the user-facing thread.
Result: for every normal user request, the main model answers as an approval
judge and emits {"outcome":"allow"} (or a full{"risk_level":...,"outcome":"allow","rationale":...}) instead of doing the
work. It gets worse after each auto-compaction — once history is replaced by
a summary, the pinned judge base_instructions dominate, and the thread emits
approval JSON to even trivial prompts (e.g. a user typing "1").
Evidence (from an affected session's rollout session_meta, redacted)
A single conversation id carries BOTH identities across its session_meta
entries in the same rollout file:
session id: <UUID>
forked_from_id: <UUID> # forked from a parent thread
source: cli # <-- user-facing thread
thread_source: user
base_instructions: "You are judging one planned coding-agent action. Assess ..." # <-- guardian JUDGE prompt
# ...and elsewhere in the SAME file:
source: {subagent: {other: "guardian"}} # <-- guardian sub-agent identity
thread_source: subagent
So the user-facing thread (source: cli, thread_source: user) has the
guardian judge prompt as its base_instructions, and the same file also
contains the guardian subagent identity — the two were merged into one thread.
Early turn_context entries use model = "codex-auto-review" (guardian); later
ones switch to the user's configured model via an injected <model_switch>
developer message — but base_instructions is never reset, so the judge prompt
persists.
Expected
A guardian approval-review sub-session must never become the user's main thread.
On resume, the user thread's base_instructions should be the normal agent
prompt for the current model, not a guardian judge prompt inherited from a
sub-agent session.
Impact
The user's main conversation becomes unusable: every turn returns{"outcome":"allow"} instead of doing work. It is not self-healing within the
session (base_instructions are immutable), so the only recovery is starting a
new conversation, losing conversational context.
Repro (observed)
- Custom OpenAI-compatible provider,
wire_api = "responses". - Enable "Approve for me" (auto-review guardian).
- Use the session across actions and let auto-compaction trigger a few times.
- Main thread begins returning
{"outcome":"allow"}to normal requests,
especially right after context-compacted events.
---
Related issues (same guardian-subagent / user-thread boundary area, different symptoms):
- #32035 — guardian subagent threads exposed in the conversation list; main thread can't resume/fork
- #33670 — guardian threads retain parent in rollouts but absent from thread_spawn_edges
This report differs from those: here the guardian judge base_instructions itself is inherited by the user-facing thread, so the main model keeps answering with approval JSON instead of doing the work.