Repository navigation
fix(runtime): re-fold when a manual-compaction checkpoint is stale - #5931
Conversation
…euse The compactHistory fast path returned already_compacted on a raw-prefix match alone, while the pre-send gate rejects the same checkpoint with effective_history_changed. Fold the covered span through the current projection transitions and require the pinned effectiveSourceDigest to still match; on drift, fall through to the planner's roll-forward gate, which discards the stale checkpoint and re-summarizes the current effective view (apache#5929). Generated-by: Devin Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
jackwener
left a comment
There was a problem hiding this comment.
No findings.
At exact head 752cff908ad3503778bb698a18b5dbe6a25d77e3, the manual-compaction reuse path now folds the checkpoint's covered events through the current projection-transition ledger and validates coverage.effectiveSourceDigest before returning already_compacted. When the effective view has drifted, it falls through to the existing planner, which discards the stale checkpoint and summarizes the current effective view. When the digest still matches, the path remains a no-op and makes no summarizer call.
I verified the regression in both directions: the new test passes with the gate, while removing the gate reproduces the old unchanged result instead of compacted. The exact head passed the Runtime build, the complete ai-sdk-backend suite (254/254), and the affected compaction/projection suites (303/303), plus Biome, ASF-header, and whitespace checks. The fresh synthetic merge into current main 1a66e4d5e22067afd986d43fc634159e307a8e31 was clean (tree ca796f7186e4d572d4407cf7560d4443db8eb060); the two PR files were byte-identical to the tested head, and the merge result passed the same 254/254 and 303/303 suites. The exact-head hosted test check is successful.
The PR's Devin disclosure matches the substantive commit, which carries Generated-by: Devin. I did not run a live-provider manual-compaction session; the regression uses the real Runtime backend compaction and projection seams with deterministic injected ledger state.
Automated review notice: This comment was posted by an automated review agent operated by jackwener. It is not an independent human review and does not replace one.
Astro-Han
left a comment
There was a problem hiding this comment.
Reviewed 752cff908ad3503778bb698a18b5dbe6a25d77e3 (2 files, +130/−2; the production change itself is +8/−1).
No P0–P3 findings.
What the change does. The manual-compaction reuse fast path no longer decides on raw prefix identity alone. Before returning already_compacted it now folds the checkpoint's covered events through the current projection transitions and checks the pinned effective digest:
const effectiveCovered = await this.foldEffectiveModelHistory(match.coveredRuntimeEvents);
if (this.checkpointEffectiveCoverageMatches(previousCheckpoint, effectiveCovered)) {On a mismatch the reuse branch is skipped and control reaches the planner, whose roll-forward currency check discards the stale checkpoint and re-summarizes the current effective view.
The part I verified directly, because it is the substance of the claim: the gate is genuinely the same one the pre-send path applies, not a lookalike. checkpointEffectiveCoverageMatches is a single private method in this file (defined once, at ai-sdk-compaction.ts:576) and the new call site at :326 joins the existing one at :728. So the two paths cannot drift apart in what they consider "effective history" without someone editing the one implementation — which is the property that makes this fix right rather than merely plausible. The wrapping structure also matches the body's stated behaviour: the no-op path is untouched when the digest still matches (no summarizer call), and drift falls through rather than being answered as already_compacted.
The root cause the description gives is consistent with the code: a projection transition committed after the fold (an archived tool result, for instance) rewrites the effective view of the covered span without touching the raw ledger, so a raw-prefix match could still succeed while the effective digest no longer did.
Gate on this head: test is green (label likewise); mergeable is true and the state is blocked.
What I could not judge, and what rests on the existing review
- I did not run the suites. The both-direction evidence — the new test asserting
compactedwith the gate, and reproducing the oldunchangedresult without it — is reported by the existing current-head review rather than reproduced by me; I verified the gate-sharing structure and the regression test's presence, not its outcome. - No live-provider manual-compaction session, so the end-to-end behaviour after drift is not exercised here.
I did not approve, request changes, or merge.
Automated review notice: This comment was posted by an automated review agent operated by Astro-Han. It is not an independent human review and does not replace one.
jackwener
left a comment
There was a problem hiding this comment.
Approved at exact head 752cff908ad3503778bb698a18b5dbe6a25d77e3. I found no unresolved P0–P2 issues.
The manual-compaction fast path now checks both raw-prefix identity and the current effective-source digest before reusing a checkpoint. If projection state has changed, the existing planner discards the stale checkpoint and summarizes the current effective view. The regression passes with this gate and reproduces the old unchanged result when the gate is removed.
The PR is open, non-draft, mergeable, and its exact-head test check is successful. The exact head passed the Runtime build, the complete ai-sdk-backend suite (254/254), and the affected compaction/projection suites (303/303). It also merges cleanly with current main 887924e14aa3fe78b746fd88a43548d7335b9573 as tree 00b27ba5ba9e244798698369790916624c9baf8c; the advance since the tested merge touches only UI files, and the complete Runtime subtree is byte-identical to the tested merge result.
Review notice: This review was prepared by an automated review agent operated by jackwener and is published at the direction of colafornia, who has read these findings and is the human accountable for them.
…sult pruning (#6058) Manual /compact (AiSdkCompaction.compactHistory) summarized the covered span without running tool-result pruning first, so an oversized but unarchived function response appeared in the summarizer's effective view at full length and the checkpoint pinned coverage.effectiveSourceDigest over that view. On the next Turn, prepareContextBudgetPolicy runs pruneToolResults before it validates the checkpoint; the fresh archive transition replaces the covered body with its placeholder, the pinned digest no longer matches, and the just-written summary was silently discarded as effective_history_changed while the raw history replayed. Fix: compactHistory now runs the same pruneToolResults pass over its planning pool before planning. The archives the next Turn would commit anyway are committed first, so projectEffectiveCoverage folds them into both the summarizer's view and the pinned digest - the same effective view the pre-send validator folds. The planning pool stays the raw events, so the checkpoint's coverage identity, the #5929 reuse gate, and the #5931 fast path are untouched; no checkpoint schema changes. Fixes #6048 Generated-by: GLM-5.3-Flash (ZCode)
English
Summary
When a user asks for manual compaction, the runtime could answer
already_compactedand do nothing, even though the stored checkpoint was stale. The send path already rejects the same checkpoint witheffective_history_changed, so the stale checkpoint stayed in place.The cause is a missing check in the reuse fast path.
compactHistoryonly matched the raw event prefix (matchHistoryCompactCheckpointPrefix). A projection transition committed after the fold — for example, an archived tool result — changes the effective view without touching the raw ledger, so the raw match still passed.Now the fast path folds the covered events through the current projection transitions and checks
coverage.effectiveSourceDigestviacheckpointEffectiveCoverageMatches, the same gateprepareContextBudgetPolicyapplies before a send. When the digest no longer matches, the call falls through toplanHistoryCompaction, whose existing roll-forward gate discards the stale checkpoint and re-summarizes the current effective view.Behavior change: after the covered history drifts, manual compaction returns
compactedand writes a new checkpoint. With unchanged effective history it stays a no-op with no model call. Checkpoint schema, provider fit, and trigger policy are unchanged.Fixes #5929
Verification
manual compactHistory re-folds when a transition drifted the covered effective history (#5929)inai-sdk-backend.test.tsfails without the fix (returnsunchangedinstead ofcompacted) and passes with it. It also asserts that the summarizer reads the folded effective view, and that a repeat compaction with a matching digest stays a no-op with no summarizer call.node --test dist/__tests__/ai-sdk-backend.test.js: 254/254 pass.history-compaction,history-compact-checkpoint,history-compact-summarizer,history-compact-mid-turn-checkpoint,history-compact-ledger,model-projection-transition-ledger,conversation-copy,memory-extraction,overflow-reactive-recovery,mid-turn-capacity-backend): 303/303 pass.tsc -p tsconfig.jsonfor@maka/core,@maka/storage,@maka/runtime: clean.biome checkon the two touched files: clean.npm test,lint/format:checkbeyond the touched files,knip, desktop e2e, and live-provider manual compaction (the issue notes there is no live-session reproduction).AI use
Tool(s) and scope: Devin (Cognition) wrote the fix and the regression test; the commit carries a
Generated-by: Devintrailer.Checklist
Does this PR entail a change in behavior?
中文
Summary
用户手动触发压缩时,runtime 可能回答
already_compacted然后什么都不做,尽管已存的 checkpoint 已经过时。发送路径对同一个 checkpoint 会以effective_history_changed拒绝,所以过时的 checkpoint 一直留着。根因是复用快路径漏了一项检查。
compactHistory只匹配原始事件前缀(matchHistoryCompactCheckpointPrefix)。压缩之后提交的投影 transition——比如归档一个工具结果——会改变有效视图,但不改原始账本,所以原始匹配照样通过。现在快路径先用当前的投影 transition 折算被覆盖的事件,再通过
checkpointEffectiveCoverageMatches校验coverage.effectiveSourceDigest,和prepareContextBudgetPolicy在发送前做的检查一样。digest 不匹配时,调用落入planHistoryCompaction,由它现有的滚动续接检查丢弃过时的 checkpoint,并基于当前有效视图重新摘要。行为变化:覆盖范围内的有效历史漂移后,手动压缩返回
compacted并写入新 checkpoint。有效历史没变时仍是无操作,不调用模型。checkpoint 结构、供应商判定和触发策略不变。Fixes #5929
Verification
ai-sdk-backend.test.ts新增回归测试manual compactHistory re-folds when a transition drifted the covered effective history (#5929):不修复时失败(返回unchanged),修复后通过。还断言摘要器读到的是折算后的有效视图,以及 digest 匹配时重复压缩不调用摘要器。node --test dist/__tests__/ai-sdk-backend.test.js:254/254 通过。history-compaction、history-compact-checkpoint、history-compact-summarizer、history-compact-mid-turn-checkpoint、history-compact-ledger、model-projection-transition-ledger、conversation-copy、memory-extraction、overflow-reactive-recovery、mid-turn-capacity-backend):303/303 通过。tsc -p tsconfig.json(@maka/core、@maka/storage、@maka/runtime):干净。biome check(两个改动文件):干净。npm test、改动文件以外的lint/format:check、knip、desktop e2e、真实供应商下的手动压缩(issue 注明没有真实会话复现)。AI use
Tool(s) and scope: Devin (Cognition) wrote the fix and the regression test; the commit carries a
Generated-by: Devintrailer.Checklist
Does this PR entail a change in behavior?