12 KiB
Remove underdeveloped pruneOn modes (→ v1.0.0)
- Date: 2026-05-31
- Branch / worktree:
remove-prune-on-modes(.worktrees/remove-prune-on-modes) - Status: spec — awaiting review
- Release target:
v1.0.0(major)
Summary
Drop three pruneOn trigger modes — every-turn, on-context-tag, agentic-auto — and every artifact that exists only to serve them. Keep the two load-bearing modes: agent-message (default) and on-demand. Delete the dead remindUnprunedCount config field. Cut a v1.0.0 release marking the stable, two-mode core after removing experimental/underdeveloped surface.
The removed surface is either experimental-but-unwired (agentic-auto's context_prune tool was scaffolded, not productionized), coupled to an external extension we don't control (on-context-tag soft-depends on ttttmr/pi-context), or a cache-hostile debugging aid (every-turn).
Motivation & recorded assessment
Local setup impact: none. Verified across all three agent profiles:
| Profile | contextPrune.pruneOn |
Affected? |
|---|---|---|
~/.pi/agent |
(no contextPrune key) |
no |
~/.pi/agent.anthropic |
agent-message |
no |
~/.pi/agent.bedrock |
agent-message |
no |
Both .anthropic and .bedrock also carry remindUnprunedCount: true, which is already a no-op for them (only ever honored in agentic-auto).
Graceful fallback already exists. isPruneOn() in src/config.ts validates the persisted pruneOn against PRUNE_ON_MODES; an unknown/removed value falls back to DEFAULT_CONFIG.pruneOn (agent-message). So even a config pinned to a removed mode degrades silently rather than erroring.
pi-context coupling is soft, and overlaps this extension. There is no package/peer dependency on ttttmr/pi-context (package.json has zero deps on it). The only coupling is runtime: the tool_execution_end handler listens for the context_tag / context_checkpoint tool names. If pi-context isn't installed, on-context-tag silently never fires. Separately, pi-context's own context_compact tool performs LLM-driven compaction of a path into a handoff summary — partial functional overlap with this extension's rolling tool-batch summarization (different trigger models: checkpoint/time-travel vs. automatic). Removing on-context-tag drops a redundant trigger wired to an uncontrolled extension.
Scope
| Mode | Disposition | Rationale |
|---|---|---|
agent-message |
keep (default) | Flushes on the agent's next text response; prefix-cache friendly. |
on-demand |
keep | Manual flush via /pruner now. |
every-turn |
remove | Debugging-only; worst prompt-cache churn. |
on-context-tag |
remove | Soft-depends on external pi-context; redundant with its context_compact. |
agentic-auto |
remove | Model-driven context_prune tool was scaffolded, never productionized. |
End state: export type PruneOn = "on-demand" | "agent-message";
Removal inventory (the contract)
Files deleted
| File | Why it's now dead |
|---|---|
src/reminder.ts |
annotateWithUnprunedCount / countUnprunedToolCalls feed the <pruner-note>, only emitted in agentic-auto. |
src/context-prune-tool.ts |
Registers the context_prune tool, only activated in agentic-auto. |
src/progress-text.ts |
pruneProgressText is imported only by context-prune-tool.ts. (formatCharProgress lives in src/stats.ts and stays — used by the /pruner now widget.) |
index.ts
- Remove imports:
annotateWithUnprunedCount, countUnprunedToolCalls(./src/reminder.js);registerContextPruneTool(./src/context-prune-tool.js);CONTEXT_PRUNE_TOOL_NAME,AGENTIC_AUTO_SYSTEM_PROMPT(./src/types.js). - Fix initial
currentConfigvalue:{ ...DEFAULT_CONFIG, pruneOn: "every-turn" }→{ ...DEFAULT_CONFIG }(theevery-turnseed is a pre-session_startplaceholder and references a removed mode). - Delete the
syncToolActivationhelper and all calls to it (also itssession_startcall). - Delete the
tool_execution_endhandler in full (on-context-tag). - Delete the
before_agent_starthandler in full (agentic-autosystem prompt). - Delete the
registerContextPruneTool(...)registration call. - In the
contexthandler, delete theagentic-auto<pruner-note>block (thecountUnprunedToolCalls/annotateWithUnprunedCountpath). - In
turn_end: delete theif (pruneOn === "every-turn") { flush }branch; drop thepruneOn !== "every-turn"clause from the budget-flush guard (now unconditional); collapse the trigger-label switch to the two remaining modes:const trigger = currentConfig.value.pruneOn === "agent-message" ? "agent's next text response" : "/pruner now"; - Drop
CONTEXT_PRUNE_TOOL_NAMEfrom the two capture-exclusion sets (theturn_endprotected-toolSetand thecapturePendingBatchesexclude list); both reduce tocurrentConfig.value.protectedTools. - Drop the
syncToolActivationargument from theregisterCommands(...)call.
types.ts
PruneOn: narrow to"on-demand" | "agent-message"; update its doc comment.PRUNE_ON_MODES: keep onlyagent-message(listed first, the default) andon-demand.- Delete
CONTEXT_PRUNE_TOOL_NAMEandAGENTIC_AUTO_SYSTEM_PROMPTconsts. (PROGRESS_WIDGET_IDstays.) - Delete
remindUnprunedCountfrom theContextPruneConfiginterface and fromDEFAULT_CONFIG. - Update the
protectedToolsdoc comment that references theagentic-auto<pruner-note>.
config.ts
- Delete the
remindUnprunedCountnormalization block innormalize(). - No other change:
isPruneOnderives fromPRUNE_ON_MODES, so trimming that array narrows validation automatically. - No migration code added (per decision below).
commands.ts
PRUNE_MODE_GUIDANCE: remove theevery-turn/on-context-tag/agentic-autoentries.- Delete
remindUnprunedCountDescriptionand its uses. - Settings overlay: remove the
remindUnprunedCountSettingItem, itsonChangebranch, and the cross-update of its description in thepruneOnonChangebranch. - Remove the
syncToolActivationparameter fromregisterCommandsand every call site inside it. /pruner status: drop theremind: … (agentic-auto only)line.HELP_TEXT: remove theprune-on every-turn/on-context-tag/agentic-autolines, theremindUnprunedCountnote, theprotectedTools<pruner-note>sentence, and the removed-mode guidance bullets. In theRelated:block, drop thepi-contextlink line (orphaned by removingon-context-tag); keep theRelated:header and the Anthropic prompt-caching bullet.- Update the comment that parses a selector label (example currently
"every-turn — Every turn") to use a surviving mode.
Config & migration behavior
- Persisted removed modes (
pruneOn: "every-turn" | "on-context-tag" | "agentic-auto"): silently fall back toagent-messageviaisPruneOn(). No crash, no warning. - Dead
remindUnprunedCountkey: no strip/migration code is added. Becausenormalize()does{ ...DEFAULT_CONFIG, ...existing }, a staleremindUnprunedCountin an existing file is carried through into the runtime config object and re-persisted on the next settings write — until the key is physically removed from the file. It is therefore hand-cleaned from the two settings files after release (see Release plan). Once removed from the file it cannot reappear: nothing writes it back (the field is gone from the interface,DEFAULT_CONFIG, and the settings UI). - Rationale for no migration code: the user explicitly chose hand-clean-after-release over adding a one-line strip. Keeps the change pure-deletion; the release script rewrites the version pin in those same files, so tidying the dead key afterward avoids racing it.
Documentation changes
| Doc | Edits |
|---|---|
README.md |
Mode table: drop the three removed rows, keep agent-message + on-demand. Remove remindUnprunedCount from the example config block and the settings table. Update the autoBudgetThreshold note that references every-turn. Remove the context_prune tool description. Remove the pi-context "Related extensions" link tied to on-context-tag (keep unrelated pi-cache-graph tuning references). |
PRUNING.md |
Reframe the "per-turn pruning trap" ASCII section as naive per-turn pruning (a hypothetical bad approach that motivates the batched agent-message default), not a selectable mode. Remove the three modes from the comparison table and the trigger decision-tree. Fix the protectedTools <pruner-note> reference, the remindUnprunedCount bullet, the autoBudgetThreshold/every-turn note, the "scaffolded but not wired" agentic-auto compress-tool note, the cache-performance mode mentions, and the pi-context link. |
AGENTS.md |
Project-layout block: drop context-prune-tool from the (other src/*.ts) comment. |
CHANGELOG.md |
New top entry for v1.0.0 (terse bullets, newest-first, dated), recording the mode/field removals and the 1.0 framing. |
src/batch-capture.ts |
Update the comment (~line 128) that references agentic-auto; the trim-to-ready-tool-calls behavior stays (valid for any flush). |
Release plan (v1.0.0)
Final phase, in order:
- Typecheck — the AGENTS.md one-liner (
bun x tsc --noEmit …). Must pass. - Pre-release smoke (release gate) — end-to-end dummy session against an isolated
$PI_CODING_AGENT_DIR:pi -e ./index.ts --no-extensions -p "…". Confirm the flush path still writes the expectedcontext-prune-*session entries and that startup logs no error about missing modes/symbols. Inspect with:jq -r 'select(.type=="custom" or .type=="custom_message") | .customType' session.jsonl | sort | uniq -c - CHANGELOG entry + commit on the worktree branch.
- Release via the
releaseskill (release.sh): bumppackage.jsonto1.0.0, create + push tagv1.0.0, auto-rewrite thegit:…@vX.Y.Zpins in~/.pi/agent*/settings.json. - Clean config — hand-remove the dead
remindUnprunedCountkey from~/.pi/agent.anthropic/settings.jsonand~/.pi/agent.bedrock/settings.json. - Final sanity — one more end-to-end dummy session, now against the real pinned
v1.0.0+ cleaned config, confirmingcontext-prune-*entries are written and the session is error-free.
Testing / verification approach
- Baseline (captured now):
bun test src/→ 130 pass, 0 fail. No test references any removed symbol, mode, or deleted file, so the suite stays green by construction. - Typecheck gates the release (step 1 above); catches any missed import or symbol reference.
- End-to-end dummy session is the integration gate (steps 2 and 6): pure-deletion changes that typecheck can still break runtime wiring (e.g. a removed handler another path depended on), so a live flush is exercised before and after release.
- No new unit tests: this is removal of code paths that have no dedicated tests; the surviving
agent-message/on-demandpaths are already covered bypruner.test.tsand friends.
Edge cases & risks
- Stale
remindUnprunedCountre-persisted before hand-clean. Expected and benign — nothing reads it. Resolved permanently by step 5. - A user (not in scope here) on a removed mode. Falls back to
agent-message; acceptable for a 1.0 that drops the modes. PRUNE_ON_MODESordering. Cosmetic (drives only the settings selector + a label-parse). Spec listsagent-messagefirst as the default; the label-parse splits on—and is order-independent.- Broken release tag if step 2 is skipped. Mitigated by making the pre-release smoke a hard gate before
release.sh.
Out of scope
- Touching the surviving
agent-message/on-demandflush logic, batching, dedup, chain compression, or thinking-strip. - Any change to
package.jsonbeyond the version bump. - Adding migration/normalization code for the dead key (explicitly declined).
- Changing pi-context or interacting with it in any way.
Open questions
- Changelog wording for the 1.0 framing. The major bump was justified as "DCP parity + remove underdeveloped parts." This spec records the substance neutrally ("stable two-mode core after removing experimental surface"). If the literal "DCP parity" phrasing should appear verbatim in the
CHANGELOG/release notes, say so and name what "DCP" refers to; otherwise the neutral phrasing ships.