@gotgenes/pi-subagents 10.0.0 → 10.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +36 -0
- package/docs/architecture/architecture.md +53 -142
- package/docs/architecture/history/phase-14-strip-policy.md +49 -0
- package/docs/plans/0227-evolve-agent-record-into-agent.md +322 -0
- package/docs/plans/0239-collapse-filter-active-tools.md +217 -0
- package/docs/retro/0227-evolve-agent-record-into-agent.md +37 -0
- package/docs/retro/0239-collapse-filter-active-tools.md +70 -0
- package/docs/retro/0242-rename-agent-tool-to-subagent.md +45 -0
- package/package.json +1 -1
- package/src/lifecycle/agent-manager.ts +39 -89
- package/src/lifecycle/agent-runner.ts +15 -38
- package/src/lifecycle/{agent-record.ts → agent.ts} +68 -10
- package/src/lifecycle/execution-state.ts +2 -2
- package/src/observation/notification.ts +8 -8
- package/src/observation/record-observer.ts +7 -7
- package/src/service/service-adapter.ts +8 -8
- package/src/session/session-config.ts +6 -16
- package/src/tools/agent-tool.ts +4 -4
- package/src/tools/background-spawner.ts +2 -2
- package/src/tools/foreground-runner.ts +4 -4
- package/src/tools/get-result-tool.ts +2 -2
- package/src/tools/steer-tool.ts +4 -5
- package/src/types.ts +1 -1
- package/src/ui/agent-creation-wizard.ts +2 -2
- package/src/ui/agent-menu.ts +5 -5
- package/src/ui/conversation-viewer.ts +3 -3
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
---
|
|
2
|
+
issue: 227
|
|
3
|
+
issue_title: "Evolve AgentRecord into Agent with behavior (Phase 15, Step 1)"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Evolve AgentRecord into Agent with behavior
|
|
7
|
+
|
|
8
|
+
## Problem Statement
|
|
9
|
+
|
|
10
|
+
`AgentRecord` is an anemic domain model — it holds identity, status transitions, and stats but no behavior.
|
|
11
|
+
`AgentManager` reaches into records 37 times, performing work that belongs on the agent:
|
|
12
|
+
|
|
13
|
+
- **abort**: `AgentManager.abort()` checks `record.status`, calls `record.abortController?.abort()`, calls `record.markStopped()` — this is the agent aborting itself, but the logic lives on the manager.
|
|
14
|
+
- **pending steers**: per-agent steer buffers live in a manager-level `Map<string, string[]>`, not on the agent.
|
|
15
|
+
- **steer flushing**: `flushPendingSteers(id, session)` iterates the manager map — should be `agent.flushPendingSteers(session)`.
|
|
16
|
+
- **worktree setup**: `setupWorktree()` creates a worktree and attaches it to the record — the agent should set up its own worktree.
|
|
17
|
+
|
|
18
|
+
## Goals
|
|
19
|
+
|
|
20
|
+
- Move per-agent behavior (`abort`, `queueSteer`/`flushPendingSteers`, `setupWorktree`) from `AgentManager` to the agent.
|
|
21
|
+
- `AgentManager` delegates to agents via Tell-Don't-Ask instead of reaching into records.
|
|
22
|
+
- Rename `AgentRecord` → `Agent`, `AgentRecordStatus` → `AgentStatus`, `AgentRecordInit` → `AgentInit` across the codebase.
|
|
23
|
+
- All changes are internal — the public `SubagentsService` API (`service.ts`) is unaffected.
|
|
24
|
+
|
|
25
|
+
## Non-Goals
|
|
26
|
+
|
|
27
|
+
- **`RunHandle` ownership** — moves to `Agent` in #228, not here.
|
|
28
|
+
- **Async `startAgent`** — deferred to #228.
|
|
29
|
+
- **`onSessionCreated` observer** — deferred to #229.
|
|
30
|
+
- **`ConcurrencyQueue` extraction** — deferred to #230.
|
|
31
|
+
Queue removal logic stays on `AgentManager.abort()` until then.
|
|
32
|
+
- **Relay deps** — deferred to #231.
|
|
33
|
+
- **Resume unification** — deferred to #232.
|
|
34
|
+
|
|
35
|
+
## Background
|
|
36
|
+
|
|
37
|
+
### Relevant modules
|
|
38
|
+
|
|
39
|
+
| Module | Responsibility | Relationship to this change |
|
|
40
|
+
| ------------------------------------------ | ------------------------------------------------------- | ---------------------------------------------------------------------------------- |
|
|
41
|
+
| `src/lifecycle/agent-record.ts` (201 LOC) | Status state machine, stats accumulation | Gains behavior methods, renames to `agent.ts` |
|
|
42
|
+
| `src/lifecycle/agent-manager.ts` (541 LOC) | Agent collection, spawn, abort, queue, steer buffering | Loses private methods, delegates to agent |
|
|
43
|
+
| `src/lifecycle/worktree.ts` | `WorktreeManager` interface and git worktree operations | Agent calls `worktrees.create()` directly |
|
|
44
|
+
| `src/lifecycle/worktree-state.ts` | Per-agent worktree lifecycle state | Already attached to agent — `setupWorktree` formalizes this |
|
|
45
|
+
| `src/tools/steer-tool.ts` | LLM-facing steer tool | Calls `record.queueSteer()` directly instead of `manager.queueSteer()` |
|
|
46
|
+
| `src/service/service-adapter.ts` | Cross-extension API adapter | Calls `record.queueSteer()` directly; `queueSteer` removed from `AgentManagerLike` |
|
|
47
|
+
| `src/observation/record-observer.ts` | Session event → agent stats accumulation | Import rename only |
|
|
48
|
+
| `src/types.ts` | Internal re-exports | Re-export updates |
|
|
49
|
+
| `test/helpers/make-record.ts` | Shared test factory | Renames to `make-agent.ts`, factory → `createTestAgent()` |
|
|
50
|
+
|
|
51
|
+
### Constraints
|
|
52
|
+
|
|
53
|
+
- The public export from `package.json` is `"./src/service.ts"` only — `AgentRecord` is internal, so the rename is not breaking for consumers.
|
|
54
|
+
- `WorktreeManager` is injected via the manager's constructor — `Agent.setupWorktree()` receives it as a parameter (no new constructor dependency).
|
|
55
|
+
- Queue removal in `abort()` stays on `AgentManager` because the queue is manager-owned until #230 extracts `ConcurrencyQueue`.
|
|
56
|
+
|
|
57
|
+
## Design Overview
|
|
58
|
+
|
|
59
|
+
### New methods on Agent
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
class Agent {
|
|
63
|
+
// Existing: markRunning, markCompleted, markAborted, markSteered, markError, markStopped,
|
|
64
|
+
// incrementToolUses, addUsage, incrementCompactions, resetForResume
|
|
65
|
+
|
|
66
|
+
// --- New behavior ---
|
|
67
|
+
|
|
68
|
+
/** Buffer a steer message for delivery once the session is ready. */
|
|
69
|
+
queueSteer(message: string): void;
|
|
70
|
+
|
|
71
|
+
/** Flush buffered steers to the session and clear the buffer. */
|
|
72
|
+
flushPendingSteers(session: AgentSession): void;
|
|
73
|
+
|
|
74
|
+
/** Abort a running agent: fire AbortController, transition to stopped. */
|
|
75
|
+
abort(): boolean;
|
|
76
|
+
|
|
77
|
+
/** Create a worktree for isolated execution. Throws if impossible. */
|
|
78
|
+
setupWorktree(worktrees: WorktreeManager, isolation: IsolationMode | undefined): string | undefined;
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Steer buffering moves to agent
|
|
83
|
+
|
|
84
|
+
Before: `AgentManager` owns `pendingSteers: Map<string, string[]>` and exposes `queueSteer(id, msg)`.
|
|
85
|
+
After: each `Agent` owns `private pendingSteers: string[] = []`.
|
|
86
|
+
Callers that already hold a record reference (steer tool, service adapter) call `agent.queueSteer(msg)` directly — the manager's `queueSteer` method and the `pendingSteers` map are removed.
|
|
87
|
+
|
|
88
|
+
Consumer call-site (steer tool):
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
// Before:
|
|
92
|
+
this.manager.queueSteer(record.id, params.message);
|
|
93
|
+
// After:
|
|
94
|
+
record.queueSteer(params.message);
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Abort moves to agent
|
|
98
|
+
|
|
99
|
+
`Agent.abort()` encapsulates the running-check + controller.abort + markStopped sequence:
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
abort(): boolean {
|
|
103
|
+
if (this._status !== "running") return false;
|
|
104
|
+
this.abortController?.abort();
|
|
105
|
+
this.markStopped();
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
`AgentManager.abort(id)` retains queue-removal logic (queue is manager-owned until #230) and delegates the running case to `agent.abort()`.
|
|
111
|
+
`AgentManager.abortAll()` calls `agent.abort()` for running agents.
|
|
112
|
+
|
|
113
|
+
### Worktree setup moves to agent
|
|
114
|
+
|
|
115
|
+
`Agent.setupWorktree(worktrees, isolation)` replaces `AgentManager.setupWorktree(id, record, isolation)`.
|
|
116
|
+
The agent creates the worktree, sets `this.worktreeState`, and returns the worktree path.
|
|
117
|
+
The error message for impossible worktree creation stays identical.
|
|
118
|
+
|
|
119
|
+
### Rename strategy
|
|
120
|
+
|
|
121
|
+
The rename (`AgentRecord` → `Agent`) is the final step — a purely mechanical search-and-replace with no behavior change.
|
|
122
|
+
This keeps behavior-adding commits small and reviewable, then consolidates the rename noise into one commit.
|
|
123
|
+
|
|
124
|
+
Files affected by the rename:
|
|
125
|
+
|
|
126
|
+
| Layer | Files |
|
|
127
|
+
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
128
|
+
| Source (lifecycle) | `agent-record.ts` → `agent.ts`, `agent-manager.ts`, `execution-state.ts` |
|
|
129
|
+
| Source (observation) | `record-observer.ts`, `notification.ts` |
|
|
130
|
+
| Source (tools) | `agent-tool.ts`, `steer-tool.ts`, `get-result-tool.ts`, `background-spawner.ts`, `foreground-runner.ts` |
|
|
131
|
+
| Source (UI) | `agent-menu.ts`, `agent-creation-wizard.ts`, `conversation-viewer.ts` |
|
|
132
|
+
| Source (service) | `service-adapter.ts` |
|
|
133
|
+
| Source (types) | `types.ts` |
|
|
134
|
+
| Tests | `agent-record.test.ts` → `agent.test.ts`, `agent-manager.test.ts`, `record-observer.test.ts`, `steer-tool.test.ts`, `get-result-tool.test.ts`, `service-adapter.test.ts` |
|
|
135
|
+
| Test helpers | `make-record.ts` → `make-agent.ts` |
|
|
136
|
+
|
|
137
|
+
## Module-Level Changes
|
|
138
|
+
|
|
139
|
+
### `src/lifecycle/agent-record.ts` → `src/lifecycle/agent.ts`
|
|
140
|
+
|
|
141
|
+
1. Add `private pendingSteers: string[] = []` field.
|
|
142
|
+
2. Add `queueSteer(message: string): void` — pushes to `pendingSteers`.
|
|
143
|
+
3. Add `flushPendingSteers(session: AgentSession): void` — iterates buffer, calls `session.steer()`, clears array.
|
|
144
|
+
4. Add `abort(): boolean` — if running, fires controller and marks stopped.
|
|
145
|
+
5. Add `setupWorktree(worktrees: WorktreeManager, isolation: IsolationMode | undefined): string | undefined` — creates worktree, sets `worktreeState`, returns path.
|
|
146
|
+
6. Add import for `WorktreeState`, `WorktreeManager`, `IsolationMode`.
|
|
147
|
+
7. Rename class `AgentRecord` → `Agent`, type `AgentRecordStatus` → `AgentStatus`, interface `AgentRecordInit` → `AgentInit`.
|
|
148
|
+
|
|
149
|
+
### `src/lifecycle/agent-manager.ts`
|
|
150
|
+
|
|
151
|
+
1. Remove `private pendingSteers = new Map<string, string[]>()`.
|
|
152
|
+
2. Remove `queueSteer(id, message)` public method.
|
|
153
|
+
3. Remove `private flushPendingSteers(id, session)` method.
|
|
154
|
+
4. In `startAgent`'s `onSessionCreated` callback: replace `this.flushPendingSteers(id, session)` with `record.flushPendingSteers(session)`.
|
|
155
|
+
5. Remove `private setupWorktree(id, record, isolation)` method.
|
|
156
|
+
6. In `startAgent`: replace `this.setupWorktree(id, record, options.isolation)` with `record.setupWorktree(this.worktrees, options.isolation)`.
|
|
157
|
+
7. Simplify `abort(id)`: delegate running case to `record.abort()`.
|
|
158
|
+
8. Simplify `abortAll()`: call `record.abort()` for running agents.
|
|
159
|
+
9. In `removeRecord`: remove `this.pendingSteers.delete(id)`.
|
|
160
|
+
10. Update imports: `AgentRecord` → `Agent`, `AgentRecordInit` → `AgentInit` (if used).
|
|
161
|
+
|
|
162
|
+
### `src/tools/steer-tool.ts`
|
|
163
|
+
|
|
164
|
+
1. Remove `queueSteer` from `SteerToolManager` interface.
|
|
165
|
+
2. Replace `this.manager.queueSteer(record.id, params.message)` with `record.queueSteer(params.message)`.
|
|
166
|
+
3. Update import: `AgentRecord` → `Agent`.
|
|
167
|
+
|
|
168
|
+
### `src/service/service-adapter.ts`
|
|
169
|
+
|
|
170
|
+
1. Remove `queueSteer` from `AgentManagerLike` interface.
|
|
171
|
+
2. In `steer()`: replace `this.manager.queueSteer(id, message)` with `record.queueSteer(message)` and return `true`.
|
|
172
|
+
3. Update import: `AgentRecord` → `Agent`.
|
|
173
|
+
|
|
174
|
+
### `src/types.ts`
|
|
175
|
+
|
|
176
|
+
1. Update re-export: `AgentRecord` → `Agent`, source path `#src/lifecycle/agent-record` → `#src/lifecycle/agent`.
|
|
177
|
+
|
|
178
|
+
### `src/observation/record-observer.ts`
|
|
179
|
+
|
|
180
|
+
1. Update import and parameter types: `AgentRecord` → `Agent`.
|
|
181
|
+
|
|
182
|
+
### `src/observation/notification.ts`
|
|
183
|
+
|
|
184
|
+
1. Update import and parameter types: `AgentRecord` → `Agent`.
|
|
185
|
+
|
|
186
|
+
### `src/tools/*.ts` (agent-tool, get-result-tool, background-spawner, foreground-runner)
|
|
187
|
+
|
|
188
|
+
1. Update imports and type annotations: `AgentRecord` → `Agent`.
|
|
189
|
+
|
|
190
|
+
### `src/ui/*.ts` (agent-menu, agent-creation-wizard, conversation-viewer)
|
|
191
|
+
|
|
192
|
+
1. Update imports and type annotations: `AgentRecord` → `Agent`.
|
|
193
|
+
|
|
194
|
+
### `test/helpers/make-record.ts` → `test/helpers/make-agent.ts`
|
|
195
|
+
|
|
196
|
+
1. Rename file.
|
|
197
|
+
2. Update imports: `AgentRecord` → `Agent`, `AgentRecordInit` → `AgentInit`.
|
|
198
|
+
3. Rename factory: `createTestRecord` → `createTestAgent`.
|
|
199
|
+
4. Update return type annotation.
|
|
200
|
+
|
|
201
|
+
### `test/lifecycle/agent-record.test.ts` → `test/lifecycle/agent.test.ts`
|
|
202
|
+
|
|
203
|
+
1. Rename file.
|
|
204
|
+
2. Update import: `AgentRecord` → `Agent`.
|
|
205
|
+
3. Update all `describe` block names and `new AgentRecord(...)` calls.
|
|
206
|
+
4. Add new test blocks for `queueSteer`, `flushPendingSteers`, `abort`, `setupWorktree`.
|
|
207
|
+
|
|
208
|
+
### `test/lifecycle/agent-manager.test.ts`
|
|
209
|
+
|
|
210
|
+
1. Remove tests for `AgentManager.queueSteer` (behavior moved to agent).
|
|
211
|
+
2. Update `abort()` tests to verify delegation.
|
|
212
|
+
3. Update imports if `AgentRecord` type is referenced.
|
|
213
|
+
|
|
214
|
+
### `test/tools/steer-tool.test.ts`
|
|
215
|
+
|
|
216
|
+
1. Remove `queueSteer` from mock manager.
|
|
217
|
+
2. Update "session not ready" test to verify `record.queueSteer()` is called.
|
|
218
|
+
|
|
219
|
+
### `test/service/service-adapter.test.ts`
|
|
220
|
+
|
|
221
|
+
1. Remove `queueSteer` from mock managers.
|
|
222
|
+
2. Update steer tests to verify `record.queueSteer()`.
|
|
223
|
+
|
|
224
|
+
### `packages/pi-subagents/docs/architecture/architecture.md`
|
|
225
|
+
|
|
226
|
+
1. Update file listing: `agent-record.ts` → `agent.ts`.
|
|
227
|
+
2. Update `AgentRecordInit` reference in interface width table.
|
|
228
|
+
|
|
229
|
+
## Test Impact Analysis
|
|
230
|
+
|
|
231
|
+
### New unit tests enabled by the extraction
|
|
232
|
+
|
|
233
|
+
1. **`Agent.queueSteer()` / `Agent.flushPendingSteers()`** — isolated tests for steer buffering without needing a full `AgentManager` setup.
|
|
234
|
+
Previously the steer buffering was only testable via `AgentManager` integration tests.
|
|
235
|
+
2. **`Agent.abort()`** — isolated tests for the abort state machine (running → stopped, not-running → no-op) without needing manager scaffolding.
|
|
236
|
+
3. **`Agent.setupWorktree()`** — isolated tests for worktree creation and error handling with a mock `WorktreeManager`, without full spawn infrastructure.
|
|
237
|
+
|
|
238
|
+
### Existing tests that become redundant
|
|
239
|
+
|
|
240
|
+
1. `AgentManager — queueSteer` tests — the behavior is now tested directly on `Agent`.
|
|
241
|
+
The manager no longer has a `queueSteer` method.
|
|
242
|
+
2. Parts of `AgentManager — abort` tests that verify controller.abort + markStopped — these are now `Agent.abort()` tests.
|
|
243
|
+
The manager abort tests should focus on queue-removal logic and delegation.
|
|
244
|
+
|
|
245
|
+
### Existing tests that must stay
|
|
246
|
+
|
|
247
|
+
1. `AgentManager — abort` tests for the "queued" case (queue removal is still manager-owned).
|
|
248
|
+
2. `AgentManager — abortAll` tests (orchestrates both queue clearing and agent abort).
|
|
249
|
+
3. All `AgentManager — spawn/spawnAndWait` tests — the spawn flow still lives on the manager.
|
|
250
|
+
4. `steer-tool` and `service-adapter` tests for the steer path — updated to verify the new call pattern.
|
|
251
|
+
|
|
252
|
+
## TDD Order
|
|
253
|
+
|
|
254
|
+
1. **Red/Green: add `queueSteer()` and `flushPendingSteers()` to `AgentRecord`**
|
|
255
|
+
- Add tests in `agent-record.test.ts` for buffering and flushing steers.
|
|
256
|
+
- Implement the methods on `AgentRecord`.
|
|
257
|
+
- Commit: `feat(pi-subagents): add steer buffering to AgentRecord`
|
|
258
|
+
|
|
259
|
+
2. **Refactor: delegate steer buffering from manager to agent**
|
|
260
|
+
- Remove `pendingSteers` map, `queueSteer()`, `flushPendingSteers()` from `AgentManager`.
|
|
261
|
+
- In `startAgent`'s `onSessionCreated`: call `record.flushPendingSteers(session)`.
|
|
262
|
+
- In `removeRecord`: remove `pendingSteers.delete(id)`.
|
|
263
|
+
- Update `steer-tool.ts`: remove `queueSteer` from `SteerToolManager`, call `record.queueSteer()`.
|
|
264
|
+
- Update `service-adapter.ts`: remove `queueSteer` from `AgentManagerLike`, call `record.queueSteer()`.
|
|
265
|
+
- Remove `AgentManager — queueSteer` tests; update steer-tool and service-adapter tests.
|
|
266
|
+
- Run `pnpm run check` to verify no type errors.
|
|
267
|
+
- Commit: `refactor(pi-subagents): delegate steer buffering from manager to agent`
|
|
268
|
+
|
|
269
|
+
3. **Red/Green: add `abort()` to `AgentRecord`**
|
|
270
|
+
- Add tests in `agent-record.test.ts`: running → aborts and returns true; non-running → returns false; no controller → still marks stopped.
|
|
271
|
+
- Implement the method.
|
|
272
|
+
- Commit: `feat(pi-subagents): add abort() to AgentRecord`
|
|
273
|
+
|
|
274
|
+
4. **Refactor: delegate abort from manager to agent**
|
|
275
|
+
- Simplify `AgentManager.abort()`: queued case stays, running case delegates to `record.abort()`.
|
|
276
|
+
- Simplify `AgentManager.abortAll()`: call `record.abort()` for running agents.
|
|
277
|
+
- Update manager abort tests to focus on queue removal and delegation.
|
|
278
|
+
- Commit: `refactor(pi-subagents): delegate abort from manager to agent`
|
|
279
|
+
|
|
280
|
+
5. **Red/Green: add `setupWorktree()` to `AgentRecord`**
|
|
281
|
+
- Add tests in `agent-record.test.ts`: non-worktree returns undefined; worktree created → sets `worktreeState` and returns path; creation fails → throws.
|
|
282
|
+
- Implement the method (import `WorktreeState`, `WorktreeManager`, `IsolationMode`).
|
|
283
|
+
- Commit: `feat(pi-subagents): add setupWorktree() to AgentRecord`
|
|
284
|
+
|
|
285
|
+
6. **Refactor: delegate worktree setup from manager to agent**
|
|
286
|
+
- Remove `private setupWorktree()` from `AgentManager`.
|
|
287
|
+
- In `startAgent`: replace `this.setupWorktree(id, record, options.isolation)` with `record.setupWorktree(this.worktrees, options.isolation)`.
|
|
288
|
+
- Update any tests that verify worktree setup delegation.
|
|
289
|
+
- Commit: `refactor(pi-subagents): delegate worktree setup from manager to agent`
|
|
290
|
+
|
|
291
|
+
7. **Rename `AgentRecord` → `Agent` across codebase**
|
|
292
|
+
- Rename `src/lifecycle/agent-record.ts` → `src/lifecycle/agent.ts`.
|
|
293
|
+
- Rename class `AgentRecord` → `Agent`, type `AgentRecordStatus` → `AgentStatus`, interface `AgentRecordInit` → `AgentInit`.
|
|
294
|
+
- Update all source imports and type references (~20 source files).
|
|
295
|
+
- Rename `test/lifecycle/agent-record.test.ts` → `test/lifecycle/agent.test.ts`.
|
|
296
|
+
- Rename `test/helpers/make-record.ts` → `test/helpers/make-agent.ts`, factory `createTestRecord` → `createTestAgent`.
|
|
297
|
+
- Update all test imports and references (~10 test files).
|
|
298
|
+
- Rename `subscribeRecordObserver` → `subscribeAgentObserver` and `RecordObserverOptions` → `AgentObserverOptions` in `record-observer.ts`.
|
|
299
|
+
- Run `pnpm run check` and full test suite.
|
|
300
|
+
- Commit: `refactor(pi-subagents): rename AgentRecord to Agent`
|
|
301
|
+
|
|
302
|
+
8. **Update architecture docs**
|
|
303
|
+
- Update `docs/architecture/architecture.md` file listing: `agent-record.ts` → `agent.ts`.
|
|
304
|
+
- Update `AgentRecordInit` → `AgentInit` in the interface width table.
|
|
305
|
+
- Update the Phase 15 Step 1 entry to reflect completion.
|
|
306
|
+
- Commit: `docs(pi-subagents): update architecture for Agent rename`
|
|
307
|
+
|
|
308
|
+
## Risks and Mitigations
|
|
309
|
+
|
|
310
|
+
1. **Large rename diff in step 7** — The rename touches ~30 files.
|
|
311
|
+
Mitigated by making it a purely mechanical change (no behavior change) in a dedicated commit, so reviewers can verify it's a clean rename.
|
|
312
|
+
2. **Queue-removal leaks into agent** — `Agent.abort()` must NOT remove from the manager's queue (that's #230's concern).
|
|
313
|
+
Mitigated by scoping `Agent.abort()` to only handle the running case; `AgentManager.abort()` retains queue removal.
|
|
314
|
+
3. **Interface changes cascade** — Removing `queueSteer` from `SteerToolManager` and `AgentManagerLike` requires updating test mocks.
|
|
315
|
+
Mitigated by handling interface changes and test updates in the same step (step 2).
|
|
316
|
+
4. **Test factory rename ripple** — `createTestRecord` → `createTestAgent` touches many test files.
|
|
317
|
+
Mitigated by including this in the rename step (step 7), which is already a mechanical change.
|
|
318
|
+
|
|
319
|
+
## Open Questions
|
|
320
|
+
|
|
321
|
+
None — the issue's proposed change is unambiguous and scoped.
|
|
322
|
+
`RunHandle` ownership and other Phase 15 steps are explicitly deferred to their own issues.
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
---
|
|
2
|
+
issue: 239
|
|
3
|
+
issue_title: "Collapse filterActiveTools to recursion guard (Phase 14, Step 3)"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Collapse `filterActiveTools` to recursion guard
|
|
7
|
+
|
|
8
|
+
## Problem Statement
|
|
9
|
+
|
|
10
|
+
With `disallowed_tools` (#237) and `extensions` filtering (#238) removed, `filterActiveTools` retains two branches that no longer justify a config bag or two-pass pre-bind/post-bind dance:
|
|
11
|
+
|
|
12
|
+
1. The `extensions === false` early return — a passthrough that belongs at the callsite.
|
|
13
|
+
2. The `EXCLUDED_TOOL_NAMES` recursion guard — the function's sole essential purpose.
|
|
14
|
+
|
|
15
|
+
The `builtinToolNameSet` membership check always returns `true` now (no string-array `extensions` filtering remains), making it dead logic.
|
|
16
|
+
`ToolFilterConfig` exists only to carry two fields (`toolNames`, `extensions`) through the assembler→runner boundary, but after this change they travel independently: `toolNames` feeds `createSession`, `extensions` feeds the resource loader's `noExtensions` flag, and neither is consumed by the filter function.
|
|
17
|
+
|
|
18
|
+
## Goals
|
|
19
|
+
|
|
20
|
+
- Reduce `filterActiveTools` to a one-liner: filter out `EXCLUDED_TOOL_NAMES`.
|
|
21
|
+
- Delete the `ToolFilterConfig` interface.
|
|
22
|
+
- Flatten `SessionConfig.toolFilter` back into two top-level fields: `toolNames` and `extensions`.
|
|
23
|
+
- Remove the pre-bind filter call — without denylist/allowlist logic, filtering before `bindExtensions` serves no purpose.
|
|
24
|
+
- Keep a single post-bind filter call for the recursion guard.
|
|
25
|
+
- Update tests to reflect the simplified flow.
|
|
26
|
+
|
|
27
|
+
## Non-Goals
|
|
28
|
+
|
|
29
|
+
- Removing `extensions` from `SessionConfig` entirely — it's still needed for the `noExtensions` flag on the resource loader.
|
|
30
|
+
- Renaming `EXCLUDED_TOOL_NAMES` or moving it to a separate module.
|
|
31
|
+
- Phase 15 domain model changes (#227–#232) — those operate on the simplified codebase this change produces.
|
|
32
|
+
|
|
33
|
+
## Background
|
|
34
|
+
|
|
35
|
+
`filterActiveTools` was extracted as part of Phase 10 (#168) to group `toolNames`, `disallowedSet`, and `extensions` into a `ToolFilterConfig` bag.
|
|
36
|
+
Issue #237 removed `disallowedSet`; #238 narrowed `extensions` from `true | string[] | false` to `boolean`.
|
|
37
|
+
Both are now closed.
|
|
38
|
+
|
|
39
|
+
The two-pass filter dance (Patch 2, RepOne #443) exists to catch extension-registered tools that join the active set during `bindExtensions`.
|
|
40
|
+
With the only remaining filter logic being the `EXCLUDED_TOOL_NAMES` guard, a single post-bind pass suffices.
|
|
41
|
+
|
|
42
|
+
Current `filterActiveTools` body:
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
function filterActiveTools(
|
|
46
|
+
activeTools: string[],
|
|
47
|
+
config: ToolFilterConfig,
|
|
48
|
+
): string[] {
|
|
49
|
+
const { toolNames, extensions } = config;
|
|
50
|
+
if (!extensions) {
|
|
51
|
+
return activeTools;
|
|
52
|
+
}
|
|
53
|
+
const builtinToolNameSet = new Set(toolNames);
|
|
54
|
+
return activeTools.filter((t) => {
|
|
55
|
+
if (EXCLUDED_TOOL_NAMES.includes(t)) return false;
|
|
56
|
+
if (builtinToolNameSet.has(t)) return true;
|
|
57
|
+
return true;
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
The `builtinToolNameSet` check is dead — both branches return `true`.
|
|
63
|
+
The `!extensions` early return belongs at the callsite.
|
|
64
|
+
|
|
65
|
+
### Affected files
|
|
66
|
+
|
|
67
|
+
- `src/lifecycle/agent-runner.ts` — `filterActiveTools`, pre-bind/post-bind calls, `ToolFilterConfig` import
|
|
68
|
+
- `src/session/session-config.ts` — `ToolFilterConfig` interface, `SessionConfig.toolFilter` field, assembler return literal
|
|
69
|
+
- `test/lifecycle/agent-runner-extension-tools.test.ts` — pre-bind/post-bind assertions
|
|
70
|
+
- `test/session/session-config.test.ts` — `toolFilter.*` assertions
|
|
71
|
+
- `test/lifecycle/agent-runner.test.ts` — session mock (`setActiveToolsByName` calls)
|
|
72
|
+
- `docs/architecture/architecture.md` — references to `ToolFilterConfig`, `filterActiveTools`, Phase 14 status
|
|
73
|
+
|
|
74
|
+
## Design Overview
|
|
75
|
+
|
|
76
|
+
### `filterActiveTools` simplification
|
|
77
|
+
|
|
78
|
+
The function reduces to:
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
function filterActiveTools(activeTools: string[]): string[] {
|
|
82
|
+
return activeTools.filter((t) => !EXCLUDED_TOOL_NAMES.includes(t));
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
No config parameter.
|
|
87
|
+
The `extensions === false` guard moves to the callsite: `if (cfg.extensions)`.
|
|
88
|
+
|
|
89
|
+
### `SessionConfig` flattening
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
export interface SessionConfig {
|
|
93
|
+
effectiveCwd: string;
|
|
94
|
+
systemPrompt: string;
|
|
95
|
+
toolNames: string[]; // was toolFilter.toolNames
|
|
96
|
+
extensions: boolean; // was toolFilter.extensions
|
|
97
|
+
model: unknown;
|
|
98
|
+
thinkingLevel: ThinkingLevel | undefined;
|
|
99
|
+
noSkills: boolean;
|
|
100
|
+
extras: PromptExtras;
|
|
101
|
+
agentMaxTurns: number | undefined;
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### `runAgent` callsite changes
|
|
106
|
+
|
|
107
|
+
Before (two-pass):
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
if (cfg.toolFilter.extensions) {
|
|
111
|
+
const filtered = filterActiveTools(session.getActiveToolNames(), cfg.toolFilter);
|
|
112
|
+
session.setActiveToolsByName(filtered);
|
|
113
|
+
}
|
|
114
|
+
// ... bindExtensions ...
|
|
115
|
+
if (cfg.toolFilter.extensions) {
|
|
116
|
+
const refiltered = filterActiveTools(session.getActiveToolNames(), cfg.toolFilter);
|
|
117
|
+
session.setActiveToolsByName(refiltered);
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
After (single post-bind pass):
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
// ... bindExtensions ...
|
|
125
|
+
if (cfg.extensions) {
|
|
126
|
+
const filtered = filterActiveTools(session.getActiveToolNames());
|
|
127
|
+
session.setActiveToolsByName(filtered);
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Other `cfg.toolFilter.*` references update to `cfg.toolNames` and `cfg.extensions`.
|
|
132
|
+
|
|
133
|
+
## Module-Level Changes
|
|
134
|
+
|
|
135
|
+
### `src/session/session-config.ts`
|
|
136
|
+
|
|
137
|
+
1. Delete the `ToolFilterConfig` interface.
|
|
138
|
+
2. Replace `toolFilter: ToolFilterConfig` on `SessionConfig` with two flat fields: `toolNames: string[]` and `extensions: boolean`.
|
|
139
|
+
3. Update the return literal in `assembleSessionConfig` from `toolFilter: { toolNames, extensions }` to `toolNames, extensions`.
|
|
140
|
+
4. Update the JSDoc on `SessionConfig` — remove "Tool filtering cluster" comment.
|
|
141
|
+
|
|
142
|
+
### `src/lifecycle/agent-runner.ts`
|
|
143
|
+
|
|
144
|
+
1. Remove the `ToolFilterConfig` import.
|
|
145
|
+
2. Simplify `filterActiveTools` to `(activeTools: string[]) => string[]` — just the `EXCLUDED_TOOL_NAMES` filter.
|
|
146
|
+
3. Remove the pre-bind filter block (the first `if (cfg.toolFilter.extensions)` block).
|
|
147
|
+
4. Update the post-bind filter block: `cfg.toolFilter.extensions` → `cfg.extensions`, remove the config argument from the `filterActiveTools` call.
|
|
148
|
+
5. Update `noExtensions: !cfg.toolFilter.extensions` → `noExtensions: !cfg.extensions`.
|
|
149
|
+
6. Update `tools: cfg.toolFilter.toolNames` → `tools: cfg.toolNames`.
|
|
150
|
+
7. Update or remove the Patch 2 comments — the two-pass dance is gone; the remaining call is just a recursion guard.
|
|
151
|
+
|
|
152
|
+
### `test/lifecycle/agent-runner-extension-tools.test.ts`
|
|
153
|
+
|
|
154
|
+
1. Remove the pre-bind/post-bind ordering assertions — there is only one post-bind call now.
|
|
155
|
+
2. Update `setActiveToolsByName` call count expectations from 2 to 1.
|
|
156
|
+
3. Update assertions to check `setActiveToolsByName.mock.calls[0][0]` (was `calls[1][0]` for the second call).
|
|
157
|
+
4. The `extensions: false` test continues to assert that `setActiveToolsByName` is not called.
|
|
158
|
+
|
|
159
|
+
### `test/session/session-config.test.ts`
|
|
160
|
+
|
|
161
|
+
1. Update `result.toolFilter.toolNames` → `result.toolNames`.
|
|
162
|
+
2. Update `result.toolFilter.extensions` → `result.extensions`.
|
|
163
|
+
|
|
164
|
+
### `test/lifecycle/agent-runner.test.ts`
|
|
165
|
+
|
|
166
|
+
1. No structural changes needed — the session mock already has `setActiveToolsByName: vi.fn()`, and the default test config has `extensions: false` (so the filter doesn't run).
|
|
167
|
+
If any test asserts on `setActiveToolsByName` call counts, verify they still pass.
|
|
168
|
+
|
|
169
|
+
### `docs/architecture/architecture.md`
|
|
170
|
+
|
|
171
|
+
1. Update the Phase 14 Step 3 entry to mark it complete.
|
|
172
|
+
2. Update the structural analysis table: `SessionConfig` field count changes, `ToolFilterConfig` is removed.
|
|
173
|
+
3. Update the smell table to mark the two-pass filter and `ToolFilterConfig` smells as resolved.
|
|
174
|
+
|
|
175
|
+
## Test Impact Analysis
|
|
176
|
+
|
|
177
|
+
1. **New tests enabled:** None — the simplification doesn't introduce new testable surface.
|
|
178
|
+
2. **Tests that become redundant:** The pre-bind/post-bind ordering test in `agent-runner-extension-tools.test.ts` — the pre-bind call is removed.
|
|
179
|
+
The test that the post-bind filter includes extension tools stays; it verifies the recursion guard runs after `bindExtensions`.
|
|
180
|
+
3. **Tests that stay as-is:** The `extensions: false` skip test, the `EXCLUDED_TOOL_NAMES` exclusion test, all `session-config.test.ts` tests (with property path updates).
|
|
181
|
+
|
|
182
|
+
## TDD Order
|
|
183
|
+
|
|
184
|
+
1. **Flatten `SessionConfig` and delete `ToolFilterConfig`** — Replace `toolFilter: ToolFilterConfig` with `toolNames: string[]` and `extensions: boolean` on `SessionConfig`.
|
|
185
|
+
Delete the `ToolFilterConfig` interface.
|
|
186
|
+
Update the assembler return literal.
|
|
187
|
+
Update `session-config.test.ts` property paths (`result.toolFilter.toolNames` → `result.toolNames`, `result.toolFilter.extensions` → `result.extensions`).
|
|
188
|
+
Run `pnpm run check` to verify downstream compile errors (expected in `agent-runner.ts`).
|
|
189
|
+
Commit: `refactor: flatten SessionConfig and remove ToolFilterConfig`
|
|
190
|
+
|
|
191
|
+
2. **Simplify `filterActiveTools` and remove pre-bind call** — Reduce `filterActiveTools` to `(activeTools: string[]) => string[]`.
|
|
192
|
+
Remove the `ToolFilterConfig` import.
|
|
193
|
+
Remove the pre-bind filter block.
|
|
194
|
+
Update the post-bind filter block to use `cfg.extensions` and pass no config to `filterActiveTools`.
|
|
195
|
+
Update `noExtensions` and `tools` references to `cfg.extensions` and `cfg.toolNames`.
|
|
196
|
+
Update `agent-runner-extension-tools.test.ts`: change `setActiveToolsByName` call count from 2 to 1, update assertion indices from `calls[1]` to `calls[0]`, remove the pre-bind/post-bind ordering test, update comments.
|
|
197
|
+
Verify `agent-runner.test.ts` still passes.
|
|
198
|
+
Commit: `refactor: simplify filterActiveTools to recursion guard`
|
|
199
|
+
|
|
200
|
+
3. **Update architecture docs** — Mark Phase 14 Step 3 as complete.
|
|
201
|
+
Update `SessionConfig` field count and remove `ToolFilterConfig` references from the structural analysis.
|
|
202
|
+
Mark the two-pass filter and `ToolFilterConfig` smells as resolved.
|
|
203
|
+
Commit: `docs: mark Phase 14 Step 3 complete in architecture`
|
|
204
|
+
|
|
205
|
+
## Risks and Mitigations
|
|
206
|
+
|
|
207
|
+
1. **Pre-bind filter removal may miss a recursion-guard edge case** — If a subagent's own tools (`subagent`, `get_subagent_result`, `steer_subagent`) were in the built-in tool set before `bindExtensions`, removing the pre-bind filter could leave them active during the bind phase.
|
|
208
|
+
Mitigation: These tools are registered by this extension during `bindExtensions`, not before.
|
|
209
|
+
They cannot be in the pre-bind active set.
|
|
210
|
+
The post-bind filter is sufficient.
|
|
211
|
+
|
|
212
|
+
2. **Flattening `SessionConfig` may break external consumers** — `SessionConfig` is not exported from the package entry point; it's an internal interface between `session-config.ts` and `agent-runner.ts`.
|
|
213
|
+
No external consumers exist.
|
|
214
|
+
|
|
215
|
+
## Open Questions
|
|
216
|
+
|
|
217
|
+
None — the issue's changes are unambiguous and all dependencies are complete.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
issue: 227
|
|
3
|
+
issue_title: "Evolve AgentRecord into Agent with behavior (Phase 15, Step 1)"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Retro: #227 — Evolve AgentRecord into Agent with behavior
|
|
7
|
+
|
|
8
|
+
## Stage: Planning (2026-05-27T12:00:00Z)
|
|
9
|
+
|
|
10
|
+
### Session summary
|
|
11
|
+
|
|
12
|
+
Produced an 8-step TDD plan to move per-agent behavior (`abort`, `queueSteer`/`flushPendingSteers`, `setupWorktree`) from `AgentManager` into `AgentRecord`, then rename `AgentRecord` → `Agent` across the codebase.
|
|
13
|
+
The plan follows a "add behavior first, rename last" strategy to keep behavior diffs small and the rename commit purely mechanical.
|
|
14
|
+
|
|
15
|
+
### Observations
|
|
16
|
+
|
|
17
|
+
- `AgentRecord` is internal-only (public API is `SubagentRecord` in `service.ts`), so the rename is non-breaking.
|
|
18
|
+
- The `queueSteer` method can be removed from `AgentManagerLike` and `SteerToolManager` interfaces entirely — both callers (`steer-tool`, `service-adapter`) already hold the agent reference from `getRecord()`, so they can call `agent.queueSteer()` directly.
|
|
19
|
+
- Queue removal in `abort()` must stay on `AgentManager` until #230 extracts `ConcurrencyQueue`.
|
|
20
|
+
- `RunHandle` ownership explicitly deferred to #228 — the plan does not touch `RunHandle` at all.
|
|
21
|
+
- The rename step (step 7) touches ~30 files but is purely mechanical; all behavior changes land in steps 1–6.
|
|
22
|
+
|
|
23
|
+
## Stage: Implementation — TDD (2026-05-27T13:00:00Z)
|
|
24
|
+
|
|
25
|
+
### Session summary
|
|
26
|
+
|
|
27
|
+
Completed all 8 TDD steps from the plan.
|
|
28
|
+
Added 9 new tests (steer buffering, `abort()`, `setupWorktree()`) and migrated 977 existing tests to the renamed `Agent` class.
|
|
29
|
+
Test count went from 977 to 986 across 62 test files.
|
|
30
|
+
|
|
31
|
+
### Observations
|
|
32
|
+
|
|
33
|
+
- Fallow reported `AgentInit` and `AgentStatus` as unused type exports from `types.ts`; suppressed with `// fallow-ignore-next-line unused-type` (correct singular form — tool's error message hints at this).
|
|
34
|
+
- `ESLint` auto-removed an `as any` cast in the `setupWorktree` test (the mock `WorktreeManager` already satisfied the interface structurally); staged and re-committed cleanly.
|
|
35
|
+
- Biome auto-formatted several test files during the rename commit; re-staged and re-committed.
|
|
36
|
+
- Pre-completion reviewer returned **WARN** for 4 stale diagram/table references in `architecture.md` and the `package-pi-subagents` skill table; all fixed before the final commit.
|
|
37
|
+
- No deviations from the plan's behavior design; the `queueSteer` removal from manager interfaces worked exactly as anticipated in the retro notes.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
---
|
|
2
|
+
issue: 239
|
|
3
|
+
issue_title: "Collapse filterActiveTools to recursion guard (Phase 14, Step 3)"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Retro: #239 — Collapse filterActiveTools to recursion guard
|
|
7
|
+
|
|
8
|
+
## Stage: Planning (2026-05-27T20:00:00Z)
|
|
9
|
+
|
|
10
|
+
### Session summary
|
|
11
|
+
|
|
12
|
+
Produced a 3-step TDD plan to flatten `SessionConfig.toolFilter` into top-level `toolNames` and `extensions` fields, simplify `filterActiveTools` to a one-liner recursion guard, remove the pre-bind filter call, and update architecture docs.
|
|
13
|
+
Both dependencies (#237, #238) are confirmed closed.
|
|
14
|
+
|
|
15
|
+
### Observations
|
|
16
|
+
|
|
17
|
+
- The `builtinToolNameSet` membership check in `filterActiveTools` is fully dead code — both branches return `true` after #238 removed the `string[]` extensions path.
|
|
18
|
+
- `ToolFilterConfig` is only imported by `agent-runner.ts` and never referenced in test files, so deletion is clean.
|
|
19
|
+
- The pre-bind filter call is safe to remove because `EXCLUDED_TOOL_NAMES` tools (`subagent`, `get_subagent_result`, `steer_subagent`) are registered by this extension during `bindExtensions`, not before — they cannot appear in the pre-bind active set.
|
|
20
|
+
- The `agent-runner-extension-tools.test.ts` file has 4 tests; 1 becomes structurally impossible (pre-bind/post-bind ordering) and the remaining 3 need assertion index adjustments (`calls[1]` → `calls[0]`).
|
|
21
|
+
- `SessionConfig` is internal-only (not package-exported), so flattening has no external API impact.
|
|
22
|
+
|
|
23
|
+
## Stage: Implementation — TDD (2026-05-27T22:00:00Z)
|
|
24
|
+
|
|
25
|
+
### Session summary
|
|
26
|
+
|
|
27
|
+
Completed all 3 TDD cycles: (1) flattened `SessionConfig` and deleted `ToolFilterConfig`, (2) simplified `filterActiveTools` to a one-liner and removed the pre-bind filter call, (3) updated architecture docs to mark Phase 14 Step 3 complete.
|
|
28
|
+
Test count held at 977 (no net change — the extension-tools test file was rewritten, removing 1 test and updating 3 others while keeping the same total count).
|
|
29
|
+
A follow-up skill maintenance commit updated `.pi/skills/package-pi-subagents/SKILL.md` to remove stale Patch 2 references.
|
|
30
|
+
|
|
31
|
+
### Observations
|
|
32
|
+
|
|
33
|
+
- The plan's step order (SessionConfig first → expected compile errors in `agent-runner.ts` → green after runner update) worked exactly as designed with no surprises.
|
|
34
|
+
- `agent-runner.test.ts` needed no changes — the default test config has `extensions: false`, so the filter call never ran in those tests.
|
|
35
|
+
- Pre-completion reviewer returned **WARN** for stale `package-pi-subagents` skill content: the "Patch 2 scheduled for removal" note and the `// Patch 2 (RepOne` grep instruction were both stale after #239 completion.
|
|
36
|
+
Fixed immediately as a follow-up `docs:` commit before writing retro notes.
|
|
37
|
+
- `pnpm fallow dead-code` passed with 0 issues — no orphaned exports left behind.
|
|
38
|
+
|
|
39
|
+
## Stage: Final Retrospective (2026-05-27T14:40:00Z)
|
|
40
|
+
|
|
41
|
+
### Session summary
|
|
42
|
+
|
|
43
|
+
Completed the full issue lifecycle (plan → TDD → ship → retro) in a single continuous session.
|
|
44
|
+
Issue #239 shipped as `pi-subagents-v10.0.1` with 7 commits (2 refactor, 4 docs, 1 release).
|
|
45
|
+
Phase 14 is now fully complete, unblocking Phase 15 (#227–#232).
|
|
46
|
+
|
|
47
|
+
### Observations
|
|
48
|
+
|
|
49
|
+
#### What went well
|
|
50
|
+
|
|
51
|
+
- The plan's type-dependency-chain ordering (`SessionConfig` first → expected compile errors → `agent-runner.ts` resolves them) produced zero surprises during TDD.
|
|
52
|
+
Each step's red/green boundary was exactly where the plan predicted.
|
|
53
|
+
- Pre-completion reviewer caught stale `package-pi-subagents` skill content ("Patch 2 scheduled for removal" and a `// Patch 2 (RepOne` grep instruction) that no longer matched source.
|
|
54
|
+
Fixed before shipping — the reviewer earned its keep.
|
|
55
|
+
- Multi-model routing was well-matched: `claude-sonnet-4-6` for planning and TDD (judgment + code), `deepseek-v4-flash` for shipping (mechanical checklist), `claude-opus-4-6` for retrospective (synthesis).
|
|
56
|
+
- Feedback loops were incremental: `pnpm vitest run` after each test change, `pnpm run check` after Step 1 to confirm expected errors, full suite + lint + dead-code after all steps.
|
|
57
|
+
|
|
58
|
+
#### What caused friction (agent side)
|
|
59
|
+
|
|
60
|
+
No friction points identified.
|
|
61
|
+
This was the final step of a 3-step phase with both dependencies already closed, a well-scoped plan, and internal-only API changes — the simplest possible lifecycle.
|
|
62
|
+
|
|
63
|
+
#### What caused friction (user side)
|
|
64
|
+
|
|
65
|
+
None observed.
|
|
66
|
+
The user's involvement was limited to issuing the four standard lifecycle commands (`/plan-issue`, `/tdd-plan`, `/ship-issue`, `/retro`) with no corrections or redirections needed.
|
|
67
|
+
|
|
68
|
+
### Changes made
|
|
69
|
+
|
|
70
|
+
1. Appended Final Retrospective stage entry to `packages/pi-subagents/docs/retro/0239-collapse-filter-active-tools.md`.
|