Skip to content

bug(provider-switch): compaction wait may accept a Claude rate-limit message as the portable summary #820

Description

@Juliusolsson05

Expected behavior

When the source provider is asked to /compact before a switch and the compaction request is rejected by a usage limit, the switch must abort without replacing the pane, and no rate-limit message may ever be accepted as the portable summary.

Actual behavior (plausible, not yet reproduced)

compactBeforeSwitch.ts accepts the first compaction whose describeLatestCompaction availability is not incomplete. The parser treats any non-empty carrier text other than the literal Conversation compacted placeholder as portable.

In the vendored Claude Code source, compactConversation (services/compact/compact.ts) rejects a summary only when it starts with the literal API Error prefix. A subscriber 429 is turned into an assistant record whose text starts with You've hit your … (services/api/errors.ts, services/rateLimitMessages.ts), which does not carry that prefix. The prompt-cache fork path guards isApiErrorMessage, but the streaming fallback path was not verified to reject it. If Claude persists that text as a compact boundary and carrier, Agent Code's wait loop would accept it, project a transcript whose only history is the limit message, and replace the pane.

Evidence

  • packages/agent-transcript-parser/src/operations/compaction.ts compactionAvailability: only the placeholder is treated as incomplete.
  • src/main/providerSwitch/compactBeforeSwitch.ts waitForNewCompaction: accepts availability !== 'incomplete'.
  • Vendored Claude Code services/compact/compact.ts (startsWithApiErrorPrefix guard; fork-path isApiErrorMessage guard at ~1210) and services/api/withRetry.ts shouldRetry (429 is not retried for claude.ai subscribers).
  • Five local Claude transcripts in the last 300 contain "error":"rate_limit" records; none has yet been checked for a following compact boundary.

Impact

Data-destroying for the exact scenario the bulk switch exists for. Even if Claude never persists such a boundary, the wait loop should reject a rate-limit carrier and fail fast when a rate_limit error record lands after /compact.

Repro plan

Extract a fixture from a Claude transcript where /compact ran under a limit (or synthesize the boundary/carrier pair from a real rate-limit record) and assert the parser reports rejected and the wait loop throws.

Activity

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

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions