Skip to content

Conversation

@ThomasK33
Copy link
Member

🤖 Fix post-compaction context sidebar staleness after plan writes.

  • Backend now emits refreshed workspace metadata (including postCompaction state) after relevant tool completions (file_edit_*, propose_plan) when the experiment is enabled.
  • Refresh is debounced in WorkspaceService to coalesce rapid tool sequences.

Validation:

  • make static-check
  • Added unit tests for the tool-call-end trigger and the debounce behavior.

📋 Implementation Plan

Fix: Post-compaction context UI not updating when plan is written

Summary

When the Post-Compaction Context experiment is enabled, the right-sidebar section (Costs tab) should show a “Plan file” item as soon as the agent writes the plan file. Currently it doesn’t update until the user toggles the experiment off/on.

Repro (current behavior)

  1. Enable Settings → Experiments → Post-Compaction Context.
  2. In a workspace, have the agent write a plan (uses file_edit_* tools to create/update the plan file).
  3. Observe: the right-sidebar Post-Compaction Context section does not appear / does not show “Plan file”.
  4. Toggle the experiment off then on.
  5. Observe: the section updates and now shows the “Plan file” item.

Root cause (why this happens)

Frontend data source is stale.

  • UI path:

    • CostsTab.tsx renders <PostCompactionSection … /> only when postCompactionEnabled.
    • usePostCompactionState(workspaceId) reads workspaceMetadata.get(workspaceId)?.postCompaction from WorkspaceContext.
    • If planPath is null, the section won’t render (it early-returns when there’s no plan and no tracked files).
  • Where does workspaceMetadata[*].postCompaction come from?

    • It is only included when WorkspaceContext.loadWorkspaceMetadata() calls api.workspace.list({ includePostCompaction: true }).
    • That list call is triggered on mount and when the user toggles the experiment (see ExperimentsSection.tsx calling refreshWorkspaceMetadata()).
  • Critically:

    • Writing the plan uses file_edit_* tools → creates history entries and writes the plan file on disk.
    • No code path automatically refreshes workspace metadata (or postCompaction state) after a file-edit tool finishes.
    • Backend only emits metadata enriched with postCompaction in one place today: after compaction completes (onCompactionComplete callback wired in WorkspaceService.getOrCreateSession).

So the UI is correct given its inputs; it’s just never told to recompute/reload post-compaction state when plan/file edits happen.

Recommended fix (Approach A): backend-driven metadata refresh on tool completion

Goal: after a file_edit_* tool finishes (and after propose_plan), emit an updated workspace metadata event that includes fresh postCompaction state.

This keeps the UI model simple: WorkspaceContext already subscribes to metadata events and will update workspaceMetadata, which will flow into usePostCompactionState and re-render the right sidebar.

Implementation plan

1) Add a new “post-compaction state may have changed” callback to AgentSession

  • Extend AgentSession constructor args to include something like:
    • onPostCompactionStateChange?: () => void
  • Store it on the session.

2) Trigger that callback on relevant tool completions

In AgentSession.attachAiListeners():

  • In the "tool-call-end" handler, after emitChatEvent(payload):
    • If tool name is one of:
      • file_edit_insert
      • file_edit_replace_string
      • (optionally) propose_plan (since it can create/validate plan state)
    • Then call this.onPostCompactionStateChange?.().

Optional gating: only do this if the session has seen options?.experiments?.postCompactionContext === true recently.

  • Store this.postCompactionExperimentEnabled from the latest sendMessage options.
  • Gate callback invocation behind it.

3) Implement the callback in WorkspaceService (debounced + safe)

In WorkspaceService.getOrCreateSession() pass:

  • onPostCompactionStateChange: () => schedulePostCompactionMetadataRefresh(workspaceId)

Implement schedulePostCompactionMetadataRefresh in WorkspaceService:

  • Coalesce bursts (multiple file edits) with a short debounce (e.g. 100–250ms).
  • On fire:
    • const metadata = await this.getInfo(workspaceId)
    • If present, compute const postCompaction = await this.getPostCompactionState(workspaceId)
    • this.sessions.get(workspaceId)?.emitMetadata({ ...metadata, postCompaction })
  • Wrap in try/catch; treat runtime-unreachable as “don’t crash; skip emitting postCompaction”.

4) Ensure metadata updates don’t accidentally drop postCompaction

  • Today, some metadata events may not include postCompaction.
  • With this approach, we explicitly re-emit enriched metadata after file edits.
  • (Optional follow-up) When experiment is enabled, also enrich other metadata emits (create/title update/etc.) to avoid losing the field.

Validation

  • Manual:
    • Enable experiment.
    • Write plan.
    • Confirm Post-Compaction Context section appears immediately (no experiment toggle needed).
    • Edit plan again; confirm it remains visible.
    • Edit a normal file (exec mode) and ensure tracked files list updates as expected.
  • Regression:
    • With experiment disabled, ensure no extra metadata spam and section stays hidden.

Tests

  • Add a unit test around the new trigger behavior:
    • Simulate an AgentSession receiving a tool-call-end event for file_edit_*.
    • Assert onPostCompactionStateChange callback is invoked (and debounced once for bursts).
  • Add a WorkspaceService unit test for debounced refresh:
    • Stub getInfo() and getPostCompactionState().
    • Call scheduler multiple times; assert a single session.emitMetadata() with enriched metadata.

Net LoC estimate (product code)

  • ~80–160 LoC
    • AgentSession: add callback field + invoke on tool-call-end.
    • WorkspaceService: debounce + enriched metadata emit.

Alternative fix (Approach B): frontend-driven fetch of post-compaction state

Instead of relying on workspaceMetadata.postCompaction, update usePostCompactionState to call api.workspace.getPostCompactionState({ workspaceId }):

  • On mount / workspace change.
  • On tool-call-end events (would require subscribing to WorkspaceStore or a custom event).

Pros

  • Uses existing API (getPostCompactionState) without expanding backend event behavior.

Cons

  • Needs a reliable “tool-call-end happened” signal on the frontend.
  • More moving parts in UI (event wiring, polling, or store integration).

Net LoC estimate (product code)

  • ~120–220 LoC
    • Hook changes + new event/listener plumbing.

Decision

Proceed with Approach A (backend-driven metadata refresh):

  • It matches existing architecture (metadata subscription already exists).
  • It fixes the immediate bug (plan write doesn’t update sidebar) without adding UI polling.
  • It can be debounced centrally and is straightforward to reason about.

Generated with mux

Change-Id: I7fa5e5c4f22cbd7bfa57dc0b65c4810293717857
Signed-off-by: Thomas Kosiewski <tk@coder.com>
Change-Id: I0475383e5384c2f7e8968239e43bdaf9165edb86
Signed-off-by: Thomas Kosiewski <tk@coder.com>
Copy link

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

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.

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Change-Id: Id054d125f88bfb4c2573e5374a226e6cfc989cc4
Signed-off-by: Thomas Kosiewski <tk@coder.com>
@ThomasK33
Copy link
Member Author

@codex review

@chatgpt-codex-connector
Copy link

Codex Review: Didn't find any major issues. Nice work!

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Change-Id: I738acdd7101ccb04c6053b03693df4fb39c02769
Signed-off-by: Thomas Kosiewski <tk@coder.com>
@ThomasK33
Copy link
Member Author

@codex review

@chatgpt-codex-connector
Copy link

Codex Review: Didn't find any major issues. Keep them coming!

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@chatgpt-codex-connector
Copy link

Codex Review: Didn't find any major issues. Another round soon, please!

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant