Skip to content

[Bug]: Codex /compact fails instantly when a resumed turn starts before the compaction turn #18833

Open

Description

@tverma101

Area

apps/server

Steps to reproduce

Minimal deterministic repro using the CodexAdapterV2 app-server replay harness. The following inbound frame sequence fails on current main:

  1. Client sends thread/compact/start for a Codex thread.
  2. Before the compaction turn begins, Codex emits turn/started for an unrelated resumed goal turn (status: "inProgress"). Resumed goals legitimately re-emit a turn on reconnect, and starting a compaction does not suppress this.
  3. The adapter's turn/started handler claims the first pending root turn for the thread (pendingRootTurns is keyed by thread ID only, with no check of which native turn it belongs to), so the resumed turn is registered as the compaction run.
  4. The resumed turn completes as interrupted (turn/completed with status: "interrupted").
  5. The real compaction turn (turn/started + item/started with item.type: "contextCompaction") then arrives, but the pending entry has already been consumed; the compaction item is never attached to a run.

Expected behavior

The compaction run should be identified by its contextCompaction item (its own turn and item/started event), not by whichever turn Codex happens to start first. An unrelated resumed turn must not be claimed as the compaction run, and compaction should proceed and complete normally.

Actual behavior

The compaction fails immediately with Context compaction ended with interrupted. even though Codex never reported the compaction itself as interrupted. In production this surfaces as: pressing /compact kills the in-flight turn (turn state interrupted) and errors within milliseconds, before any provider request is sent.

Observed live on a long-running Codex thread (macOS Alpha build): 4/4 manual /compact attempts failed this way, while auto-compaction of the same thread succeeded 7 times the same evening through the identical provider path — isolating the failure to the adapter's turn-claim step.

Impact

Major degradation or frequent failure

Version or commit

main @ c0c495e (checkout of main, 2026-10-10). The turn/started claim block is unchanged on latest main as of 2026-10-12 (file last modified in ed0f7b9). Also observed live on the 0.0.45 macOS Alpha release build.

Environment

macOS (darwin 27.0.0), Node 26, Codex app-server via the @openai/codex binary, T3 Code 0.0.45 Alpha.

Logs or stack traces

Replaying the frame sequence above against main (both start-RPC variants — accepted and error-returning):

× tracks the compaction item after an interrupted resumed turn when the start RPC is accepted
× tracks the compaction item after an interrupted resumed turn when the start RPC is interrupted

AssertionError: expected [] not to be empty
ProviderAdapterTurnStartError: Failed to start run on codex provider thread: …
Caused by: CodexAppServerRequestError: Context compaction ended with interrupted.
  { code: -32000, errorMessage: 'Context compaction ended with interrupted.',
    method: 'thread/compact/start', operation: 'receive-response' }

The same scenario passes once the compaction run is tracked by its contextCompaction item instead of the first turn/started.

Workaround

None from the UI. A source-level fix that identifies the compaction run by its contextCompaction item (a pending-compaction map keyed by native thread, registered when item/started delivers item.type === "contextCompaction") resolves it deterministically. I have this as a local fix with a regression test covering both start-RPC variants, and can send a focused PR if the problem is triaged.

Activity

  1. juliusmarminge commented on Oct 12, 2026

    @juliusmarminge
    Member

    Note

    Grok responding on behalf of Julius.

    Thanks for the detailed repro. I checked this against main and it holds up.

    • compactThread puts the run into pendingRootTurns keyed only by native thread ID, then calls thread/compact/start.
    • In the turn/started handler, the pendingRootTurns lookup comes before the goalHolds and active-goal branches, and it doesn't check what kind of turn just started. So if Codex continues a goal and that turn starts first, the turn gets registered as the compaction run and the pending entry is deleted. When the real compaction turn starts later, nothing owns it, and the run settles with the interrupted status of the goal turn.
    • That fits what you saw: auto-compaction works because it doesn't go through compactThread or the pending claim.

    Open PR #17265 doesn't fix this. It settles compaction items that never got item/completed when their turn ends, and it doesn't touch the turn/started claim. #12885 was a different issue: a Claude session getting evicted across threads.

    Accepting this as a bug. Please send the focused PR. Two notes for it:

    • Make sure a goal continuation that arrives while a compaction is pending still reaches the goalHolds path, so it joins its own run instead of being dropped or stopped as an orphan.
    • Keep the existing rule that a start that errors or is interrupted clears the pending entry.

    Please include the replay test that covers both start-RPC variants.

  2. added
    acceptedfeature request accepted
    bugSomething is broken or behaving incorrectly.
    via-triageFiled through npx t3 triage
    on Oct 12, 2026
  3. juliusmarminge commented on Oct 21, 2026

    @juliusmarminge
    Member

    Tested current main 70e5e384da with the real web dev app on Linux x86_64, Node 24.21.0, codex-cli 0.163.0, GPT-6-Astra, and isolated state.

    /compact completed in four attempts: a fresh conversation, after stopping a running goal, queued after goal resume, and after restarting the dev server during an active goal. Each emitted contextCompaction start/completion and a completed compaction turn. The restart also produced a separate provider-start error before the compact request.

    Sanitized runtime transcript with steps and protocol timestamps.

    I did not observe an unrelated resumed turn starting between thread/compact/start and the compaction turn. This does not rule out the reported race. Please attach the replay fixture/regression test mentioned in the report, the affected codex --version, and the UI/reconnect steps that trigger that ordering. macOS Alpha was not verified here. Leaving open pending those details.

  4. tverma101 commented on Oct 22, 2026

    @tverma101
    Author

    Thanks for testing this in the real T3 web app, including goal resume and a server restart. I now have a fresh reproduction of the missing event ordering, plus a failure through the unmodified installed T3 server. PR #18912 is already open for this bug, so this adds reporter-side reproduction evidence to that existing work.

    Affected version: the original affected IINA native session records cli_version: "0.162.1". That is historical session metadata. For the fresh tests, I also ran the official 0.162.1 binary and the installed 0.163.0 binary, checking their actual --version outputs.

    Repeatable protocol trigger:

    1. Create a conversation with history and persist an active goal.
    2. Close that app-server, then start a fresh one against the same private Codex home.
    3. Send thread/resume, then immediately send thread/compact/start without waiting for resumed goal work to settle. The fixture tests both awaiting the resume response and pipelining those two requests.
    4. Compare with the same reconnect while the goal is paused.

    On macOS arm64, each binary produced the competing goal start inside the compact window in 8/8 active-goal trials and 0/4 paused-goal controls. All 24 direct native compactions completed, and their start RPCs returned accepted responses. These live-process experiments use a loopback mock Responses endpoint for model output; Codex itself generates the native turn events, with no injected turn notifications or RPC errors.

    Fresh T3 failure: I ran the unmodified installed T3 Code Beta 0.1.0 server, a private T3 home, and Codex 0.163.0. I prepared an imported test conversation with history through the real web app, stopped the private server, reactivated the native goal, restarted the server, and dispatched /compact through T3's normal authenticated WebSocket API. The joined native/orchestration trace is:

    2026-10-22, UTC
    19:44:20.261  T3 sends thread/compact/start
    19:44:20.284  Goal turn A starts
    19:44:20.285  A completes interrupted
    19:44:20.286  Real compaction turn B starts
    19:44:20.287  B emits contextCompaction
    19:44:20.290  T3 sends thread/goal/set: paused
    19:44:20.291  T3 persists the compact run as interrupted, bound to A
    19:44:20.335  T3 sends turn/interrupt specifically targeting B
    19:44:20.439  B completes interrupted
    

    The server logged orchestration-v2.codex-goal-turn-without-run for B, the real compaction turn. The wrong ownership therefore also causes T3 to actively interrupt the actual compact. The same server's subsequent warm, paused-goal compact completed normally.

    An additional cold T3 test with 0.162.1 missed the critical window: the goal start arrived 3 ms before the outgoing compact request, the compaction was owned correctly, and it completed after the intentionally held mock was released. Thus reconnection alone is not a guaranteed UI recipe; the exact timing condition and native fixture matter.

    Fixtures and limits: the expandable sections below include the complete standalone Python probe, the original test-only adapter patch, and sanitized evidence with native turn IDs, T3 ownership, the outbound interrupt, controls, and persisted statuses. Save the Python block as native_probe.py and run:

    python3 native_probe.py --binary /absolute/path/to/codex

    The Python probe uses only the standard library, isolated state, and local mock responses; it needs no account or paid inference. The original adapter patch targets c0c495e4dc12bbb0bb44387657a5682661cdd05c and covers accepted/error-returning start responses. Its error-returning variant explicitly scripts the error, so it is separate from the real-process evidence above. I have not freshly rerun that old Vitest patch or live-tested #18912's head. I also have not reproduced the exact historical Alpha wording Context compaction ended with interrupted. Final failure verification was over server protocol and persisted state; browser snapshot timeouts prevented a useful recording of that final failure. No claim here that the existing PR's fix is live-validated.

    Standalone real-Codex reproduction: native_probe.py
    #!/usr/bin/env python3
    """Exercise the installed Codex app-server with isolated state and a local model."""
    import asyncio
    import argparse
    import contextlib
    import json
    import os
    from pathlib import Path
    import subprocess
    import time
    import uuid
    
    ROOT = Path(__file__).resolve().parent
    PARSER = argparse.ArgumentParser()
    PARSER.add_argument("--binary", default="codex")
    ARGS = PARSER.parse_args()
    BINARY = str(Path(ARGS.binary).resolve()) if "/" in ARGS.binary else ARGS.binary
    VERSION = subprocess.check_output([BINARY, "--version"], text=True).strip().split()[-1]
    PREFIX = "codex-" + VERSION
    LOG = ROOT / (PREFIX + ".protocol.jsonl")
    MODEL_LOG = ROOT / (PREFIX + ".mock-model.jsonl")
    STDERR_LOG = ROOT / (PREFIX + ".stderr.jsonl")
    
    
    def record(path, value):
        with path.open("a") as f:
            f.write(json.dumps({"time_ns": time.time_ns(), **value}) + "\n")
    
    
    class MockModel:
        def __init__(self):
            self.requests = 0
            self.received = asyncio.Queue()
            self.release = asyncio.Event()
            self.release.set()
    
        async def handle(self, reader, writer):
            try:
                head = await reader.readuntil(b"\r\n\r\n")
                lines = head.decode().split("\r\n")
                method, path, _ = lines[0].split(" ")
                headers = dict(l.split(": ", 1) for l in lines[1:] if ": " in l)
                body = await reader.readexactly(int(headers.get("content-length", "0")))
                self.requests += 1
                await self.received.put(path)
                record(MODEL_LOG, {"method": method, "path": path, "bytes": len(body)})
                await self.release.wait()
                rid = "resp_" + uuid.uuid4().hex
                msg = {"id": "msg_" + uuid.uuid4().hex, "type": "message", "status": "completed", "role": "assistant", "content": [{"type": "output_text", "text": "Local test response. Continue the task if a goal is active.", "annotations": []}]}
                response = {"id": rid, "object": "response", "created_at": int(time.time()), "status": "completed", "model": "gpt-5.4", "output": [msg], "usage": {"input_tokens": 20, "output_tokens": 12, "total_tokens": 32}}
                if path.endswith("/compact"):
                    payload = json.dumps({"id": rid, "object": "response.compaction", "created_at": int(time.time()), "output": [{"type": "compaction", "id": "cmp_" + uuid.uuid4().hex, "encrypted_content": "local-test-summary"}], "usage": response["usage"]}).encode()
                    writer.write(b"HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: " + str(len(payload)).encode() + b"\r\nConnection: close\r\n\r\n" + payload)
                else:
                    events = [("response.created", {"type": "response.created", "response": {"id": rid}}), ("response.output_item.done", {"type": "response.output_item.done", "output_index": 0, "item": msg}), ("response.completed", {"type": "response.completed", "response": response})]
                    payload = "".join("event: " + kind + "\ndata: " + json.dumps(value) + "\n\n" for kind, value in events).encode()
                    writer.write(b"HTTP/1.1 200 OK\r\nContent-Type: text/event-stream\r\nContent-Length: " + str(len(payload)).encode() + b"\r\nConnection: close\r\n\r\n" + payload)
                await writer.drain()
            except (ConnectionError, asyncio.IncompleteReadError):
                pass
            finally:
                writer.close()
                with contextlib.suppress(Exception):
                    await writer.wait_closed()
    
    
    class Client:
        def __init__(self, home, label):
            self.home, self.label = home, label
            self.pending = {}
            self.next_id = 0
            self.events = []
            self.changed = asyncio.Event()
    
        async def start(self):
            env = {k: v for k, v in os.environ.items() if not any(s in k.upper() for s in ["TOKEN", "API_KEY", "AUTH", "T3CODE", "OPENAI"])}
            env["CODEX_HOME"] = str(self.home)
            self.proc = await asyncio.create_subprocess_exec(BINARY, "app-server", "--listen", "stdio://", cwd=ROOT / "workspace", env=env, stdin=asyncio.subprocess.PIPE, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE)
            record(LOG, {"phase": self.label, "spawned_pid": self.proc.pid})
            self.reader = asyncio.create_task(self.read())
            self.stderr = asyncio.create_task(self.read_stderr())
            await self.call("initialize", {"clientInfo": {"name": "t3_compact_local_probe", "version": "1"}, "capabilities": {"experimentalApi": True}})
            await self.send({"method": "initialized"})
    
        async def read_stderr(self):
            while line := await self.proc.stderr.readline():
                record(STDERR_LOG, {"phase": self.label, "line": line.decode(errors="replace").rstrip()})
    
        async def read(self):
            while line := await self.proc.stdout.readline():
                frame = json.loads(line)
                record(LOG, {"phase": self.label, "direction": "in", "frame": frame})
                if "id" in frame and ("result" in frame or "error" in frame):
                    f = self.pending.pop(frame["id"], None)
                    if f is not None and not f.done():
                        f.set_result(frame)
                elif "method" in frame:
                    self.events.append(frame)
                    self.changed.set()
    
        async def send(self, frame):
            record(LOG, {"phase": self.label, "direction": "out", "frame": frame})
            self.proc.stdin.write((json.dumps(frame) + "\n").encode())
            await self.proc.stdin.drain()
    
        async def begin(self, method, params):
            self.next_id += 1
            f = asyncio.get_running_loop().create_future()
            self.pending[self.next_id] = f
            await self.send({"id": self.next_id, "method": method, "params": params})
            return f
    
        async def call(self, method, params):
            f = await self.begin(method, params)
            result = await asyncio.wait_for(f, 15)
            if "error" in result:
                raise RuntimeError(f"{method}: {result['error']}")
            return result["result"]
    
        async def until(self, predicate, since=0, timeout=15):
            async def wait():
                while True:
                    self.changed.clear()
                    for e in self.events[since:]:
                        if predicate(e):
                            return e
                    await self.changed.wait()
            return await asyncio.wait_for(wait(), timeout)
    
        async def close(self):
            self.proc.stdin.close()
            try:
                await asyncio.wait_for(self.proc.wait(), 5)
            except asyncio.TimeoutError:
                self.proc.terminate()
                try:
                    await asyncio.wait_for(self.proc.wait(), 5)
                except asyncio.TimeoutError:
                    self.proc.kill()
                    await self.proc.wait()
            await asyncio.gather(self.reader, self.stderr, return_exceptions=True)
            record(LOG, {"phase": self.label, "exit_code": self.proc.returncode})
    
    
    async def main():
        for p in [LOG, MODEL_LOG, STDERR_LOG]:
            p.write_text("")
        (ROOT / "workspace").mkdir(exist_ok=True)
        home = ROOT / ("isolated-" + PREFIX)
        home.mkdir(exist_ok=True)
        model = MockModel()
        server = await asyncio.start_server(model.handle, "127.0.0.1", 0)
        port = server.sockets[0].getsockname()[1]
        (home / "config.toml").write_text(f'''model = "gpt-5.4"
    model_provider = "local_probe"
    approval_policy = "never"
    sandbox_mode = "read-only"
    check_for_update_on_startup = false
    [analytics]
    enabled = false
    [feedback]
    enabled = false
    [features]
    skip_host_skill_discovery = true
    [model_providers.local_probe]
    name = "Local reproduction mock"
    base_url = "http://127.0.0.1:{port}/v1"
    wire_api = "responses"
    requires_openai_auth = false
    supports_websockets = false
    request_max_retries = 0
    stream_max_retries = 0
    ''')
        c = Client(home, "prepare")
        try:
            await c.start()
            result = await c.call("thread/start", {"cwd": str(ROOT / "workspace"), "model": "gpt-5.4", "modelProvider": "local_probe", "approvalPolicy": "never", "sandbox": "read-only", "experimentalRawEvents": True})
            thread = result["thread"]["id"]
            print("isolated thread", thread, flush=True)
            await c.call("turn/start", {"threadId": thread, "input": [{"type": "text", "text": "Reply with a short test greeting.", "text_elements": []}]})
            event = await c.until(lambda e: e["method"] == "turn/completed")
            print("initial turn", event["params"]["turn"]["status"], "model requests", model.requests, flush=True)
            start = len(c.events)
            f = await c.begin("thread/compact/start", {"threadId": thread})
            response = await asyncio.wait_for(f, 15)
            await c.until(lambda e: e["method"] == "turn/completed", since=start)
            print("plain compact RPC", json.dumps(response), flush=True)
            model.release.clear()
            await c.call("thread/goal/set", {"threadId": thread, "objective": "Continue saying local test greetings until explicitly stopped.", "status": "active", "origin": "user", "tokenBudget": 1000})
            await c.until(lambda e: e["method"] == "turn/started", since=start + 1)
            await c.close()
            summaries = []
            for trial in range(12):
                active = trial % 3 != 0
                mode = ("active_resume_then_compact" if trial % 3 == 1 else "active_pipelined_resume_compact") if active else "paused_goal_control"
                # Persist the desired goal status, then reconnect with a new process.
                # This uses the same thread and home; each trial replaces its state.
                seed = Client(home, f"seed_{trial}")
                await seed.start()
                await seed.call("thread/resume", {"threadId": thread, "excludeTurns": True})
                await seed.call("thread/goal/set", {"threadId": thread, "status": "active" if active else "paused", "origin": "user"})
                await seed.close()
                label = f"{mode}_{trial}"
                c = Client(home, label)
                await c.start()
                params = {"threadId": thread, "cwd": str(ROOT / "workspace"), "model": "gpt-5.4", "modelProvider": "local_probe", "excludeTurns": True}
                if mode != "active_pipelined_resume_compact":
                    await c.call("thread/resume", params)
                    f = await c.begin("thread/compact/start", {"threadId": thread})
                else:
                    resume = await c.begin("thread/resume", params)
                    f = await c.begin("thread/compact/start", {"threadId": thread})
                    await asyncio.wait_for(resume, 15)
                model.release.set()
                result = await asyncio.wait_for(f, 15)
                item_event = await c.until(lambda e: e["method"] == "item/started" and e["params"].get("item", {}).get("type") == "contextCompaction")
                compact_turn_id = item_event["params"]["turnId"]
                await c.call("thread/goal/set", {"threadId": thread, "status": "paused", "origin": "user"})
                terminal = await c.until(lambda e: e["method"] == "turn/completed" and e["params"]["turn"]["id"] == compact_turn_id)
                frames = [json.loads(line) for line in LOG.read_text().splitlines()]
                frames = [d for d in frames if d.get("phase") == label]
                sent = next(i for i, d in enumerate(frames) if d.get("direction") == "out" and d.get("frame", {}).get("method") == "thread/compact/start")
                compact_start = next(i for i, d in enumerate(frames) if d.get("direction") == "in" and d.get("frame", {}).get("method") == "turn/started" and d["frame"]["params"]["turn"]["id"] == compact_turn_id)
                stray = [d["frame"]["params"]["turn"] for d in frames[sent + 1:compact_start] if d.get("direction") == "in" and d.get("frame", {}).get("method") == "turn/started"]
                summary = {"trial": trial, "mode": mode, "stray_starts_in_compact_window": len(stray), "compact_rpc_error": result.get("error"), "compact_final_status": terminal["params"]["turn"]["status"], "compact_turn_id": compact_turn_id}
                summaries.append(summary)
                (ROOT / (PREFIX + ".results.json")).write_text(json.dumps({"binary_version": VERSION, "trials": summaries}, indent=2) + "\n")
                print(json.dumps(summary), flush=True)
                model.release.clear()
                await c.close()
        finally:
            if hasattr(c, "proc") and c.proc.returncode is None:
                await c.close()
            model.release.set()
            server.close()
            await server.wait_closed()
    
    
    if __name__ == "__main__":
        asyncio.run(main())
    Original CodexAdapterV2.test.ts regression patch (accepted / interrupted RPC variants)
    diff --git a/apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.test.ts b/apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.test.ts
    index 9a1c8f6c9..fc0f01128 100644
    --- a/apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.test.ts
    +++ b/apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.test.ts
    @@ -2913,6 +2913,145 @@ describe("CodexAdapterV2 post-settle continuation", () => {
         ),
       );
     
    +  it.effect.each(["accepted", "interrupted"] as const)(
    +    "tracks the compaction item after an interrupted resumed turn when the start RPC is %s",
    +    (startResult) =>
    +      Effect.scoped(
    +        Effect.gen(function* () {
    +          const nativeThreadId = "compact-race-thread";
    +          const resumedTurnId = "resumed-goal-turn";
    +          const compactTurnId = "compact-race-turn";
    +          const item = { type: "contextCompaction", id: "compact-race-item" };
    +          const transcript = makeCodexReplayTranscript({
    +            scenario: "native-compaction-after-resumed-turn",
    +            entries: [
    +              ...codexReplayPreamble({
    +                nativeThreadId,
    +                nativeTurnId: "unused-turn",
    +                prompt: "unused",
    +              }).slice(0, 5),
    +              {
    +                type: "expect_outbound",
    +                label: "compact",
    +                frame: {
    +                  id: 3,
    +                  method: "thread/compact/start",
    +                  params: { threadId: nativeThreadId },
    +                },
    +              },
    +              {
    +                type: "emit_inbound",
    +                label: "resumed goal starts first",
    +                frame: {
    +                  method: "turn/started",
    +                  params: {
    +                    threadId: nativeThreadId,
    +                    turn: makeCodexReplayTurn({ id: resumedTurnId, status: "inProgress" }),
    +                  },
    +                },
    +              },
    +              {
    +                type: "emit_inbound",
    +                label: "resumed goal is interrupted",
    +                frame: {
    +                  method: "turn/completed",
    +                  params: {
    +                    threadId: nativeThreadId,
    +                    turn: makeCodexReplayTurn({ id: resumedTurnId, status: "interrupted" }),
    +                  },
    +                },
    +              },
    +              startResult === "accepted"
    +                ? { type: "emit_inbound", label: "compact accepted", frame: { id: 3, result: {} } }
    +                : {
    +                    type: "emit_inbound",
    +                    label: "compact request reports the interruption",
    +                    frame: {
    +                      id: 3,
    +                      error: {
    +                        code: -32000,
    +                        message: "Context compaction ended with interrupted.",
    +                      },
    +                    },
    +                  },
    +              {
    +                type: "emit_inbound",
    +                label: "compaction starts",
    +                frame: {
    +                  method: "turn/started",
    +                  params: {
    +                    threadId: nativeThreadId,
    +                    turn: makeCodexReplayTurn({ id: compactTurnId, status: "inProgress" }),
    +                  },
    +                },
    +              },
    +              ...(["item/started", "item/completed"] as const).map((method) => ({
    +                type: "emit_inbound" as const,
    +                label: method,
    +                frame: {
    +                  method,
    +                  params: { threadId: nativeThreadId, turnId: compactTurnId, item },
    +                },
    +              })),
    +              {
    +                type: "emit_inbound",
    +                label: "compaction completes",
    +                frame: {
    +                  method: "turn/completed",
    +                  params: {
    +                    threadId: nativeThreadId,
    +                    turn: makeCodexReplayTurn({ id: compactTurnId, status: "completed" }),
    +                  },
    +                },
    +              },
    +            ],
    +          });
    +          const harness = yield* makeCodexReplayHarness(transcript);
    +          assert.isDefined(harness.runtime.compactThread);
    +          yield* harness.runtime.compactThread!(
    +            makeCodexTestTurnInput({
    +              threadId: harness.threadId,
    +              providerThread: harness.providerThread,
    +              now: yield* DateTime.now,
    +              attemptId: RunAttemptId.make("compact-race-attempt"),
    +              text: "/compact",
    +            }),
    +          );
    +          yield* harness.firstTerminal;
    +
    +          const compactTurns = harness.events.flatMap((event) =>
    +            event.type === "provider_turn.updated" &&
    +            event.providerTurn.nativeTurnRef?.nativeId === compactTurnId
    +              ? [event.providerTurn]
    +              : [],
    +          );
    +          const items = harness.events.flatMap((event) =>
    +            event.type === "turn_item.updated" && event.turnItem.type === "compaction"
    +              ? [event.turnItem]
    +              : [],
    +          );
    +          assert.isNotEmpty(compactTurns);
    +          assert.isTrue(
    +            harness.events.every(
    +              (event) =>
    +                event.type !== "provider_turn.updated" ||
    +                event.providerTurn.nativeTurnRef?.nativeId !== resumedTurnId,
    +            ),
    +            "the unrelated resumed turn must not be adopted by the compaction run",
    +          );
    +          assert.deepEqual(
    +            items.map((entry) => entry.status),
    +            ["running", "completed"],
    +          );
    +          assert.equal(harness.terminalEvents()[0]?.status, "completed");
    +        }).pipe(
    +          Effect.provide(
    +            Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer),
    +          ),
    +        ),
    +      ),
    +  );
    +
       it.effect("resumes a provider thread without requesting or decoding its history", () =>
         Effect.scoped(
           Effect.gen(function* () {
    Sanitized native and T3 protocol evidence, controls, and persisted run statuses
    {
      "date": "2026-10-22",
      "platform": "macOS arm64",
      "method": "Real Codex app-server processes, private state, loopback mock Responses endpoint; no scripted native turn events or RPC errors. Unmodified installed T3 server exercised through normal WebSocket RPC.",
      "native_trials": {
        "0.162.1": {
          "active_goal_trials": 8,
          "active_goal_window_reproductions": 8,
          "paused_goal_trials": 4,
          "paused_goal_window_reproductions": 0,
          "native_compactions_completed": 12
        },
        "0.163.0": {
          "active_goal_trials": 8,
          "active_goal_window_reproductions": 8,
          "paused_goal_trials": 4,
          "paused_goal_window_reproductions": 0,
          "native_compactions_completed": 12
        }
      },
      "t3_server": {
        "application_version": "0.1.0 Beta",
        "asar_sha256": "ed8c4ef3e1c8b8531d05dbf3d12a27f5e4a715105e2eaaac2e910f739a21128d",
        "failure_codex_version": "0.163.0",
        "cold_active_run_ordinal": 3,
        "cold_active_result": "interrupted; goal turn adopted as compact; real compaction subsequently interrupted by T3",
        "warm_paused_run_ordinal": 4,
        "warm_paused_result": "completed",
        "additional_01621_cold_run_ordinal": 5,
        "additional_01621_result": "Missed timing window: goal turn started 3ms before compact RPC; correct native compaction owner; completed after releasing the mock. The test observer timed out while the local mock was intentionally held.",
        "native_events": [
          {
            "case": "cold-active",
            "timestamp": "2026-10-22T19:44:20.222Z",
            "direction": "outgoing",
            "method": "thread/resume",
            "response_to": null,
            "accepted": null,
            "native_turn_id": null,
            "status": null,
            "item_type": null
          },
          {
            "case": "cold-active",
            "timestamp": "2026-10-22T19:44:20.261Z",
            "direction": "outgoing",
            "method": "thread/compact/start",
            "response_to": null,
            "accepted": null,
            "native_turn_id": null,
            "status": null,
            "item_type": null
          },
          {
            "case": "cold-active",
            "timestamp": "2026-10-22T19:44:20.284Z",
            "direction": "incoming",
            "method": "turn/started",
            "response_to": null,
            "accepted": null,
            "native_turn_id": "01a16524-9b12-7cc0-a37a-2f0951b26b59",
            "status": "inProgress",
            "item_type": null
          },
          {
            "case": "cold-active",
            "timestamp": "2026-10-22T19:44:20.285Z",
            "direction": "incoming",
            "method": null,
            "response_to": "thread/compact/start",
            "accepted": true,
            "native_turn_id": null,
            "status": null,
            "item_type": null
          },
          {
            "case": "cold-active",
            "timestamp": "2026-10-22T19:44:20.285Z",
            "direction": "incoming",
            "method": "turn/completed",
            "response_to": null,
            "accepted": null,
            "native_turn_id": "01a16524-9b12-7cc0-a37a-2f0951b26b59",
            "status": "interrupted",
            "item_type": null
          },
          {
            "case": "cold-active",
            "timestamp": "2026-10-22T19:44:20.286Z",
            "direction": "incoming",
            "method": "turn/started",
            "response_to": null,
            "accepted": null,
            "native_turn_id": "01a16524-9b26-7e62-acdc-24ed2a1284df",
            "status": "inProgress",
            "item_type": null
          },
          {
            "case": "cold-active",
            "timestamp": "2026-10-22T19:44:20.287Z",
            "direction": "incoming",
            "method": "item/started",
            "response_to": null,
            "accepted": null,
            "native_turn_id": "01a16524-9b26-7e62-acdc-24ed2a1284df",
            "status": null,
            "item_type": "contextCompaction"
          },
          {
            "case": "cold-active",
            "timestamp": "2026-10-22T19:44:20.290Z",
            "direction": "outgoing",
            "method": "thread/goal/set",
            "response_to": null,
            "accepted": null,
            "native_turn_id": null,
            "status": "paused",
            "item_type": null
          },
          {
            "case": "cold-active",
            "timestamp": "2026-10-22T19:44:20.335Z",
            "direction": "outgoing",
            "method": "turn/interrupt",
            "response_to": null,
            "accepted": null,
            "native_turn_id": "01a16524-9b26-7e62-acdc-24ed2a1284df",
            "status": null,
            "item_type": null
          },
          {
            "case": "cold-active",
            "timestamp": "2026-10-22T19:44:20.439Z",
            "direction": "incoming",
            "method": "turn/completed",
            "response_to": null,
            "accepted": null,
            "native_turn_id": "01a16524-9b26-7e62-acdc-24ed2a1284df",
            "status": "interrupted",
            "item_type": null
          },
          {
            "case": "warm-paused-control",
            "timestamp": "2026-10-22T19:45:39.127Z",
            "direction": "outgoing",
            "method": "thread/compact/start",
            "response_to": null,
            "accepted": null,
            "native_turn_id": null,
            "status": null,
            "item_type": null
          },
          {
            "case": "warm-paused-control",
            "timestamp": "2026-10-22T19:45:39.129Z",
            "direction": "incoming",
            "method": null,
            "response_to": "thread/compact/start",
            "accepted": true,
            "native_turn_id": null,
            "status": null,
            "item_type": null
          },
          {
            "case": "warm-paused-control",
            "timestamp": "2026-10-22T19:45:39.133Z",
            "direction": "incoming",
            "method": "turn/started",
            "response_to": null,
            "accepted": null,
            "native_turn_id": "01a16525-cf38-73c3-b75f-9a1c22c5256a",
            "status": "inProgress",
            "item_type": null
          },
          {
            "case": "warm-paused-control",
            "timestamp": "2026-10-22T19:45:39.136Z",
            "direction": "incoming",
            "method": "item/started",
            "response_to": null,
            "accepted": null,
            "native_turn_id": "01a16525-cf38-73c3-b75f-9a1c22c5256a",
            "status": null,
            "item_type": "contextCompaction"
          },
          {
            "case": "warm-paused-control",
            "timestamp": "2026-10-22T19:45:39.147Z",
            "direction": "incoming",
            "method": "item/completed",
            "response_to": null,
            "accepted": null,
            "native_turn_id": "01a16525-cf38-73c3-b75f-9a1c22c5256a",
            "status": null,
            "item_type": "contextCompaction"
          },
          {
            "case": "warm-paused-control",
            "timestamp": "2026-10-22T19:45:39.148Z",
            "direction": "incoming",
            "method": "turn/completed",
            "response_to": null,
            "accepted": null,
            "native_turn_id": "01a16525-cf38-73c3-b75f-9a1c22c5256a",
            "status": "completed",
            "item_type": null
          },
          {
            "case": "cold-active-old-cli",
            "timestamp": "2026-10-22T19:47:10.317Z",
            "direction": "outgoing",
            "method": "thread/resume",
            "response_to": null,
            "accepted": null,
            "native_turn_id": null,
            "status": null,
            "item_type": null
          },
          {
            "case": "cold-active-old-cli",
            "timestamp": "2026-10-22T19:47:10.408Z",
            "direction": "incoming",
            "method": "turn/started",
            "response_to": null,
            "accepted": null,
            "native_turn_id": "01a16527-33bb-7f80-abda-0662bf0d90ec",
            "status": "inProgress",
            "item_type": null
          },
          {
            "case": "cold-active-old-cli",
            "timestamp": "2026-10-22T19:47:10.411Z",
            "direction": "outgoing",
            "method": "thread/compact/start",
            "response_to": null,
            "accepted": null,
            "native_turn_id": null,
            "status": null,
            "item_type": null
          },
          {
            "case": "cold-active-old-cli",
            "timestamp": "2026-10-22T19:47:10.413Z",
            "direction": "incoming",
            "method": null,
            "response_to": "thread/compact/start",
            "accepted": true,
            "native_turn_id": null,
            "status": null,
            "item_type": null
          },
          {
            "case": "cold-active-old-cli",
            "timestamp": "2026-10-22T19:47:10.417Z",
            "direction": "incoming",
            "method": "turn/completed",
            "response_to": null,
            "accepted": null,
            "native_turn_id": "01a16527-33bb-7f80-abda-0662bf0d90ec",
            "status": "interrupted",
            "item_type": null
          },
          {
            "case": "cold-active-old-cli",
            "timestamp": "2026-10-22T19:47:10.422Z",
            "direction": "incoming",
            "method": "turn/started",
            "response_to": null,
            "accepted": null,
            "native_turn_id": "01a16527-33cd-75d1-b4cc-457d7b0cb7a2",
            "status": "inProgress",
            "item_type": null
          },
          {
            "case": "cold-active-old-cli",
            "timestamp": "2026-10-22T19:47:10.426Z",
            "direction": "incoming",
            "method": "item/started",
            "response_to": null,
            "accepted": null,
            "native_turn_id": "01a16527-33cd-75d1-b4cc-457d7b0cb7a2",
            "status": null,
            "item_type": "contextCompaction"
          }
        ],
        "orchestration_events": {
          "cold-active": [
            {
              "timestamp": "2026-10-22T19:44:20.247Z",
              "sequence": 69,
              "type": "run.updated",
              "run_id": "run:thread:6361fdf9-3d5a-448f-a253-d10add45022e:ordinal:3",
              "id": "run:thread:6361fdf9-3d5a-448f-a253-d10add45022e:ordinal:3",
              "status": "running",
              "native_turn_id": null,
              "provider_turn_id": null,
              "item_type": null
            },
            {
              "timestamp": "2026-10-22T19:44:20.288Z",
              "sequence": 72,
              "type": "provider-turn.updated",
              "run_id": "run:thread:6361fdf9-3d5a-448f-a253-d10add45022e:ordinal:3",
              "id": "provider-turn:provider:codex:native-turn:01a16524-9b12-7cc0-a37a-2f0951b26b59",
              "status": "running",
              "native_turn_id": "01a16524-9b12-7cc0-a37a-2f0951b26b59",
              "provider_turn_id": null,
              "item_type": null
            },
            {
              "timestamp": "2026-10-22T19:44:20.290Z",
              "sequence": 74,
              "type": "provider-turn.updated",
              "run_id": "run:thread:6361fdf9-3d5a-448f-a253-d10add45022e:ordinal:3",
              "id": "provider-turn:provider:codex:native-turn:01a16524-9b12-7cc0-a37a-2f0951b26b59",
              "status": "interrupted",
              "native_turn_id": "01a16524-9b12-7cc0-a37a-2f0951b26b59",
              "provider_turn_id": null,
              "item_type": null
            },
            {
              "timestamp": "2026-10-22T19:44:20.291Z",
              "sequence": 77,
              "type": "run.updated",
              "run_id": "run:thread:6361fdf9-3d5a-448f-a253-d10add45022e:ordinal:3",
              "id": "run:thread:6361fdf9-3d5a-448f-a253-d10add45022e:ordinal:3",
              "status": "interrupted",
              "native_turn_id": null,
              "provider_turn_id": null,
              "item_type": null
            }
          ],
          "warm-paused-control": [
            {
              "timestamp": "2026-10-22T19:45:39.118Z",
              "sequence": 94,
              "type": "run.updated",
              "run_id": "run:thread:6361fdf9-3d5a-448f-a253-d10add45022e:ordinal:4",
              "id": "run:thread:6361fdf9-3d5a-448f-a253-d10add45022e:ordinal:4",
              "status": "running",
              "native_turn_id": null,
              "provider_turn_id": null,
              "item_type": null
            },
            {
              "timestamp": "2026-10-22T19:45:39.134Z",
              "sequence": 97,
              "type": "provider-turn.updated",
              "run_id": "run:thread:6361fdf9-3d5a-448f-a253-d10add45022e:ordinal:4",
              "id": "provider-turn:provider:codex:native-turn:01a16525-cf38-73c3-b75f-9a1c22c5256a",
              "status": "running",
              "native_turn_id": "01a16525-cf38-73c3-b75f-9a1c22c5256a",
              "provider_turn_id": null,
              "item_type": null
            },
            {
              "timestamp": "2026-10-22T19:45:39.137Z",
              "sequence": 99,
              "type": "turn-item.updated",
              "run_id": "run:thread:6361fdf9-3d5a-448f-a253-d10add45022e:ordinal:4",
              "id": "turn-item:provider:codex:native-item:01a16525-cf3d-7d62-b8f3-b68a929909c9",
              "status": "running",
              "native_turn_id": null,
              "provider_turn_id": "provider-turn:provider:codex:native-turn:01a16525-cf38-73c3-b75f-9a1c22c5256a",
              "item_type": "compaction"
            },
            {
              "timestamp": "2026-10-22T19:45:39.138Z",
              "sequence": 100,
              "type": "provider-turn.updated",
              "run_id": "run:thread:6361fdf9-3d5a-448f-a253-d10add45022e:ordinal:4",
              "id": "provider-turn:provider:codex:native-turn:01a16525-cf38-73c3-b75f-9a1c22c5256a",
              "status": "running",
              "native_turn_id": "01a16525-cf38-73c3-b75f-9a1c22c5256a",
              "provider_turn_id": null,
              "item_type": null
            },
            {
              "timestamp": "2026-10-22T19:45:39.141Z",
              "sequence": 101,
              "type": "provider-turn.updated",
              "run_id": "run:thread:6361fdf9-3d5a-448f-a253-d10add45022e:ordinal:4",
              "id": "provider-turn:provider:codex:native-turn:01a16525-cf38-73c3-b75f-9a1c22c5256a",
              "status": "running",
              "native_turn_id": "01a16525-cf38-73c3-b75f-9a1c22c5256a",
              "provider_turn_id": null,
              "item_type": null
            },
            {
              "timestamp": "2026-10-22T19:45:39.145Z",
              "sequence": 102,
              "type": "provider-turn.updated",
              "run_id": "run:thread:6361fdf9-3d5a-448f-a253-d10add45022e:ordinal:4",
              "id": "provider-turn:provider:codex:native-turn:01a16525-cf38-73c3-b75f-9a1c22c5256a",
              "status": "running",
              "native_turn_id": "01a16525-cf38-73c3-b75f-9a1c22c5256a",
              "provider_turn_id": null,
              "item_type": null
            },
            {
              "timestamp": "2026-10-22T19:45:39.148Z",
              "sequence": 103,
              "type": "turn-item.updated",
              "run_id": "run:thread:6361fdf9-3d5a-448f-a253-d10add45022e:ordinal:4",
              "id": "turn-item:provider:codex:native-item:01a16525-cf3d-7d62-b8f3-b68a929909c9",
              "status": "completed",
              "native_turn_id": null,
              "provider_turn_id": "provider-turn:provider:codex:native-turn:01a16525-cf38-73c3-b75f-9a1c22c5256a",
              "item_type": "compaction"
            },
            {
              "timestamp": "2026-10-22T19:45:39.149Z",
              "sequence": 104,
              "type": "provider-turn.updated",
              "run_id": "run:thread:6361fdf9-3d5a-448f-a253-d10add45022e:ordinal:4",
              "id": "provider-turn:provider:codex:native-turn:01a16525-cf38-73c3-b75f-9a1c22c5256a",
              "status": "completed",
              "native_turn_id": "01a16525-cf38-73c3-b75f-9a1c22c5256a",
              "provider_turn_id": null,
              "item_type": null
            },
            {
              "timestamp": "2026-10-22T19:45:39.150Z",
              "sequence": 106,
              "type": "run.updated",
              "run_id": "run:thread:6361fdf9-3d5a-448f-a253-d10add45022e:ordinal:4",
              "id": "run:thread:6361fdf9-3d5a-448f-a253-d10add45022e:ordinal:4",
              "status": "waiting",
              "native_turn_id": null,
              "provider_turn_id": null,
              "item_type": null
            },
            {
              "timestamp": "2026-10-22T19:45:39.152Z",
              "sequence": 113,
              "type": "run.updated",
              "run_id": "run:thread:6361fdf9-3d5a-448f-a253-d10add45022e:ordinal:4",
              "id": "run:thread:6361fdf9-3d5a-448f-a253-d10add45022e:ordinal:4",
              "status": "completed",
              "native_turn_id": null,
              "provider_turn_id": null,
              "item_type": null
            }
          ],
          "cold-active-old-cli": [
            {
              "timestamp": "2026-10-22T19:47:10.400Z",
              "sequence": 126,
              "type": "run.updated",
              "run_id": "run:thread:6361fdf9-3d5a-448f-a253-d10add45022e:ordinal:5",
              "id": "run:thread:6361fdf9-3d5a-448f-a253-d10add45022e:ordinal:5",
              "status": "running",
              "native_turn_id": null,
              "provider_turn_id": null,
              "item_type": null
            },
            {
              "timestamp": "2026-10-22T19:47:10.423Z",
              "sequence": 129,
              "type": "provider-turn.updated",
              "run_id": "run:thread:6361fdf9-3d5a-448f-a253-d10add45022e:ordinal:5",
              "id": "provider-turn:provider:codex:native-turn:01a16527-33cd-75d1-b4cc-457d7b0cb7a2",
              "status": "running",
              "native_turn_id": "01a16527-33cd-75d1-b4cc-457d7b0cb7a2",
              "provider_turn_id": null,
              "item_type": null
            },
            {
              "timestamp": "2026-10-22T19:47:10.427Z",
              "sequence": 131,
              "type": "turn-item.updated",
              "run_id": "run:thread:6361fdf9-3d5a-448f-a253-d10add45022e:ordinal:5",
              "id": "turn-item:provider:codex:native-item:01a16527-33d8-7780-8179-fd2e62c3152a",
              "status": "running",
              "native_turn_id": null,
              "provider_turn_id": "provider-turn:provider:codex:native-turn:01a16527-33cd-75d1-b4cc-457d7b0cb7a2",
              "item_type": "compaction"
            }
          ]
        },
        "final_persisted_run_statuses": [
          {
            "ordinal": 1,
            "status": "failed",
            "requested_at": "2026-10-22T19:38:54.013Z",
            "completed_at": "2026-10-22T19:38:54.069Z"
          },
          {
            "ordinal": 2,
            "status": "completed",
            "requested_at": "2026-10-22T19:39:56.035Z",
            "completed_at": "2026-10-22T19:39:56.466Z"
          },
          {
            "ordinal": 3,
            "status": "interrupted",
            "requested_at": "2026-10-22T19:44:20.161Z",
            "completed_at": "2026-10-22T19:44:20.291Z"
          },
          {
            "ordinal": 4,
            "status": "completed",
            "requested_at": "2026-10-22T19:45:39.113Z",
            "completed_at": "2026-10-22T19:45:39.152Z"
          },
          {
            "ordinal": 5,
            "status": "completed",
            "requested_at": "2026-10-22T19:47:10.285Z",
            "completed_at": "2026-10-22T19:48:06.497Z"
          }
        ]
      },
      "historical_cli_asset": {
        "url": "https://github.com/openai/codex/releases/download/rust-v0.162.1/codex-aarch64-apple-darwin.tar.gz",
        "verified_archive_sha256": "b293a0fcdd871900a44c0a4f40a1aa401b31e02854f790ebdb8bd5d1177fd1fe"
      },
      "limits": [
        "Exact October12 Alpha error text was not reproduced; current T3 run interruption and active cancellation were reproduced.",
        "Full T3 failure was observed once with0.163.0, with a successful paused-goal control; the additional0.162.1 T3 attempt missed the window.",
        "Native24-trial counts apply to the supplied local mock and request timing, not all UI reconnects.",
        "Model responses were local mocks; the Codex/T3 processes and native protocol event generation were real.",
        "T3 browser history was initialized in the real web app, but final failure was triggered and observed over its server RPC; browser snapshot timeouts prevented a useful screenshot of that final failure."
      ]
    }

    Reproduction and investigation: GPT-6.1 Sol through the Codex harness in T3 Code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    acceptedfeature request acceptedbugSomething is broken or behaving incorrectly.via-triageFiled through npx t3 triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions