Skip to content

feat(desktop): support manual context compaction in Side Conversations - #4532

Merged
me2seeks merged 6 commits into
apache:mainfrom
testikun:codex/issue-4521-side-compact
Sep 3, 2026
Merged

feat(desktop): support manual context compaction in Side Conversations#4532
me2seeks merged 6 commits into
apache:mainfrom
testikun:codex/issue-4521-side-compact

Conversation

@testikun

@testikun testikun commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Support the exact /compact command in idle, committed Side Conversations.
  • Route compaction to the companion Session without sending /compact to the model or touching the source Session.
  • Keep draft, quote, and attachment state intact when the fork is preparing, running, archived, unavailable, or the Host rejects the operation.

Implementation

  • Added the existing Session-scoped compaction operation to the Workbar Side Conversation port and Desktop adapter.
  • Added command interception before steering/send and reused the existing compaction progress/terminal presentation semantics for the companion panel.
  • Added retry-safe in-flight fencing, exact model/archive checks, and localized workspace-unavailable error handling.
  • Fixed Runtime Host terminal projection so synthesized complete events preserve contextCompactionOutcome, allowing asynchronous Side Conversation compaction to settle and release its lock.

Tests

  • Runtime Host projector regression: 20/20 passing.
  • Desktop Side Conversation focused suite: 32/32 passing.
  • Desktop typecheck: passing.
  • Renderer architecture scanner: passing.
  • ASF header and git diff --check: passing.
  • Full workspace suite under Node 24: first run reached 1,566 passing / 12 skipped with one unrelated owned Host exits promptly after its first connection closes failure; the complete Runtime Host suite rerun passed 1,567 / 12 skipped.

Platform limitations

  • This is a Desktop Workbar feature only; CLI/TUI /compact behavior is unchanged.
  • It uses the existing Runtime Host context-compaction capability, so the operation remains subject to the selected model, workspace availability, and platform support already enforced by that capability.

Fixes #4521

@github-actions github-actions Bot added the effort/L Under 1000 readable lines label Sep 2, 2026
@testikun
testikun force-pushed the codex/issue-4521-side-compact branch 2 times, most recently from 5f3797a to d0515a3 Compare September 2, 2026 05:26

@me2seeks me2seeks left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review

Method: traced the compaction flow end to end — panel → dispatchQuoteCompanionInputuseQuoteCompanion.compact() → the Workbar port → the existing sessions.compact IPC (no new protocol surface) — and followed contextCompactionOutcome through runtime-kernelcanonical-turn-snapshotsession-projectorapplyOwnedEvent for all three terminal states (completed / failed / interrupted). CI test is green on this head; the branch merges cleanly into main.

The projector hunk is the right root-cause fix: the kernel's live complete event already carried the outcome, and only the snapshot-synthesized path dropped it — which is exactly the path an asynchronous Side Conversation compaction is observed through. The new test pins it.

Findings

P2 — The async compaction exclusivity lock leaks when the compaction turn terminates without an outcome. use-quote-companion.ts:289-298 clears compactionRequestInFlightRef only on a complete event carrying contextCompactionOutcome. Two plausible paths never produce that event: the Host restarting mid-compaction classifies the run as interrupted → the projector synthesizes abort; a kernel-level failure of the compaction run itself → error. Neither carries an outcome (canonical-turn-snapshot.ts reads the outcome only for completed runs). When that happens, compactionRequestInFlightRef stays true forever: send() (:797) and compact() (:750) refuse permanently, and the panel only recovers by closing it. The existing tests cover an RPC throw and a finished result, but not a terminal error/abort for the compaction turn. Smallest sound fix: in applyOwnedEvent, also match terminal error/abort events against compactionTurnIdRef.current, clear both refs, and report through onContextCompactionError; add a regression test for "Host interruption settles an async companion compaction and releases the lock".

P3 — Refused /compact is silent. dispatchQuoteCompanionInput intercepts the exact command before the streaming/steer split, and compact() returns false when the fork is running, preparing, or archived — the draft survives (good), but the user gets no signal why nothing happened. A localized toast on the refusal paths would close the loop.

P3 — Narrow double-/compact race. If the first compaction's terminal event arrives before its RPC resolves and a second /compact fires inside that window, the first call's continuation can overwrite compactionTurnIdRef and swallow the second compaction's outcome presentation. Consequence is a missing toast, not stuck state; noting for the record.

Production code that can be deleted

None identified — the feature rides the existing Session-scoped compaction capability and IPC channel; the helpers stay feature-private (d0515a3).

Low-quality tests to delete or replace

None identified. The new tests assert behavior: dispatch order before steering, exclusivity until the terminal event, retry after failure, and rejection without consuming staged quotes.

Review-relevant risks

User-visible behavior change (a new command path in Side Conversations) and an additive field on a synthesized Session event in the Runtime Host projection contract. Per CONTRIBUTING.md, material changes in protected areas require independent human review; this automated review is not an approval. Desktop-only scope is documented; CLI/TUI /compact is unchanged.

Conclusion

  1. The solution is optimal for the actual problem: existing capability, existing channel, root-cause projector fix.
  2. Deletable production code: none identified.
  3. Deletable/replaceable tests: none identified.
  4. Deeper refactor: not required.
  5. Merge readiness: after the P2 lock-leak is fixed (small, localized) or an explicit plan exists; everything else is ready.
  6. Residual risks: compaction availability remains subject to the backend capability checks already enforced by the Host; that is inherited, not introduced.

@testikun
testikun force-pushed the codex/issue-4521-side-compact branch 2 times, most recently from d24c02f to d2d6d58 Compare September 3, 2026 05:45
@testikun

testikun commented Sep 3, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the review. I rebased the PR onto the current apache/main (0f4d20e31) and addressed the compaction review findings on the new head d2d6d58f6.

  • The compaction fence is released for matching terminal abort and non-recoverable error events.
  • Terminal events are now fenced by the exact compaction turnId; if the Host terminal event arrives before the compact RPC response, it is buffered and settled only after the RPC confirms the same identity. An unrelated Turn completion can no longer release the lock or show a false compaction result.
  • Added regression coverage for Host interruption, unrelated Turn completion, and lazy-fork preparation.

The focused Desktop Side Conversation suite passes 38/38, the Runtime Host projector suite passes 20/20, and the PR CI has been restarted on this rebased head. The remaining silent-refusal note is non-blocking and unchanged.

@testikun
testikun force-pushed the codex/issue-4521-side-compact branch from d2d6d58 to 869b3f8 Compare September 3, 2026 07:06
testikun added 6 commits September 3, 2026 15:19
Add exact /compact handling for idle committed Side Conversations, routing compaction to the companion Session and preserving existing feedback and draft ownership.

Generated-by: Codex
@testikun
testikun force-pushed the codex/issue-4521-side-compact branch from 869b3f8 to 967dac9 Compare September 3, 2026 07:21

@me2seeks me2seeks left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Re-reviewed the rebased head (967dac9) against the current main. Verified end to end:

  • P2 lock-leak (previous review) — fixed. applyOwnedEvent now treats terminal abort and non-recoverable error events for the tracked compaction turnId as compaction terminals, releasing the in-flight fence and reporting through onContextCompactionError. Regression test "releases an async companion compaction after a Host interruption" covers it.
  • P3 double-/compact race — fixed. Terminal events are fenced by the exact compaction turnId; a Host terminal arriving before the compact RPC response is buffered in pendingCompactionTerminalRef and settled only after the response proves identity. Covered by "does not settle a pending companion compaction from another turn outcome".
  • Projector root-cause fix is minimal and correct. Snapshot-synthesized complete events now forward contextCompactionOutcome (packages/runtime-host/src/adapter/session-projector.ts), pinned by the new projector test; failed/interrupted turns surface through the pre-existing error/abort branches.
  • No new protocol surface — rides the existing sessions:compact IPC; Workbar port, desktop adapter, fake services, and storybook bridge are consistent.
  • Guards in compact() (mounted / archived / exact model choice / in-flight / submit lock / pending admission / active turn / runningTurnIds) plus the send() interception keep draft, quotes, and attachments intact on every refusal path.

Local verification on this exact diff: runtime-host build clean + session-projector 20/20, desktop quote-companion suite 38/38, workbar-services-adapter 2/2, desktop typecheck clean, renderer architecture scanner clean, git diff --check clean. The earlier CI desktop-release-targets failure was environmental (node:test IPC deserialization against a spawned electron-builder child; passes locally under Node 24 and 26, and the lane is green on this head's CI).

Non-blocking notes, unchanged from the previous round: the companion compaction presentation duplicates the app-shell one deliberately (feature-private boundary), a refused /compact is silent (author acknowledged), and the failed-outcome toast uses the fallback copy rather than the Host reason — consistent with the existing shell semantics.

@me2seeks
me2seeks merged commit 61cbda2 into apache:main Sep 3, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort/L Under 1000 readable lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(desktop): support manual context compaction in Side Conversations

2 participants