fix compaction kept-tail mapping after prior summaries#2646
fix compaction kept-tail mapping after prior summaries#2646rumpl wants to merge 1 commit intodocker:mainfrom
Conversation
e9d6efc to
56b7b6e
Compare
|
/review |
56b7b6e to
b3ab4bf
Compare
|
/review |
|
@aheritier Completely changed the implementation, please take a look |
Assessment: 🔴 CRITICALThis PR correctly identifies and fixes the core kept-tail mapping bug. The 🔴 [HIGH]
|
When a session already contained a summary, the kept-tail boundary returned by mapToSessionIndex was wrong. It counted non-system items from sess.Messages[0], but the filtered chat-message list it was indexing into came from sess.GetMessages(a), which prepends a synthetic 'Session Summary: ...' user message and starts iterating items at the prior summary's FirstKeptEntry. Both effects shifted the count, so the new FirstKeptEntry landed far too early in the session - typically inside the conversation the prior summary already replaced. Replace nonSystemMessages + mapToSessionIndex with a single gatherCompactionInput that walks sess.Messages directly (mirroring session.buildSessionSummaryMessages), surfaces the synthetic Session Summary message when a prior summary exists, and returns a parallel slice of origin indices. firstKeptSessionIndex maps a split index back to a sess.Messages index via that parallel slice - correct by construction regardless of prior summaries. The synthetic message maps back to the prior summary's index so the prior summary item is preserved across the new compaction when the boundary lands on it. ComputeFirstKeptEntry no longer needs the *agent.Agent parameter; updated its single call site in session_compaction.go. Adds regression coverage in compactor_test.go for the prior-summary case (with and without a non-zero FirstKeptEntry on the prior summary), and replaces TestMapToSessionIndex with the equivalent TestGatherCompactionInput_NoPriorSummary. Signed-off-by: Djordje Lukic <djordje.lukic@docker.com>
b3ab4bf to
abc7caa
Compare
|
/review |
There was a problem hiding this comment.
PR Review: fix compaction kept-tail mapping after prior summaries
Assessment: 🟡 NEEDS ATTENTION
The core fix — replacing mapToSessionIndex + nonSystemMessages with gatherCompactionInput + CompactionInput() — is correct and well-tested. The parallel sessIndices slice approach cleanly solves the off-by-one shift caused by the synthetic "Session Summary" message and the FirstKeptEntry start offset.
⚠️ Note: Automatic verification was inconclusive (upstream API overloaded). The following finding is from draft analysis and has not been independently confirmed. The author should evaluate it.
🤖 Code Review —
|
There was a problem hiding this comment.
Assessment: 🟢 APPROVE
The fix correctly addresses the FirstKeptEntry mapping bug when prior summaries exist. The new gatherCompactionInput + firstKeptSessionIndex approach walks sess.Messages directly with a parallel origin-index slice, which is correct by construction regardless of prior summaries. Concurrency model (RLock in snapshotItems) is sound. Regression tests accurately cover the reported bug.
No high or medium severity issues were found in the changed code.
Low-severity observations (no action required)
pkg/session/session.go:Item.FirstKeptEntryusesomitemptyin JSON serialization, making the value0indistinguishable from "unset". The struct comment mentions-1as a sentinel but-1is never used in practice — the actual sentinel is0. This is a documentation inconsistency and a future maintenance hazard, not an active bug.pkg/session/session.go:sessIndicesis intentionally non-monotonic when a prior summary exists (the synthetic summary message maps to a higher index than the kept-tail entries that follow it). The current code handles this correctly, but future callers should not assume the slice is sorted.pkg/runtime/compactor/compactor.go:extractMessagesretains a_ *agent.Agentdead parameter. No functional impact since the function is unexported.
Problem
When a session already contained a summary, the kept-tail boundary returned by
mapToSessionIndexwas wrong. It counted non-system items fromsess.Messages[0], but the filtered chat-message list it was indexing into came fromsess.GetMessages(a), which:"Session Summary: ..."user message that doesn't exist insess.Messagesat all.FirstKeptEntry— skipping every item before that offset.Both effects shift the count, so the new
FirstKeptEntrylanded far too early in the session — typically inside the conversation that the prior summary already replaced. Subsequent compactions could then resurrect already-summarized history.Fix
Replace
nonSystemMessages+mapToSessionIndexwith a singlegatherCompactionInputthat walkssess.Messagesdirectly (mirroringsession.buildSessionSummaryMessages), surfaces the synthetic Session Summary message when a prior summary exists, and returns a parallel slice of origin indices.firstKeptSessionIndexmaps a split index back to asess.Messagesindex via that parallel slice — correct by construction regardless of prior summaries. The synthetic message maps back to the prior summary's index, so when the kept-tail boundary lands on it, the prior summary item is preserved across the new compaction.ComputeFirstKeptEntryno longer needs the*agent.Agentparameter; updated its single call site insession_compaction.go.Tests
Added regression coverage in
pkg/runtime/compactor/compactor_test.go:TestGatherCompactionInput_NoPriorSummary— replaces the oldTestMapToSessionIndex, pinning the no-summary path.TestGatherCompactionInput_WithPriorSummary— the regression test: with a summary at index 10 (FirstKeptEntry=8), a split keeping the last 2 messages now correctly maps toitems[13], where the old code returneditems[5].TestGatherCompactionInput_PriorSummaryWithoutFirstKeptEntry— covers the "compact everything" prior-summary case where iteration must start strictly after the summary item.