-
Notifications
You must be signed in to change notification settings - Fork 3k
feat(core)!: redesign auto-compaction thresholds with three-tier ladder #4168
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
54bd9b1
feat(core)!: redesign auto-compaction thresholds with three-tier ladder
LaZzyMan 1c6c4b7
test(core): fix leftover hasFailedCompressionAttempt option in compre…
LaZzyMan 821265c
fix(core): drop compaction summary when output hits maxOutputTokens cap
LaZzyMan 0dcf773
fix(cli): render three-tier thresholds in /context TUI view
LaZzyMan f5f11e2
fix(core,cli): address PR #4168 review batch 4
LaZzyMan e742c10
fix(core,cli): address PR #4168 review batch 5
LaZzyMan c5ee6db
fix(core,cli): address PR #4168 review batch 6
LaZzyMan c68eb77
fix(core,cli): address PR #4168 review batch 7
LaZzyMan c19ecfc
refactor(core): trim narration-heavy comments in R7 changes
LaZzyMan 89e1e77
fix(core): address PR #4168 review batch 8 (R7.1 incompleteness clust…
LaZzyMan e36aa36
fix(core,cli): address PR #4168 review batch 9
LaZzyMan c3969f7
fix(core): plumb lastCandidatesTokenCount into prompt-token estimator…
LaZzyMan e861d07
fix(core,cli): address PR #4168 review batch 11
LaZzyMan b198603
fix(core,cli): address PR #4168 review batch 12
LaZzyMan File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
1,752 changes: 1,752 additions & 0 deletions
1,752
docs/plans/2026-05-14-auto-compaction-threshold-redesign.md
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,135 @@ | ||
| /** | ||
| * @license | ||
| * Copyright 2025 Google LLC | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| import { describe, it, expect } from 'vitest'; | ||
| import { tipRegistry, type TipContext } from './tipRegistry.js'; | ||
|
|
||
| const baseCtx: TipContext = { | ||
| lastPromptTokenCount: 0, | ||
| contextWindowSize: 200_000, | ||
| sessionPromptCount: 10, | ||
| sessionCount: 1, | ||
| platform: 'darwin', | ||
| thresholds: { | ||
| warn: 147_000, | ||
| auto: 167_000, | ||
| hard: 177_000, | ||
| effectiveWindow: 180_000, | ||
| }, | ||
| }; | ||
|
|
||
| function tipById(id: string) { | ||
| return tipRegistry.find((t) => t.id === id)!; | ||
| } | ||
|
|
||
| describe('context-* tip thresholds align with computeThresholds', () => { | ||
| it('compress-intro fires between warn and auto', () => { | ||
| const t = tipById('compress-intro'); | ||
| expect(t.isRelevant({ ...baseCtx, lastPromptTokenCount: 100_000 })).toBe( | ||
| false, | ||
| ); | ||
| expect(t.isRelevant({ ...baseCtx, lastPromptTokenCount: 150_000 })).toBe( | ||
| true, | ||
| ); | ||
| expect(t.isRelevant({ ...baseCtx, lastPromptTokenCount: 168_000 })).toBe( | ||
| false, | ||
| ); | ||
| }); | ||
|
|
||
| it('context-high fires between auto and hard', () => { | ||
| const t = tipById('context-high'); | ||
| expect(t.isRelevant({ ...baseCtx, lastPromptTokenCount: 150_000 })).toBe( | ||
| false, | ||
| ); | ||
| expect(t.isRelevant({ ...baseCtx, lastPromptTokenCount: 170_000 })).toBe( | ||
| true, | ||
| ); | ||
| expect(t.isRelevant({ ...baseCtx, lastPromptTokenCount: 178_000 })).toBe( | ||
| false, | ||
| ); | ||
| }); | ||
|
|
||
| it('context-critical fires at or above hard', () => { | ||
| const t = tipById('context-critical'); | ||
| expect(t.isRelevant({ ...baseCtx, lastPromptTokenCount: 170_000 })).toBe( | ||
| false, | ||
| ); | ||
| expect(t.isRelevant({ ...baseCtx, lastPromptTokenCount: 178_000 })).toBe( | ||
| true, | ||
| ); | ||
| }); | ||
|
|
||
| it('context-high covers the small-window collapse case (R11.2: hard === auto)', () => { | ||
| // R9.5 gated context-critical on `hard > auto` to avoid claiming | ||
| // "near hard limit" when there's no distinct hard tier. That | ||
| // created a coverage gap on small windows: context-high's band | ||
| // `[auto, hard)` is the empty set when hard === auto, so users at | ||
| // the auto threshold got no tip at all. R11.2: context-high must | ||
| // fire on `>= auto` when hard === auto (treating it as "everything | ||
| // above auto" — there's no distinct hard tier to delimit). | ||
| const t = tipById('context-high'); | ||
| const collapsedCtx = { | ||
| ...baseCtx, | ||
| thresholds: { | ||
| effectiveWindow: 32_000, | ||
| warn: 18_000, | ||
| auto: 22_400, | ||
| hard: 22_400, // collapsed | ||
| }, | ||
| lastPromptTokenCount: 25_000, // above the collapsed threshold | ||
| }; | ||
| expect(t.isRelevant(collapsedCtx)).toBe(true); | ||
| }); | ||
|
|
||
| it('context-critical suppresses when hard === auto (R9.5 small-window collapse)', () => { | ||
| // On small windows (e.g. 32K) computeThresholds collapses | ||
| // hard to equal auto. The critical band [hard, ∞) starts at the | ||
| // auto threshold; firing the tip there would claim "near hard | ||
| // limit" when there is no distinct hard limit. R9.5: gate on | ||
| // `hard > auto` like `currentTier` does. The `context-high` tip | ||
| // in band `[auto, hard)` already covers small windows. | ||
| const t = tipById('context-critical'); | ||
| const collapsedCtx = { | ||
| ...baseCtx, | ||
| thresholds: { | ||
| effectiveWindow: 32_000, | ||
| warn: 18_000, | ||
| auto: 22_400, | ||
| hard: 22_400, // collapsed to equal auto | ||
| }, | ||
| lastPromptTokenCount: 25_000, // above the collapsed threshold | ||
| }; | ||
| expect(t.isRelevant(collapsedCtx)).toBe(false); | ||
| }); | ||
|
|
||
| it('falls back gracefully when thresholds undefined (legacy callers)', () => { | ||
| const ctx = { ...baseCtx, thresholds: undefined }; | ||
| // All three context-* tips return false when thresholds are missing | ||
| // (the comparison would be unsafe without them). | ||
| expect(tipById('compress-intro').isRelevant(ctx)).toBe(false); | ||
| expect(tipById('context-high').isRelevant(ctx)).toBe(false); | ||
| expect(tipById('context-critical').isRelevant(ctx)).toBe(false); | ||
| }); | ||
|
|
||
| it('compress-intro additionally gates on sessionPromptCount > 5', () => { | ||
| const t = tipById('compress-intro'); | ||
| // Above warn, below auto, but session is too new. | ||
| expect( | ||
| t.isRelevant({ | ||
| ...baseCtx, | ||
| lastPromptTokenCount: 150_000, | ||
| sessionPromptCount: 3, | ||
| }), | ||
| ).toBe(false); | ||
| expect( | ||
| t.isRelevant({ | ||
| ...baseCtx, | ||
| lastPromptTokenCount: 150_000, | ||
| sessionPromptCount: 6, | ||
| }), | ||
| ).toBe(true); | ||
| }); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.