v0.3.64 e18c6ae

2026-07-28-configurable-auto-compact-threshold.md RawHistoryFolder

Configurable Auto-Compact Threshold Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Add persisted auto-compaction threshold presets to the interactive TUI settings.

Architecture: Keep automatic triggering in packages/agent/modes. Thread an optional integer percentage from persisted agent.Config into modes.InteractiveConfig, normalize missing or invalid values to 85%, and let zero disable only percentage-based triggers. Reuse the settings dialog's existing fixed-option picker and settings-store persistence path.

Tech Stack: Go, standard library, Zot's existing TUI settings dialog, JSON configuration.

Global Constraints

  • Supported values are 0 (off), 70, 80, 85, and 90.
  • Missing or invalid persisted values resolve to 85.
  • Disabling the threshold does not disable manual compaction or HTTP 413 recovery.
  • Do not add dependencies or change non-interactive compaction behavior.
  • Preserve unrelated working-tree changes and stage only this feature.

Task 1: Configuration and threshold decision

Files:

  • Modify: packages/agent/config.go
  • Modify: packages/agent/modes/interactive.go
  • Create: packages/agent/modes/auto_compact_test.go
  • Modify: packages/agent/cli.go

Interfaces:

  • Produces: Config.AutoCompactThreshold *int

  • Produces: InteractiveConfig.AutoCompactThreshold *int

  • Produces: normalizeAutoCompactThreshold(*int) int

  • Produces: shouldAutoCompact(inputTokens, contextWindow, thresholdPercent int) bool

  • Step 1: Write failing normalization and decision tests

Add table-driven tests proving nil and invalid values resolve to 85, supported presets survive unchanged, zero disables percentage checks, and 70/80/85/90 trigger exactly at their boundaries.

func TestNormalizeAutoCompactThreshold(t *testing.T) {
	tests := []struct {
		name string
		value *int
		want int
	}{
		{name: "missing", value: nil, want: 85},
		{name: "off", value: intPtr(0), want: 0},
		{name: "seventy", value: intPtr(70), want: 70},
		{name: "invalid", value: intPtr(42), want: 85},
	}
	// Assert normalizeAutoCompactThreshold(test.value) == test.want.
}

func TestShouldAutoCompactUsesConfiguredThreshold(t *testing.T) {
	if shouldAutoCompact(84, 100, 85) {
		t.Fatal("84% must remain below an 85% threshold")
	}
	if !shouldAutoCompact(85, 100, 85) {
		t.Fatal("85% must trigger an 85% threshold")
	}
	if shouldAutoCompact(100, 100, 0) {
		t.Fatal("off must disable percentage-triggered compaction")
	}
}
  • Step 2: Run tests and verify RED

Run:

go test ./packages/agent/modes -run 'TestNormalizeAutoCompactThreshold|TestShouldAutoCompactUsesConfiguredThreshold' -count=1

Expected: compilation failure because the normalization and decision helpers do not exist.

  • Step 3: Add optional configuration and minimal helpers

Add AutoCompactThreshold *int with JSON name auto_compact_threshold to both configuration structs. Implement normalization for exactly 0, 70, 80, 85, and 90, defaulting all other values to 85. Replace the hard-coded fraction check with the normalized percentage helper and pass the config field through cli.go.

  • Step 4: Run focused tests and verify GREEN

Run:

go test ./packages/agent/modes -run 'TestNormalizeAutoCompactThreshold|TestShouldAutoCompactUsesConfiguredThreshold' -count=1

Expected: PASS.

Task 2: Persistence and TUI preset picker

Files:

  • Modify: packages/agent/settings_store.go
  • Modify: packages/agent/settings_store_test.go
  • Modify: packages/agent/modes/interactive.go
  • Modify: packages/agent/modes/auto_compact_test.go

Interfaces:

  • Extends: SettingsStore.SetAutoCompactThreshold(percent int) error

  • Consumes: normalizeAutoCompactThreshold(*int) int

  • Step 1: Write failing persistence and settings-item tests

Add a settings-store test that writes 70 into an isolated $ZOT_HOME and verifies auto_compact_threshold plus an unrelated config field. Add a modes test that opens settings with missing configuration and verifies the option values are 0, 70, 80, 85, 90 with 85 selected.

