Honor an explicit MinimumPreserved=0 in compaction strategies instead of collapsing to the default - #686
Conversation
There was a problem hiding this comment.
Pull request overview
Updates the compaction strategies’ “minimum preserved” floors so callers can explicitly set 0 (preserve none) and have it honored, while keeping defaults when the value is unset. This aligns the Go SDK behavior with the .NET port by making the minimum-preserved fields nullable and clamping negative values to 0.
Changes:
- Changed
MinimumPreservedGroups/MinimumPreservedTurnsfields fromintto*intacross compaction strategies to distinguish “unset” vs “explicit 0”. - Updated compaction logic to use defaults when the pointer is
nil, otherwise clamp the provided value to>= 0. - Updated examples and tests; added new tests covering explicit
0and negative clamping behavior.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| examples/02-agents/agents/step18_compaction_pipeline/main.go | Updates example strategy config to use pointer-valued minimum-preserved fields and adds a local ptr helper. |
| agent/compaction/truncation.go | Makes MinimumPreservedGroups nullable and honors explicit 0 / clamps negatives. |
| agent/compaction/toolresult.go | Makes MinimumPreservedGroups nullable and honors explicit 0 / clamps negatives. |
| agent/compaction/summarization.go | Makes MinimumPreservedGroups nullable and honors explicit 0 / clamps negatives while keeping existing prompt/default behavior. |
| agent/compaction/slidingwindow.go | Makes MinimumPreservedTurns nullable and honors explicit 0 / clamps negatives. |
| agent/compaction/index_test.go | Updates tests to use pointer-valued MinimumPreservedGroups. |
| agent/compaction/contextwindow.go | Updates internal strategy wiring to provide pointer-valued minimum-preserved config. |
| agent/compaction/compaction_test.go | Updates tests, adds new coverage for explicit 0 and negative clamping, and adds a ptr helper for tests. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
68767cd to
1ce19e4
Compare
This comment has been minimized.
This comment has been minimized.
The four compaction strategies computed their preserve floor with cmp.Or(max(field, 0), default), which folds unset, explicit 0, and negative values all into the type default (truncation 32, sliding window 1, summarization 8, tool result 16). A caller who set MinimumPreserved* to 0 to preserve nothing silently got the default instead, and there was no way to express the preserve-none config. Change each MinimumPreserved* field to *int so an unset (nil) config uses the default while an explicit value is honored as-is, clamping a negative to 0. This mirrors the .NET port, which uses a nullable int and EnsureNonNegative(value) => Math.Max(0, value).
1ce19e4 to
083c194
Compare
Cross-SDK Parity Review ✅Verdict: parity-approved — this change restores alignment with the upstream .NET implementation. What changedAll four compaction strategy structs ( Upstream .NET parityThe canonical .NET equivalents live in
The old Go behavior ( Go idiom alignmentUsing No new divergence introducedNo upstream Python compaction implementation was found; the .NET reference is the primary source of truth for this subsystem and the PR is consistent with it. Labels
Warning Firewall blocked 1 domainThe following domain was blocked by the firewall during workflow execution:
network:
allowed:
- defaults
- "awmgmcpg"See Network Configuration for more information.
|
What
The four compaction strategies (
TruncationStrategy,SlidingWindowStrategy,SummarizationStrategy,ToolResultStrategy) computed their minimum-preserved floor with:Because
max(0, 0) == 0andcmp.Or(0, default) == default, this folds three distinct inputs into the type default (truncation 32, sliding window 1, summarization 8, tool result 16):0— silently overridden by the defaultSo a caller who set
MinimumPreservedGroups: 0to preserve nothing got the default instead, and the aggressive preserve-none configuration was inexpressible. Only positive values worked.This changes each
MinimumPreserved*field to*int: anilconfig uses the default, while a non-nil value is honored as-is with a negative clamped to0:Why
The .NET port treats this as a nullable int and applies
EnsureNonNegative(value) => Math.Max(0, value), so an explicit0disables the floor and negatives clamp to0. The Go port silently discarded the value instead, leaving no equivalent for the preserve-none case. This restores cross-SDK parity.Tests
TestTruncationStrategy_ExplicitZeroPreservesNone: an index with 5 non-system groups,nilTrigger/Target, andMinimumPreservedGroups: ptr(0)now compacts and excludes all removable non-system groups rather than preserving the default 32.TestSlidingWindowStrategy_NegativeMinimumClampsToZero: a negative floor clamps to0(excluding all turns) instead of using the default 1.contextwindow.go, the step18 example) and existing tests to the pointer field type.go build ./...,go vet ./agent/compaction/..., andgo test ./agent/compaction/...all pass.