181 lines
7.2 KiB
Markdown
181 lines
7.2 KiB
Markdown
# Context manager stronger pruning and compaction 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:** Keep live context flatter by pruning whole older turns, compacting earlier, and injecting only lean resume state.
|
|
|
|
**Architecture:** Extend pruning at the context boundary, keep summary artifacts in snapshot/ledger only, and add an extension-managed early-compaction gate driven by observed context usage. Preserve existing commands and ledger semantics while removing footer status noise.
|
|
|
|
**Tech Stack:** TypeScript, Node test runner (`tsx --test`), Pi extension hooks
|
|
|
|
---
|
|
|
|
### Task 1: Turn-aware pruning and lean resume rendering
|
|
|
|
**Files:**
|
|
- Modify: `src/prune.ts`
|
|
- Modify: `src/prune.test.ts`
|
|
- Modify: `src/summaries.ts`
|
|
- Modify: `src/summaries.test.ts`
|
|
- Modify: `src/runtime.ts`
|
|
- Modify: `src/runtime.test.ts`
|
|
|
|
- [ ] **Step 1: Write the failing prune and resume tests**
|
|
|
|
```ts
|
|
test("pruneContextMessages drops turns older than the kept suffix", () => {
|
|
const messages = [
|
|
{ role: "user", content: "turn 1" },
|
|
{ role: "assistant", content: "after turn 1" },
|
|
{ role: "user", content: "turn 2" },
|
|
{ role: "assistant", content: "after turn 2" },
|
|
{ role: "user", content: "turn 3" },
|
|
];
|
|
|
|
const pruned = pruneContextMessages(messages, buildPolicy(2));
|
|
assert.deepEqual(pruned.map((m) => m.content), ["turn 2", "after turn 2", "turn 3"]);
|
|
});
|
|
|
|
test("buildResumePacket keeps blocker text after raw handoff sections are removed", () => {
|
|
const runtime = createContextManagerRuntime({ mode: "balanced", contextWindow: 200_000 });
|
|
runtime.recordCompactionSummary("## Open questions and blockers\n- Verify /tree replaceInstructions behavior.");
|
|
assert.match(runtime.buildResumePacket(), /Verify \/tree replaceInstructions behavior/);
|
|
assert.doesNotMatch(runtime.buildResumePacket(), /## Latest compaction handoff/);
|
|
});
|
|
```
|
|
|
|
- [ ] **Step 2: Run tests to verify they fail**
|
|
|
|
Run: `cd /home/dev/pi-packages/pi-context-manager/.worktree/context-manager-prune-compact && npm test -- src/prune.test.ts src/runtime.test.ts src/summaries.test.ts`
|
|
Expected: FAIL because pruning still keeps old turns and runtime still prepends raw handoff sections.
|
|
|
|
- [ ] **Step 3: Write the minimal implementation**
|
|
|
|
```ts
|
|
// src/prune.ts
|
|
// Keep only the last N user-anchored turns.
|
|
// Distill bulky tool results only for kept older turns, never for the newest turn.
|
|
|
|
// src/summaries.ts
|
|
export function buildResumePacket(ledger: LedgerState): string {
|
|
return [
|
|
...lines("Goal", getActiveItems(ledger, "goal").map((item) => item.text)),
|
|
...lines("Current task", getActiveItems(ledger, "activeTask").map((item) => item.text)),
|
|
...lines("Constraints", getActiveItems(ledger, "constraint").map((item) => item.text)),
|
|
...lines("Key decisions", getActiveItems(ledger, "decision").map((item) => item.text)),
|
|
...lines("Open questions / blockers", getActiveItems(ledger, "openQuestion").map((item) => item.text)),
|
|
].join("\n").trim();
|
|
}
|
|
|
|
// src/runtime.ts
|
|
function buildResumePacket() {
|
|
return renderResumePacket(snapshot.ledger).trim();
|
|
}
|
|
```
|
|
|
|
- [ ] **Step 4: Run tests to verify they pass**
|
|
|
|
Run: `cd /home/dev/pi-packages/pi-context-manager/.worktree/context-manager-prune-compact && npm test -- src/prune.test.ts src/runtime.test.ts src/summaries.test.ts`
|
|
Expected: PASS for the new pruning and lean-resume assertions.
|
|
|
|
- [ ] **Step 5: Commit**
|
|
|
|
```bash
|
|
cd /home/dev/pi-packages/pi-context-manager/.worktree/context-manager-prune-compact
|
|
git add src/prune.ts src/prune.test.ts src/summaries.ts src/summaries.test.ts src/runtime.ts src/runtime.test.ts
|
|
git commit -m "fix: prune turns and slim resume packets"
|
|
```
|
|
|
|
### Task 2: Context filtering, early compaction, and footer removal
|
|
|
|
**Files:**
|
|
- Modify: `index.ts`
|
|
- Modify: `src/extension.test.ts`
|
|
|
|
- [ ] **Step 1: Write the failing extension tests**
|
|
|
|
```ts
|
|
test("context filters raw compaction and branch summary messages", async () => {
|
|
const result = await harness.handlers.get("context")?.({
|
|
type: "context",
|
|
messages: [
|
|
{ role: "compactionSummary", summary: "## Key Decisions\n- noisy raw handoff", timestamp: 1 },
|
|
{ role: "branchSummary", summary: "# Handoff\n- noisy raw branch", timestamp: 2 },
|
|
createUserMessage("continue", 3),
|
|
],
|
|
}, harness.ctx);
|
|
|
|
assert.ok(result.messages.every((message: any) => message.role !== "compactionSummary" && message.role !== "branchSummary"));
|
|
});
|
|
|
|
test("turn_end triggers early compaction at red zone without footer status writes", async () => {
|
|
const harness = createHarness([], { usageTokens: 150_000 });
|
|
await harness.handlers.get("session_start")?.({ type: "session_start" }, harness.ctx);
|
|
await harness.handlers.get("turn_end")?.({ type: "turn_end", turnIndex: 1, message: createAssistantMessage("done", 5), toolResults: [] }, harness.ctx);
|
|
assert.equal(harness.compactions.length, 1);
|
|
assert.equal(harness.statuses.length, 0);
|
|
});
|
|
```
|
|
|
|
- [ ] **Step 2: Run tests to verify they fail**
|
|
|
|
Run: `cd /home/dev/pi-packages/pi-context-manager/.worktree/context-manager-prune-compact && npm test -- src/extension.test.ts`
|
|
Expected: FAIL because summary messages are still passed through, early compaction is absent, and status writes still occur.
|
|
|
|
- [ ] **Step 3: Write the minimal implementation**
|
|
|
|
```ts
|
|
// index.ts
|
|
// - Filter raw compactionSummary / branchSummary messages before pruning.
|
|
// - Remove ctx.ui.setStatus(...) calls.
|
|
// - After observeTokens on turn_end, call ctx.compact() once when snapshot.lastZone is red/compact.
|
|
// - Reset the latch on session_compact or when usage drops below red.
|
|
```
|
|
|
|
- [ ] **Step 4: Run tests to verify they pass**
|
|
|
|
Run: `cd /home/dev/pi-packages/pi-context-manager/.worktree/context-manager-prune-compact && npm test -- src/extension.test.ts`
|
|
Expected: PASS for summary filtering, early compaction, and no-footer assertions.
|
|
|
|
- [ ] **Step 5: Commit**
|
|
|
|
```bash
|
|
cd /home/dev/pi-packages/pi-context-manager/.worktree/context-manager-prune-compact
|
|
git add index.ts src/extension.test.ts
|
|
git commit -m "fix: compact earlier and drop status noise"
|
|
```
|
|
|
|
### Task 3: Full verification and cleanup
|
|
|
|
**Files:**
|
|
- Modify: `src/config.ts` (only if zone tightening needs a small recent-turn adjustment)
|
|
- Modify: `src/config.test.ts` (only if policy values change)
|
|
|
|
- [ ] **Step 1: Run the full test suite**
|
|
|
|
```bash
|
|
cd /home/dev/pi-packages/pi-context-manager/.worktree/context-manager-prune-compact
|
|
npm test
|
|
```
|
|
|
|
- [ ] **Step 2: If any failures remain, make the minimal fix and rerun**
|
|
|
|
```ts
|
|
// Only patch the failing behavior surfaced by the full test run.
|
|
// Do not broaden scope beyond pruning, lean resume injection, summary filtering,
|
|
// early compaction, and footer removal.
|
|
```
|
|
|
|
- [ ] **Step 3: Verify the final green run**
|
|
|
|
Run: `cd /home/dev/pi-packages/pi-context-manager/.worktree/context-manager-prune-compact && npm test`
|
|
Expected: `# fail 0`
|
|
|
|
- [ ] **Step 4: Commit final cleanup if needed**
|
|
|
|
```bash
|
|
cd /home/dev/pi-packages/pi-context-manager/.worktree/context-manager-prune-compact
|
|
git add index.ts src/*.ts docs/plans/2026-04-12-context-manager-implementation.md
|
|
git commit -m "test: cover context pressure behavior"
|
|
```
|