Skip to content

fix: stop native Codex compaction on cancellation - #618

Open
wibus-wee wants to merge 9 commits into
mainfrom
fix/stale-context-compaction-reconciliation
Open

fix: stop native Codex compaction on cancellation#618
wibus-wee wants to merge 9 commits into
mainfrom
fix/stale-context-compaction-reconciliation

Conversation

@wibus-wee

@wibus-wee wibus-wee commented Sep 11, 2026

Copy link
Copy Markdown
Member

Related issue

Follow-up to #573.

Problem / pressure

Stop during manual Codex /compact could return an ACP cancellation while native compression kept running. The adapter treated the empty start acknowledgement as evidence that compaction had no interruptible turn. Codex 0.153.4 actually emits the ordinary native turn lifecycle.

Summary

Update the Codex adapter to capture the compaction turn, interrupt it on Stop, and retain prompt ownership until native completion or connection closure. The existing CLI finalization then persists unresolved compaction as failed before releasing execution ownership.

Adapter implementation: LodyAI/acp-extension-codex#41 (merge dependency). The submodule points to its published commit.

The final PR contains the adapter submodule update, the draft history-write contract, and its owning Agent Note. Session rendering and RPC transports match the base branch; opening a conversation does not repair historical data.

Visual explanation

sequenceDiagram
  participant Host as Lody
  participant ACP as Codex adapter
  participant Native as Codex app-server
  Host->>ACP: prompt /compact
  ACP->>Native: thread/compact/start
  Native-->>ACP: turn/started
  Host->>ACP: Stop
  ACP->>Native: turn/interrupt
  Native-->>ACP: turn/completed (interrupted)
  ACP-->>Host: prompt cancelled
  Host->>Host: persist terminal history, release execution
Loading

Before / after

Before After
Manual compaction could keep running after local cancellation. Stop interrupts its native turn and waits for terminal confirmation.
Compaction process exit could leave a pending ACP prompt. Connection closure rejects the pending command.

Test plan

  • Adapter typecheck and 616 tests pass; 27 existing environment-gated tests are skipped.
  • Real Codex 0.153.4 smoke passes for normal compaction and Stop: native interrupted precedes ACP cancelled.
  • Root typechecks, lint, formatting, i18n, docs and code-collab/platform/public boundary checks pass.
  • Full pnpm check passes all preceding workspace suites, then Electron reports 103/104 passing: the relay suite cannot import Electron because the local binary is not installed.

Context handoff

Instructions for reviewing agents

  • Review focus: Inspect native turn capture in the adapter's CodexAppServerClient.ts and cancellation ownership in CodexAcpServer.ts.
  • Decisions to challenge: A cancel before native start must interrupt the arriving turn without admitting another prompt; item completion alone cannot release ownership.
  • Plausible failures / evidence gaps: Native smoke used bundled Codex 0.153.4; arbitrary CODEX_PATH versions are not validated.

Authoring context

  • User goal / directives: Correct the compaction cancellation boundary and remove the expanding historical repair mechanism.
  • Constraints / non-goals: Keep existing CLI terminal settlement; do not migrate historical records or add renderer-driven history mutations.
  • Risk-bearing decisions: Native terminal confirmation governs prompt release; pending start and connection-exit races are covered explicitly.
  • Destructive or irreversible behavior: No data migration or bulk rewrite; ordinary terminal history persistence remains authoritative.
  • Deliberately not done or tested: Legacy stale-history cleanup and documentation translations are deferred; the local Electron relay test is environment-blocked.
  • Unknowns / confidence: Deterministic lifecycle tests and real native interruption agree on the cancellation boundary.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 11, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-12T08:16:06.456648Z f814ade New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8a48169ada

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +2725 to +2728
.then((result) => {
if (!result || result.outcome === 'unknown') {
compactionReconciliationAttemptsRef.current.delete(attemptKey);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Retry reconciliation after the active turn disappears

When reconciliation returns active and that daemon subsequently exits before persisting the compaction's terminal update, this attempt key remains in the set permanently. The offline/online transition reruns the effect, but line 2717 suppresses the request to the replacement daemon, leaving the durable pending/in_progress item and its spinner stuck until the component is unmounted and reopened. Clear or invalidate active attempts when live ownership changes so restart recovery can actually reconcile the stale turn.

AGENTS.md reference: AGENTS.md:L82-L87

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a357b744e4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

'machine/acp-capabilities-refresh-cancel',
'session/cancel',
'session/live-status',
'session/reconcile-context-compaction',

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep reconciliation off the fast control lane

When four stale sessions are mounted together, their reconciliation requests can occupy every slot in the four-request control pool: unlike the other fast handlers, each one awaits document acquisition, a history write, and waitUntilSynced() before returning. That queues session/cancel, session/dispatch-turn, status, and lifecycle requests behind potentially multi-second synchronization; because expiry is checked only after acquiring the lane, those user-control requests can time out or be discarded. Run this repair on the ordinary lane or acknowledge it before performing the durable work.

AGENTS.md reference: packages/loro-streams-rpc/AGENTS.md:L48-L55

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7e2680aedd

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/cli/src/lib/message-handler.ts Outdated
return response('unknown', { error: 'Session history is not synced with its owner.' });
}

const releaseBarrier = this.executionService.tryAcquireSessionRewriteBarrier(args.sessionId);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Requeue goal turns after releasing the rewrite barrier

When a set or resume goal request arrives after this barrier is acquired but before it is released, queueGoalTurn immediately tries continueSession; runVisibleSessionTurn returns rewrite-barrier, leaving claimed false, and lines 1304–1317 of session-execution-service.ts then delete the accepted pending goal and record a pre-prompt failure. The finally block only wakes SessionDispatchWatcher, which does not own pendingGoalTurnBySession, so the promised Lody-owned goal prompt never runs; explicitly retry or wake queued goal work when reconciliation releases the barrier.

AGENTS.md reference: packages/shared/AGENTS.md:L78-L83

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7fe6689323

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +2739 to +2742
.then((result) => {
if (!isDurableContextCompactionReconciliation(result)) {
compactionReconciliationAttemptsRef.current.delete(attemptKey);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Retry active results when the same daemon releases the turn

When this request returns active during the interval between host finalization and provider termination, deleting the attempt key does not itself rerender the component or rerun this effect. If the same daemon subsequently releases the turn without persisting a terminal compaction update, neither history, connectivity, nor the machine instance ID changes, so the stale spinner remains indefinitely. The new owner-instance key only fixes daemon replacement; fresh evidence in this revision is that session live-presence/ownership state is still absent from the effect dependencies. Trigger a retry when same-daemon session activity ends.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5faa4d9f27

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/cli/src/lib/message-handler.ts Outdated
return response('retry', { error: 'The daemon still has live Session work.' });
}

if (!(await verifyOwnership())) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Recheck live ownership after the awaited metadata read

When a durable activation is recovered while reconciliation is in flight, hasLiveSessionWork() can return false and then this awaited ownership lookup yields while the control-lane dispatch registers the recovered turn. Recovery reuses the deterministic assistant:<userTurnId> entry, so the following targeted update can mark the compaction failed in that now-active same turn. Recheck live work after the final await, or serialize this mutation with turn acquisition, before changing history.

AGENTS.md reference: apps/cli/src/lib/acp/AGENTS.md:L74-L78

Useful? React with 👍 / 👎.

@wibus-wee wibus-wee changed the title fix: reconcile stale context compaction fix: stop native Codex compaction on cancellation Sep 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant