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.
Expected behavior
When the source provider is asked to
/compactbefore 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.tsaccepts the first compaction whosedescribeLatestCompactionavailability is notincomplete. The parser treats any non-empty carrier text other than the literalConversation compactedplaceholder asportable.In the vendored Claude Code source,
compactConversation(services/compact/compact.ts) rejects a summary only when it starts with the literalAPI Errorprefix. A subscriber 429 is turned into an assistant record whose text starts withYou've hit your …(services/api/errors.ts,services/rateLimitMessages.ts), which does not carry that prefix. The prompt-cache fork path guardsisApiErrorMessage, 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.tscompactionAvailability: only the placeholder is treated as incomplete.src/main/providerSwitch/compactBeforeSwitch.tswaitForNewCompaction: acceptsavailability !== 'incomplete'.services/compact/compact.ts(startsWithApiErrorPrefixguard; fork-pathisApiErrorMessageguard at ~1210) andservices/api/withRetry.tsshouldRetry(429 is not retried for claude.ai subscribers)."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_limiterror record lands after/compact.Repro plan
Extract a fixture from a Claude transcript where
/compactran under a limit (or synthesize the boundary/carrier pair from a real rate-limit record) and assert the parser reportsrejectedand the wait loop throws.