Skip to content

Fix Claude context indicator after compaction - #1461

Merged
baron merged 3 commits into
mainfrom
fix/972-claude-context-after-compaction
Oct 23, 2026
Merged

baron merged 3 commits into
mainfrom
fix/972-claude-context-after-compaction

Conversation

@baron

@baron baron commented Oct 23, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Fixes #972.

Claude's compact_boundary reports pre_tokens, not post-compaction occupancy. The old estimator retained that count, its active-turn observation, and historical fallback; UI projections could then substitute the whole transcript's character estimate and label unknown usage Live. Reopening a session could rebuild the same pre-compaction count.

  • Clear invalidated occupancy at the boundary, retain the known window, and display an explicit Pending / Awaiting post-compaction usage state in the context pill and both sidebar surfaces. Do not substitute transcript estimates while pending; unknown rings show ?, not zero, and nil occupancy is not labelled Live.
  • Append a zero-cost contextCompacted usage-ledger fence. The builder never scans across that fence, including normal hydration and reopening from history. Existing usage/billing records remain intact; legacy rows decode with no fence.
  • Carry a dedicated pending flag, reconstructed from the fence and cleared by fresh occupancy. Keep the historical compaction timestamp without using it to suppress later rebuilding.
  • Restrict the existing system-text recognizer to the compaction prefix, so Task notification prose containing the phrase does not invalidate parent usage.
  • Replay realistic NDJSON envelopes (session_id, uuid) through ClaudeSDKNDJSONTranslator and the actual view-model stream handler. Exercise non-empty transcript analytics via update(snapshot:), manual compaction with no post-size, same-turn automatic compaction with fresh usage before result, Task-prose negatives, legacy decoding, hydration, and history reopen.

Relevance and integration overlap

Issue #972 remained open/reproducible on base ab84d47e6; no existing #972 PR was found. Draft #1458 (#1019) is functionally distinct but does have textual merge conflicts in ClaudeContextUsageEstimator.swift, verified with git merge-tree using original heads 3e92c3e and 39355a0. It was not merged into this branch.

Draft #1458 still has a simple adjacent-line conflict in ClaudeContextUsageEstimator.swift; no merge was performed. When integrating #1458, keep this PR’s unconditional marker-aware rebuild and add its configuredContextWindow(for:) capacity fallback and this PR's marker-aware replay/pending flag. This follow-up removes the old canRebuildContext gate because the persisted fence makes rebuilding safe. The integrated builder should receive existingUsage?.modelContextWindow ?? configuredContextWindow(for: session); retain both the pending-state helper and #1458's configured-window helper. Do not resolve the conflict by dropping either behavior.

Validation

All builds/tests used this worktree's conductor daemon; SwiftFormat mutation was limited to changed files.

  • Original fail-first: 1 regression test, 12 expected assertion failures on the old estimator (ticket 4c0e1ee4).
  • Final expanded root selection: make dev-test FILTER='ClaudeCompactionContextUsageTests|ClaudeCompatiblePluginBridgeTests|AgentSessionLinkContextLoadTests|AgentRuntimeSidebarViewModelTests' — 65 tests passed (ticket 323155f3; reconnected to the same ticket after one client timeout).
  • make dev-swift-build PRODUCT=RepoPrompt: passed (ticket fb170bda).
  • make dev-provider-test: all 20 tests passed (ticket 384691ff).
  • Changed-files-only SwiftFormat 0.61.1; make dev-lint: passed (ticket 034425bd).
  • Follow-up static Oracle review: no material findings.
  • Commit and push preflights: passed, including redacted secret scans and repository guardrails.

An intermediate expanded run compiled successfully and passed 63/65 tests; the two failures correctly exposed an empty test fixture, fixed by saving an explicit non-empty canonical transcript. The corrected run above includes both hydration paths. The initial cold-build attempt before the first implementation hit XCBuild input/database I/O errors; the coordinated retry supplied the fail-first evidence.

Not run: full root suite, pr-ready, packaging/release validation, or live UI/provider/MCP smoke. Live smoke is out of scope for these lanes. No visible app launch, stop, or relaunch. Draft retained for independent adversarial verification.

Round-2 review follow-up

  • Clear awaitingPostCompactionUsage when the provider changes, so pending Claude occupancy does not leak into ACP. Preserve the known window and restore the estimated transcript fallback immediately.
  • One regression exercises the actual provider-switch coordinator and non-empty sidebar snapshot; it failed before the fix (ticket fd229c43).
  • Updated focused selection: 66 tests passed (ae986fe8); app product build passed (2cc33f12); changed-files-only formatting and make dev-lint passed (54aea581). Provider-package sources were unchanged in this follow-up; prior 20-test package validation still applies.
  • No full suite, live smoke, live provider run, app lifecycle, packaging, or release validation performed in this follow-up. Live smoke remains out of scope.

@baron
baron marked this pull request as ready for review October 23, 2026 09:26
@baron
baron merged commit 7e60fae into main Oct 23, 2026
7 of 8 checks passed
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.

Claude Code: context indicator remains stale immediately after compaction

1 participant