func TestConfigSettingsStorePersistsAutoCompactThreshold(t *testing.T) {
	t.Setenv("ZOT_HOME", t.TempDir())
	if err := SaveConfig(Config{Theme: "dark"}); err != nil {
		t.Fatal(err)
	}
	if err := (configSettingsStore{}).SetAutoCompactThreshold(70); err != nil {
		t.Fatal(err)
	}
	cfg, err := LoadConfig()
	if err != nil {
		t.Fatal(err)
	}
	if cfg.AutoCompactThreshold == nil || *cfg.AutoCompactThreshold != 70 {
		t.Fatalf("auto_compact_threshold = %v, want 70", cfg.AutoCompactThreshold)
	}
	if cfg.Theme != "dark" {
		t.Fatalf("unrelated config changed: theme = %q, want dark", cfg.Theme)
	}
}
  • Step 2: Run tests and verify RED

Run:

go test ./packages/agent ./packages/agent/modes -run 'AutoCompactThreshold|SettingsDialog.*AutoCompact' -count=1

Expected: compilation or assertion failure because persistence and the picker row do not exist.

  • Step 3: Implement persistence and live setting application

Add the settings-store method, fixed picker options, choice resolution, action routing, integer parsing, live InteractiveConfig update, and status message. Persist only values exposed by the picker; normalize the live value defensively before use.

  • Step 4: Run focused tests and verify GREEN

Run:

go test ./packages/agent ./packages/agent/modes -run 'AutoCompactThreshold|SettingsDialog.*AutoCompact' -count=1

Expected: PASS.

Task 3: Documentation and full verification

Files:

  • Modify: README.md
  • Review: docs/superpowers/specs/2026-07-28-configurable-auto-compact-threshold-design.md
  • Review: docs/superpowers/plans/2026-07-28-configurable-auto-compact-threshold.md

Interfaces:

  • Documents: auto_compact_threshold

  • Documents: /settings presets and off semantics

  • Step 1: Update user documentation

Document off, 70%, 80%, 85% default, and 90% in the /settings section. Update /compact to state that threshold-based auto-compaction is configurable and that payload-too-large recovery remains active when the percentage trigger is off.

  • Step 2: Format changed Go files

Run:

gofmt -w packages/agent/config.go packages/agent/settings_store.go packages/agent/settings_store_test.go packages/agent/cli.go packages/agent/modes/interactive.go packages/agent/modes/auto_compact_test.go
  • Step 3: Run the complete test suite

Run:

go test ./...

Expected: PASS.

  • Step 4: Run static analysis

Run:

go vet ./...

Expected: PASS.

  • Step 5: Inspect the final change

Run:

git diff --check
git status --short
git diff

Confirm only the configuration, TUI, tests, README, design, and plan belong to the feature.

Task 4: Publish the pull request

Files:

  • Stage only the files listed in Tasks 1-3.

Interfaces:

  • Produces: branch codex/configurable-auto-compact-threshold

  • Produces: one feature commit and a ready GitHub pull request

  • Step 1: Create the feature branch

git switch -c codex/configurable-auto-compact-threshold
  • Step 2: Stage explicit feature paths and review

Stage only the implementation, tests, README, design, and plan. Review git diff --cached --check and git diff --cached.

  • Step 3: Commit
git commit -m "Make auto-compact threshold configurable"
  • Step 4: Push the branch

Push to the appropriate writable GitHub remote with upstream tracking.

  • Step 5: Open a ready pull request

Target the upstream default branch. Include the behavior, compatibility, user impact, and exact validation commands in the PR body.

source
1# Configurable Auto-Compact Threshold Implementation Plan
2
3> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
4
5**Goal:** Add persisted auto-compaction threshold presets to the interactive TUI settings.
6
7**Architecture:** Keep automatic triggering in `packages/agent/modes`. Thread an optional integer percentage from persisted `agent.Config` into `modes.InteractiveConfig`, normalize missing or invalid values to 85%, and let zero disable only percentage-based triggers. Reuse the settings dialog's existing fixed-option picker and settings-store persistence path.
8
9**Tech Stack:** Go, standard library, Zot's existing TUI settings dialog, JSON configuration.
10
11## Global Constraints
12
13- Supported values are `0` (off), `70`, `80`, `85`, and `90`.
14- Missing or invalid persisted values resolve to `85`.
15- Disabling the threshold does not disable manual compaction or HTTP 413 recovery.
16- Do not add dependencies or change non-interactive compaction behavior.
17- Preserve unrelated working-tree changes and stage only this feature.
18
19---
20
21### Task 1: Configuration and threshold decision
22
23**Files:**
24- Modify: `packages/agent/config.go`
25- Modify: `packages/agent/modes/interactive.go`
26- Create: `packages/agent/modes/auto_compact_test.go`
27- Modify: `packages/agent/cli.go`
28
29**Interfaces:**
30- Produces: `Config.AutoCompactThreshold *int`
31- Produces: `InteractiveConfig.AutoCompactThreshold *int`
32- Produces: `normalizeAutoCompactThreshold(*int) int`
33- Produces: `shouldAutoCompact(inputTokens, contextWindow, thresholdPercent int) bool`
34
35- [ ] **Step 1: Write failing normalization and decision tests**
36
37Add table-driven tests proving nil and invalid values resolve to 85, supported presets survive unchanged, zero disables percentage checks, and 70/80/85/90 trigger exactly at their boundaries.
38
39```go
40func TestNormalizeAutoCompactThreshold(t *testing.T) {
41	tests := []struct {
42		name string
43		value *int
44		want int
45	}{
46		{name: "missing", value: nil, want: 85},
47		{name: "off", value: intPtr(0), want: 0},
48		{name: "seventy", value: intPtr(70), want: 70},
49		{name: "invalid", value: intPtr(42), want: 85},
50	}
51	// Assert normalizeAutoCompactThreshold(test.value) == test.want.
52}
53
54func TestShouldAutoCompactUsesConfiguredThreshold(t *testing.T) {
55	if shouldAutoCompact(84, 100, 85) {
56		t.Fatal("84% must remain below an 85% threshold")
57	}
58	if !shouldAutoCompact(85, 100, 85) {
59		t.Fatal("85% must trigger an 85% threshold")
60	}
61	if shouldAutoCompact(100, 100, 0) {
62		t.Fatal("off must disable percentage-triggered compaction")
63	}
64}
65```
66
67- [ ] **Step 2: Run tests and verify RED**
68
69Run:
70
71```sh
72go test ./packages/agent/modes -run 'TestNormalizeAutoCompactThreshold|TestShouldAutoCompactUsesConfiguredThreshold' -count=1
73```
74
75Expected: compilation failure because the normalization and decision helpers do not exist.
76
77- [ ] **Step 3: Add optional configuration and minimal helpers**
78
79Add `AutoCompactThreshold *int` with JSON name `auto_compact_threshold` to both configuration structs. Implement normalization for exactly `0`, `70`, `80`, `85`, and `90`, defaulting all other values to `85`. Replace the hard-coded fraction check with the normalized percentage helper and pass the config field through `cli.go`.
80
81- [ ] **Step 4: Run focused tests and verify GREEN**
82
83Run:
84
85```sh
86go test ./packages/agent/modes -run 'TestNormalizeAutoCompactThreshold|TestShouldAutoCompactUsesConfiguredThreshold' -count=1
87```
88
89Expected: PASS.
90
91### Task 2: Persistence and TUI preset picker
92
93**Files:**
94- Modify: `packages/agent/settings_store.go`
95- Modify: `packages/agent/settings_store_test.go`
96- Modify: `packages/agent/modes/interactive.go`
97- Modify: `packages/agent/modes/auto_compact_test.go`
98
99**Interfaces:**
100- Extends: `SettingsStore.SetAutoCompactThreshold(percent int) error`
101- Consumes: `normalizeAutoCompactThreshold(*int) int`
102
103- [ ] **Step 1: Write failing persistence and settings-item tests**
104
105Add a settings-store test that writes `70` into an isolated `$ZOT_HOME` and verifies `auto_compact_threshold` plus an unrelated config field. Add a modes test that opens settings with missing configuration and verifies the option values are `0`, `70`, `80`, `85`, `90` with `85` selected.
106
107```go
108func TestConfigSettingsStorePersistsAutoCompactThreshold(t *testing.T) {
109	t.Setenv("ZOT_HOME", t.TempDir())
110	if err := SaveConfig(Config{Theme: "dark"}); err != nil {
111		t.Fatal(err)
112	}
113	if err := (configSettingsStore{}).SetAutoCompactThreshold(70); err != nil {
114		t.Fatal(err)
115	}
116	cfg, err := LoadConfig()
117	if err != nil {
118		t.Fatal(err)
119	}
120	if cfg.AutoCompactThreshold == nil || *cfg.AutoCompactThreshold != 70 {
121		t.Fatalf("auto_compact_threshold = %v, want 70", cfg.AutoCompactThreshold)
122	}
123	if cfg.Theme != "dark" {
124		t.Fatalf("unrelated config changed: theme = %q, want dark", cfg.Theme)
125	}
126}
127```
128
129- [ ] **Step 2: Run tests and verify RED**
130
131Run:
132
133```sh
134go test ./packages/agent ./packages/agent/modes -run 'AutoCompactThreshold|SettingsDialog.*AutoCompact' -count=1
135```
136
137Expected: compilation or assertion failure because persistence and the picker row do not exist.
138
139- [ ] **Step 3: Implement persistence and live setting application**
140
141Add the settings-store method, fixed picker options, choice resolution, action routing, integer parsing, live `InteractiveConfig` update, and status message. Persist only values exposed by the picker; normalize the live value defensively before use.
142
143- [ ] **Step 4: Run focused tests and verify GREEN**
144
145Run:
146
147```sh
148go test ./packages/agent ./packages/agent/modes -run 'AutoCompactThreshold|SettingsDialog.*AutoCompact' -count=1
149```
150
151Expected: PASS.
152
153### Task 3: Documentation and full verification
154
155**Files:**
156- Modify: `README.md`
157- Review: `docs/superpowers/specs/2026-07-28-configurable-auto-compact-threshold-design.md`
158- Review: `docs/superpowers/plans/2026-07-28-configurable-auto-compact-threshold.md`
159
160**Interfaces:**
161- Documents: `auto_compact_threshold`
162- Documents: `/settings` presets and `off` semantics
163
164- [ ] **Step 1: Update user documentation**
165
166Document `off`, `70%`, `80%`, `85%` default, and `90%` in the `/settings` section. Update `/compact` to state that threshold-based auto-compaction is configurable and that payload-too-large recovery remains active when the percentage trigger is off.
167
168- [ ] **Step 2: Format changed Go files**
169
170Run:
171
172```sh
173gofmt -w packages/agent/config.go packages/agent/settings_store.go packages/agent/settings_store_test.go packages/agent/cli.go packages/agent/modes/interactive.go packages/agent/modes/auto_compact_test.go
174```
175
176- [ ] **Step 3: Run the complete test suite**
177
178Run:
179
180```sh
181go test ./...
182```
183
184Expected: PASS.
185
186- [ ] **Step 4: Run static analysis**
187
188Run:
189
190```sh
191go vet ./...
192```
193
194Expected: PASS.
195
196- [ ] **Step 5: Inspect the final change**
197
198Run:
199
200```sh
201git diff --check
202git status --short
203git diff
204```
205
206Confirm only the configuration, TUI, tests, README, design, and plan belong to the feature.
207
208### Task 4: Publish the pull request
209
210**Files:**
211- Stage only the files listed in Tasks 1-3.
212
213**Interfaces:**
214- Produces: branch `codex/configurable-auto-compact-threshold`
215- Produces: one feature commit and a ready GitHub pull request
216
217- [ ] **Step 1: Create the feature branch**
218
219```sh
220git switch -c codex/configurable-auto-compact-threshold
221```
222
223- [ ] **Step 2: Stage explicit feature paths and review**
224
225Stage only the implementation, tests, README, design, and plan. Review `git diff --cached --check` and `git diff --cached`.
226
227- [ ] **Step 3: Commit**
228
229```sh
230git commit -m "Make auto-compact threshold configurable"
231```
232
233- [ ] **Step 4: Push the branch**
234
235Push to the appropriate writable GitHub remote with upstream tracking.
236
237- [ ] **Step 5: Open a ready pull request**
238
239Target the upstream default branch. Include the behavior, compatibility, user impact, and exact validation commands in the PR body.

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 476

↑↓ moveenter openesc close