@gotgenes/pi-subagents 16.5.0 → 17.0.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.
Files changed (32) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/public.d.ts +4 -1
  3. package/docs/architecture/architecture.md +39 -31
  4. package/docs/plans/0421-migrate-activity-readers-to-record-getters.md +309 -0
  5. package/docs/plans/0422-delete-activity-tracker-ui-observer.md +204 -0
  6. package/docs/plans/0423-widget-self-drive-from-lifecycle.md +264 -0
  7. package/docs/plans/0424-drop-widget-dep-from-subagent-tool.md +166 -0
  8. package/docs/plans/0425-reconcile-subagent-events-contract.md +144 -0
  9. package/docs/retro/0420-fold-run-metrics-live-activity-onto-record.md +45 -0
  10. package/docs/retro/0421-migrate-activity-readers-to-record-getters.md +105 -0
  11. package/docs/retro/0422-delete-activity-tracker-ui-observer.md +97 -0
  12. package/docs/retro/0423-widget-self-drive-from-lifecycle.md +112 -0
  13. package/docs/retro/0424-drop-widget-dep-from-subagent-tool.md +89 -0
  14. package/docs/retro/0425-reconcile-subagent-events-contract.md +46 -0
  15. package/package.json +1 -1
  16. package/src/index.ts +12 -6
  17. package/src/observation/composite-subagent-observer.ts +49 -0
  18. package/src/observation/notification.ts +3 -12
  19. package/src/observation/subagent-events-observer.ts +1 -4
  20. package/src/runtime.ts +0 -6
  21. package/src/service/service.ts +4 -1
  22. package/src/tools/agent-tool.ts +1 -36
  23. package/src/tools/background-spawner.ts +1 -25
  24. package/src/tools/foreground-runner.ts +9 -34
  25. package/src/tools/helpers.ts +5 -4
  26. package/src/types.ts +1 -1
  27. package/src/ui/agent-menu.ts +0 -12
  28. package/src/ui/agent-widget.ts +56 -17
  29. package/src/ui/conversation-viewer.ts +4 -9
  30. package/src/ui/widget-renderer.ts +14 -22
  31. package/src/ui/agent-activity-tracker.ts +0 -84
  32. package/src/ui/ui-observer.ts +0 -61
@@ -0,0 +1,166 @@
1
+ ---
2
+ issue: 424
3
+ issue_title: "pi-subagents: drop the widget and activity-map dependencies from the subagent tool"
4
+ ---
5
+
6
+ # Drop the widget dependency from the subagent tool
7
+
8
+ ## Problem Statement
9
+
10
+ The LLM-facing `subagent` tool's real concern is dispatch, yet `AgentTool` still takes a `widget` constructor dependency and calls `this.widget.setUICtx(ctx.ui)` at the start of `execute`.
11
+ Every `AgentTool` unit test has to stub the widget through `createToolDeps` — testability friction that marks the domain seam.
12
+ Now that the widget self-drives its timer from lifecycle notifications (Phase 18 Step 4, [#423]) and `ToolStartHandler` already captures the UI context on every `tool_execution_start`, the tool no longer needs the widget at all.
13
+
14
+ Note: the issue title and the roadmap entry for this step also mention an `agentActivity` / activity-map dependency, but that was already removed from `AgentTool` and the runtime in Phase 18 Step 3 ([#422]).
15
+ The only remaining dependency to drop is `widget`.
16
+ This plan corrects that stale wording where it appears in the roadmap.
17
+
18
+ ## Goals
19
+
20
+ - Remove the `widget` constructor parameter from `AgentTool`; the tool depends only on manager / runtime / settings / registry / agentDir.
21
+ - Remove the now-redundant `this.widget.setUICtx(ctx.ui)` call from `AgentTool.execute` — UICtx capture stays in `ToolStartHandler`.
22
+ - Delete the `AgentToolWidget` interface and the `UICtx` import that only that interface used.
23
+ - Drop the `widget` field, its stub, and the `AgentToolWidget` import from the `createToolDeps` fixture.
24
+ - Update the `index.ts` call site and all affected tests in the same commit (the constructor-signature change breaks them at typecheck time).
25
+ - This change is **not breaking** — `AgentTool` is internal (the package's only public exports are the service and settings entries), and observable behavior is preserved because `ToolStartHandler` already captures UICtx before the tool executes.
26
+
27
+ ## Non-Goals
28
+
29
+ - No change to `ToolStartHandler`, `AgentWidget`, or the `UICtx` type itself (it stays in `src/ui/agent-widget.ts`, consumed by the widget and `ToolStartHandler`).
30
+ - No change to `foreground-runner.ts` / `background-spawner.ts` (their widget driving was already removed in [#423]).
31
+ - No work on Phase 18 Steps 6–8 ([#425], [#426], [#427]) — the public-event-contract reconciliation, test-clone consolidation, and UI-direction ADR are separate steps.
32
+
33
+ ## Background
34
+
35
+ Relevant modules and their current state:
36
+
37
+ - `src/tools/agent-tool.ts` — `AgentTool` constructor signature is `(manager, runtime, widget, settings, registry, agentDir)`.
38
+ `execute` opens with `this.widget.setUICtx(ctx.ui as UICtx)`.
39
+ The file defines the narrow `AgentToolWidget` interface (`setUICtx` only, already narrowed in [#423]) and imports `UICtx` from `#src/ui/agent-widget` solely for that interface and the cast.
40
+ - `src/handlers/tool-start.ts` — `ToolStartHandler.handleToolExecutionStart` already calls `this.widget.setUICtx(ctx.ui)` then `this.widget.onTurnStart()`.
41
+ `tool_execution_start` fires before any tool's `execute`, so the widget already has the current UICtx by the time `AgentTool.execute` runs.
42
+ This handler is wired in `index.ts` and is the canonical UICtx-capture site.
43
+ - `src/index.ts:152` — the sole `new AgentTool(...)` call site, passing `widget` as the third argument.
44
+ `widget` is still constructed and registered as a lifecycle observer (`observer.add(widget)`) and passed to `ToolStartHandler` — those usages stay.
45
+ - `test/helpers/make-deps.ts` — `createToolDeps` builds an `AgentToolFixture` with a `widget` field stub (`{ setUICtx: vi.fn() }`) and imports `AgentToolWidget`.
46
+ - `test/helpers/make-deps.test.ts` — has a `describe("widget defaults")` block asserting the stub.
47
+ - `test/tools/agent-tool.test.ts` — `makeTool` passes `deps.widget`; the test `"sets UI context on runtime at start of execute"` asserts `deps.widget.setUICtx` was called.
48
+
49
+ AGENTS.md constraint: pi-subagents is a minimal core whose dependency arrows point inward.
50
+ Removing an outbound widget dependency from the LLM tool moves the package further toward "UI is a pure consumer of broadcast events," which is the stated precondition for the Phase 18 UI-direction decision.
51
+
52
+ ## Design Overview
53
+
54
+ This is a purely subtractive refactor — no new collaborator, no new interface, no moved behavior.
55
+ The widget already receives its UICtx from `ToolStartHandler` on every turn's first tool execution, so the tool's own `setUICtx` call is redundant.
56
+
57
+ `AgentTool.execute` after the change opens directly with the registry reload:
58
+
59
+ ```typescript
60
+ async execute(toolCallId, params, signal, onUpdate, ctx) {
61
+ // Reload custom agents so new .pi/agents/*.md files are picked up without restart
62
+ this.registry.reload();
63
+ const config = resolveSpawnConfig(params, this.registry, this.runtime.getModelInfo(), this.settings);
64
+ // ... unchanged
65
+ }
66
+ ```
67
+
68
+ The `ctx` parameter is still required by the `defineTool` `execute` signature; the tool simply no longer reads `ctx.ui`.
69
+
70
+ New constructor signature:
71
+
72
+ ```typescript
73
+ constructor(
74
+ private readonly manager: AgentToolManager,
75
+ private readonly runtime: AgentToolRuntime,
76
+ private readonly settings: AgentToolSettings,
77
+ private readonly registry: AgentTypeRegistry,
78
+ private readonly agentDir: string,
79
+ ) { /* unchanged body */ }
80
+ ```
81
+
82
+ Edge cases:
83
+
84
+ - UICtx availability — `tool_execution_start` always precedes `execute`, so removing the tool's capture loses no coverage; the widget is registered as an observer and already has the UICtx when it renders.
85
+ - No behavior depends on the tool calling `setUICtx` twice per turn; the second call (in the tool) was idempotent for the same `ctx.ui` and a no-op when unchanged.
86
+
87
+ ## Module-Level Changes
88
+
89
+ - `src/tools/agent-tool.ts`
90
+ - Remove the `import { type UICtx } from "#src/ui/agent-widget";` line.
91
+ - Remove the `AgentToolWidget` interface (and its doc comment).
92
+ - Remove the `private readonly widget: AgentToolWidget` constructor parameter.
93
+ - Remove the `this.widget.setUICtx(ctx.ui as UICtx);` statement and its comment from `execute`.
94
+ - `src/index.ts`
95
+ - Update line 152: `new AgentTool(manager, runtime, settings, registry, getAgentDir())` (drop `widget`).
96
+ - Leave the `widget` construction, `observer.add(widget)`, and `new ToolStartHandler(widget)` wiring unchanged.
97
+ - `test/helpers/make-deps.ts`
98
+ - Drop `AgentToolWidget` from the imports from `#src/tools/agent-tool`.
99
+ - Remove the `widget: AgentToolWidget` field from `AgentToolFixture` (and its doc comment).
100
+ - Remove the `const widget: AgentToolWidget = { setUICtx: vi.fn() };` stub and the `widget,` entry in the returned object.
101
+ - `test/helpers/make-deps.test.ts`
102
+ - Remove the `describe("widget defaults", ...)` block.
103
+ - `test/tools/agent-tool.test.ts`
104
+ - Update `makeTool` to drop `deps.widget` from the `new AgentTool(...)` call.
105
+ - Remove the `"sets UI context on runtime at start of execute"` test (UICtx capture is covered by `test/handlers/tool-start.test.ts`).
106
+ - `docs/architecture/architecture.md`
107
+ - Mark Phase 18 Step 5 complete (✅) with a `Landed:` bullet, mirroring the Step 4 entry.
108
+ - Correct the stale `agentActivity` mention in the Step 5 description (line ~979) — only the `widget` param remains to drop.
109
+ - Update the step dependency diagram node `S5` to the completed (✅) marker.
110
+
111
+ Grep confirmation performed during planning: `AgentToolWidget` appears only in `src/tools/agent-tool.ts` and `test/helpers/make-deps.ts`; the `UICtx` type stays in use by `src/ui/agent-widget.ts`, `src/handlers/tool-start.ts` (via `unknown`), and `test/ui/agent-widget.test.ts`.
112
+ No `package-*/SKILL.md` references `AgentToolWidget` or the tool's widget dependency.
113
+
114
+ ## Test Impact Analysis
115
+
116
+ 1. New tests enabled: none required — this is a subtractive refactor, not a new seam.
117
+ The remaining `AgentTool` tests get simpler (no widget stub to thread).
118
+ 2. Tests that become redundant:
119
+ - `test/tools/agent-tool.test.ts` → `"sets UI context on runtime at start of execute"` — the tool no longer captures UICtx; `test/handlers/tool-start.test.ts` (`"calls setUICtx with the context's ui"`) already pins that behavior on its true owner.
120
+ Remove it.
121
+ - `test/helpers/make-deps.test.ts` → `describe("widget defaults")` — the fixture no longer has a widget field.
122
+ Remove it.
123
+ 3. Tests that must stay as-is:
124
+ - `test/handlers/tool-start.test.ts` — genuinely exercises UICtx capture, now the sole owner of that responsibility.
125
+ - `test/ui/agent-widget.test.ts` — exercises the widget's own `setUICtx`, unaffected.
126
+ - All other `AgentTool` dispatch/resume/background/foreground tests — exercise the tool's real concern and only shed the unused widget argument.
127
+
128
+ ## Invariants at risk
129
+
130
+ Phase 18 Step 4 ([#423]) documented Outcome: "the widget is a reactive consumer; no inbound calls from core spawn tools."
131
+ This step extends that to the LLM tool.
132
+ The invariant that UICtx is captured exactly once per turn by `ToolStartHandler` is pinned by `test/handlers/tool-start.test.ts` (`"calls setUICtx with the context's ui"` and `"calls setUICtx before onTurnStart"`) — both stay green and require no new test.
133
+ No earlier step's `Outcome:` is regressed: the widget's self-driving timer (Step 4) is independent of the tool's removed `setUICtx` call.
134
+
135
+ ## TDD Order
136
+
137
+ This is a behavior-preserving refactor; the green suite stays green.
138
+ Because removing the constructor parameter breaks the `index.ts` call site, the `make-deps` fixture, and the `agent-tool` tests at typecheck time, all source and test edits land in one commit.
139
+
140
+ 1. **Refactor: drop the widget dependency from `AgentTool`.**
141
+ Edit `src/tools/agent-tool.ts` (remove the `UICtx` import, `AgentToolWidget` interface, `widget` param, and `setUICtx` call), `src/index.ts` (drop `widget` from the constructor call), `test/helpers/make-deps.ts` (drop the field, stub, and import), `test/helpers/make-deps.test.ts` (remove the widget-defaults block), and `test/tools/agent-tool.test.ts` (drop `deps.widget` from `makeTool`, remove the UICtx test).
142
+ Run `pnpm run check` and the full package suite.
143
+ Commit: `refactor: drop the widget dependency from the subagent tool (#424)`.
144
+ 2. **Docs: mark Phase 18 Step 5 complete.**
145
+ Update `docs/architecture/architecture.md` — Step 5 ✅ + `Landed:` bullet, correct the stale `agentActivity` wording, update the `S5` diagram node.
146
+ Commit: `docs: mark Phase 18 Step 5 complete and drop the tool widget dep (#424)`.
147
+
148
+ ## Risks and Mitigations
149
+
150
+ - Risk: removing the tool's `setUICtx` loses UICtx for the widget in some path.
151
+ Mitigation: `tool_execution_start` fires before every `execute`, and `ToolStartHandler` captures UICtx there; the existing handler tests pin this.
152
+ Run the full suite, not just `agent-tool.test.ts`, since the change touches a shared fixture (`make-deps.ts`).
153
+ - Risk: an orphaned import left behind after deletions (Biome `noUnusedImports` is warning-level, exit 0).
154
+ Mitigation: re-read `agent-tool.ts` and `make-deps.ts` after editing; confirm `UICtx` and `AgentToolWidget` have no remaining references via grep.
155
+ - Risk: the stale `agentActivity` wording is mistaken for live work to do.
156
+ Mitigation: this plan explicitly records that `agentActivity` was already removed in [#422]; only `widget` remains.
157
+
158
+ ## Open Questions
159
+
160
+ None — the scope is fully determined by the established roadmap and the verified current code state.
161
+
162
+ [#422]: https://github.com/gotgenes/pi-packages/issues/422
163
+ [#423]: https://github.com/gotgenes/pi-packages/issues/423
164
+ [#425]: https://github.com/gotgenes/pi-packages/issues/425
165
+ [#426]: https://github.com/gotgenes/pi-packages/issues/426
166
+ [#427]: https://github.com/gotgenes/pi-packages/issues/427
@@ -0,0 +1,144 @@
1
+ ---
2
+ issue: 425
3
+ issue_title: "pi-subagents: reconcile the public SUBAGENT_EVENTS contract with emitted channels"
4
+ ---
5
+
6
+ # Reconcile the public `SUBAGENT_EVENTS` contract with emitted channels
7
+
8
+ ## Problem Statement
9
+
10
+ The public lifecycle-event contract has drifted out of sync with what the core actually broadcasts on `pi.events`.
11
+ `SUBAGENT_EVENTS.ACTIVITY = "subagents:activity"` is declared in the service surface (`src/service/service.ts`) and the architecture doc's lifecycle-events table, but no module ever emits it — a vacant hook that the architecture's own "no vacant hooks" rule forbids.
12
+ The vacancy hardened in Phase 18 Steps 1–5, which deleted the entire activity tier (`AgentActivityTracker`, `ui-observer`), so there is no streaming-progress source left to broadcast.
13
+ In the other direction, four channels the core does emit — `subagents:failed`, `subagents:compacted`, `subagents:created` (all in `SubagentEventsObserver`), and `subagents:steered` (in `steer-tool.ts`) — are absent from the constant map.
14
+ A consumer reading `SUBAGENT_EVENTS` therefore gets one channel that never fires and misses four that do.
15
+
16
+ ## Goals
17
+
18
+ - Remove the vacant `SUBAGENT_EVENTS.ACTIVITY` constant.
19
+ This is a **breaking change** to the public surface: it deletes a key from the exported `SUBAGENT_EVENTS` map, so a consumer referencing `SUBAGENT_EVENTS.ACTIVITY` breaks at the type level on upgrade.
20
+ Use `feat!:` with a `BREAKING CHANGE:` footer.
21
+ - Add the four emitted agent-lifecycle channels to the constant map: `FAILED`, `COMPACTED`, `CREATED`, `STEERED`.
22
+ - After the change, declared channels equal emitted agent-lifecycle channels — no vacant hook, no undeclared emission.
23
+ - Update the lifecycle-events table in `docs/architecture/architecture.md` to match, and correct the stale `subagents:completed` payload shape while there.
24
+ - Mark Phase 18 Step 6 complete in the architecture roadmap.
25
+
26
+ ## Non-Goals
27
+
28
+ - Re-introducing a streaming-progress (`activity`) event.
29
+ The activity tier was deliberately deleted in Steps 1–3; resurrecting it is out of scope and was rejected during planning.
30
+ - Adding the config-domain events (`subagents:settings_loaded`, `subagents:settings_changed`) or the child-session seam events (`subagents:child:*`) to `SUBAGENT_EVENTS`.
31
+ Those belong to separate domains and already have their own constant homes (settings emitter, `child-lifecycle.ts`); `SUBAGENT_EVENTS` is the agent-lifecycle bus only.
32
+ - The `subagents:record` session entry — it is an `appendEntry` (session persistence), not a `pi.events.emit`, so it is not a channel constant.
33
+ - Changing any event payload shape or emission site.
34
+ This issue reconciles the *constant map* with what is *already emitted*; emission logic is untouched.
35
+ - Phase 18 Steps 7–8 (test-clone consolidation, UI reconsideration) — separate issues ([#426], [#427]).
36
+
37
+ ## Background
38
+
39
+ - `src/service/service.ts` declares `SUBAGENT_EVENTS` as an `as const` object — the public, cross-extension channel constants, re-exported through the `.` subpath entry and rolled into `dist/public.d.ts`.
40
+ - `src/observation/subagent-events-observer.ts` emits `subagents:started`, `subagents:completed`, `subagents:failed`, `subagents:compacted`, and `subagents:created`.
41
+ - `src/tools/steer-tool.ts` emits `subagents:steered` with payload `{ id, message }`.
42
+ - The completed/failed payload is produced by `buildEventData(record)` in `src/observation/notification.ts`: `{ id, type, description, result, error, status, toolUses, durationMs, tokens? }`.
43
+ The architecture doc's current table lists `subagents:completed` as `{ id, type, status, result?, error? }`, which is stale.
44
+ - AGENTS.md / `package-pi-subagents` skill constraint: any change to the public surface must run `pnpm --filter @gotgenes/pi-subagents run verify:public-types` (a CI gate), and sibling packages consume this one from the published registry release, not a workspace symlink.
45
+ - The "no vacant hooks" rule (ADR-0002, architecture doc §519) governs both directions here: admit a surface only when a real consumer/emitter exists.
46
+
47
+ This is a value-only reconciliation of a shared constant: no new collaborator, no dependency-wiring change, no new parameter on any interface — so the `design-review` dependency-width / Law-of-Demeter checklist surfaces nothing actionable.
48
+
49
+ ## Design Overview
50
+
51
+ The reconciled constant map:
52
+
53
+ ```typescript
54
+ /** Event channel constants for pi.events subscriptions. */
55
+ export const SUBAGENT_EVENTS = {
56
+ STARTED: "subagents:started",
57
+ COMPLETED: "subagents:completed",
58
+ FAILED: "subagents:failed",
59
+ COMPACTED: "subagents:compacted",
60
+ CREATED: "subagents:created",
61
+ STEERED: "subagents:steered",
62
+ } as const;
63
+ ```
64
+
65
+ The declared set is now exactly the set of agent-lifecycle channels emitted by `SubagentEventsObserver` and `steer-tool.ts`.
66
+
67
+ Updated lifecycle-events table (`architecture.md`):
68
+
69
+ | Channel | Payload | When |
70
+ | --------------------- | ----------------------------------------------------------------------------------- | --------------------------------------------- |
71
+ | `subagents:started` | `{ id, type, description }` | Agent begins running |
72
+ | `subagents:completed` | `{ id, type, description, status, result?, error?, toolUses, durationMs, tokens? }` | Agent finishes successfully |
73
+ | `subagents:failed` | same as `completed` (`buildEventData` shape) | Agent ends in `error`/`stopped`/`aborted` |
74
+ | `subagents:compacted` | `{ id, type, description, reason, tokensBefore, compactionCount }` | Child session compacts |
75
+ | `subagents:created` | `{ id, type, description, isBackground }` | Background agent created (pre-admission) |
76
+ | `subagents:steered` | `{ id, message }` | Steering message delivered to a running agent |
77
+
78
+ The `subagents:activity` row is removed.
79
+
80
+ ### Edge cases
81
+
82
+ - `as const` preservation: the literal-string value types must stay narrow, so the new keys keep the `as const` assertion.
83
+ `verify:public-types` confirms the rolled `dist/public.d.ts` exposes the narrowed literal types.
84
+ - No runtime emission changes: the four added constants name channels that are *already* fired, so subscribers wired to the string literals see no behavioral change — only the typed constant is now available.
85
+
86
+ ## Module-Level Changes
87
+
88
+ - `packages/pi-subagents/src/service/service.ts` — remove the `ACTIVITY` key; add `FAILED`, `COMPACTED`, `CREATED`, `STEERED` to `SUBAGENT_EVENTS`.
89
+ - `packages/pi-subagents/test/service/service.test.ts` — update the `SUBAGENT_EVENTS` assertion: drop the `ACTIVITY` expectation, add expectations for the four new constants.
90
+ - `packages/pi-subagents/docs/architecture/architecture.md` — replace the lifecycle-events table (remove the `activity` row, add `failed`/`compacted`/`created`/`steered`, correct the `completed` payload); mark Phase 18 Step 6 complete with a `✅ … — complete.` prefix and a `Landed:` bullet.
91
+
92
+ Grep confirmation that no other live reference to the removed symbol exists:
93
+
94
+ - `grep -rn "SUBAGENT_EVENTS.ACTIVITY\|subagents:activity"` across `src/`, `test/`, and `.pi/skills/package-*/SKILL.md`: the only live hits are `service.ts:100`, the `service.test.ts` assertion, and the `architecture.md` table — all three are updated here.
95
+ Remaining `subagents:activity` matches live in historical/plan docs (`docs/plans/0048-*`, `docs/architecture/history/`, the structural-analysis finding #6 snapshot) and are intentionally left as historical record.
96
+ - `docs/comparison-with-upstream.md` already lists the lifecycle bus as `created, started, completed, failed, steered, compacted` (no `activity`) — it matches the reconciled set and needs no edit.
97
+ - The architecture structural-analysis finding #6 (the phase-start smell snapshot) is left as-is, consistent with how findings #1–5 remain present-tense snapshots while their steps are marked `✅`.
98
+
99
+ ## Test Impact Analysis
100
+
101
+ This is a constant-map reconciliation, not an extraction, so no new lower-level test surface opens up.
102
+
103
+ 1. New tests enabled: none beyond the expanded `SUBAGENT_EVENTS` assertion — the change adds no new function or collaborator.
104
+ 2. Tests becoming redundant: none.
105
+ The emission-site tests in `test/observation/subagent-events-observer.test.ts` (which assert `subagents:failed`/`compacted`/`created` are emitted) and the steer-tool test stay — they pin the *emission*, while the service test pins the *declaration*.
106
+ 3. Tests that must stay as-is: the observer and steer-tool emission tests — they genuinely exercise the channels the constants now name, and are the other half of the "declared == emitted" invariant.
107
+
108
+ ## Invariants at risk
109
+
110
+ This step touches the public-contract surface that Step 6 is itself responsible for; no earlier Phase 18 step (1–5) refactored `SUBAGENT_EVENTS`, so there is no prior `Outcome:`/`Landed:` invariant to regress.
111
+ The invariant this step establishes — declared channels equal emitted agent-lifecycle channels — is pinned from both sides:
112
+
113
+ - Declaration: the updated `test/service/service.test.ts` `SUBAGENT_EVENTS` assertion.
114
+ - Emission: existing `test/observation/subagent-events-observer.test.ts` (`failed`/`compacted`/`created`) and the steer-tool test (`steered`).
115
+
116
+ ## TDD Order
117
+
118
+ 1. **Reconcile the constant map (red → green → commit).**
119
+ Test surface: `test/service/service.test.ts` — rewrite the `SUBAGENT_EVENTS` assertion to expect `STARTED`, `COMPLETED`, `FAILED`, `COMPACTED`, `CREATED`, `STEERED` and to assert `ACTIVITY` is absent (`expect("ACTIVITY" in SUBAGENT_EVENTS).toBe(false)` or drop the import-time reference).
120
+ This fails to compile/assert against the current map (red).
121
+ Then edit `src/service/service.ts`: remove `ACTIVITY`, add the four new keys (green).
122
+ Run `pnpm --filter @gotgenes/pi-subagents run check`, `pnpm --filter @gotgenes/pi-subagents run test`, and `pnpm --filter @gotgenes/pi-subagents run verify:public-types` (public-surface gate).
123
+ Commit: `feat!: reconcile SUBAGENT_EVENTS with emitted channels (#425)` with a `BREAKING CHANGE:` footer noting the removal of `SUBAGENT_EVENTS.ACTIVITY` and the migration (subscribe to the emitted channel constants `FAILED`/`COMPACTED`/`CREATED`/`STEERED`; there is no replacement for `ACTIVITY` — the activity tier was removed in Phase 18).
124
+ 2. **Update the architecture doc (commit, no test).**
125
+ Replace the lifecycle-events table and correct the `completed` payload; mark Phase 18 Step 6 `✅ … — complete.` with a `Landed:` bullet.
126
+ Run `pnpm --filter @gotgenes/pi-subagents run lint` (rumdl).
127
+ Commit: `docs: reconcile lifecycle-events table with SUBAGENT_EVENTS (#425)`.
128
+
129
+ ## Risks and Mitigations
130
+
131
+ - **Risk:** the breaking removal of `ACTIVITY` surprises a consumer.
132
+ **Mitigation:** the constant was never emitted, so no consumer could meaningfully act on it; the `BREAKING CHANGE:` footer documents the removal and the absence of a replacement, and release-please carries it to the CHANGELOG and the issue close comment.
133
+ - **Risk:** the rolled `dist/public.d.ts` drifts from the source `as const` shape.
134
+ **Mitigation:** `verify:public-types` packs the tarball and type-checks a throwaway consumer against both entries — run it in Step 1 before committing.
135
+ - **Risk:** stale `subagents:activity` references linger in docs.
136
+ **Mitigation:** the Module-Level Changes grep enumerates every live vs. historical hit; only the three live references are edited, historical snapshots are intentionally preserved.
137
+
138
+ ## Open Questions
139
+
140
+ None.
141
+ The two design choices (remove vs. emit `ACTIVITY`; three vs. four added channels) were resolved during planning: remove `ACTIVITY`, and declare all four emitted agent-lifecycle channels (`failed`/`compacted`/`created`/`steered`).
142
+
143
+ [#426]: https://github.com/gotgenes/pi-packages/issues/426
144
+ [#427]: https://github.com/gotgenes/pi-packages/issues/427
@@ -43,3 +43,48 @@ Full suite green; type check and lint clean; zero dead code.
43
43
  - WARN finding: `package-pi-subagents` SKILL.md `Observation` domain row description ("Session-event stats") is now slightly incomplete for `record-observer` (it also accumulates live-activity fields).
44
44
  Intentionally deferred per the planning-stage decision — no symbol removed, and the description will be updated in Step 2 ([#421]) when the observer's role is fully defined after the reader migration.
45
45
  - Pre-completion reviewer verdict: **WARN** (one non-blocking finding, deferred per retro).
46
+
47
+ ## Stage: Final Retrospective (2026-06-17T17:40:39Z)
48
+
49
+ ### Session summary
50
+
51
+ Shipped Phase 18 Step 1 across planning, TDD (3 red→green→commit cycles, +27 tests), and release (`pi-subagents` v16.5.0).
52
+ The code work was clean and rework-free; all friction was in markdown-lint surfacing late — two separate doc-lint failures (one pre-existing, one introduced) escaped the baseline and post-TDD gates and were caught only by the pre-completion reviewer and the ship-stage lint, each requiring a fix commit.
53
+
54
+ ### Observations
55
+
56
+ #### What went well
57
+
58
+ - The `pre-completion-reviewer` safety net earned its keep: it caught the pre-existing `MD051` broken-fragment links in `phase-17-core-consolidation.md` (introduced earlier by `e4d92535`, unrelated to this issue) before the push, returning FAIL on the first run.
59
+ Without it the broken links would have shipped.
60
+ - TDD execution was textbook: each of the 3 steps followed red→green→commit with no rework to production code, the `maxTurns`-via-`execution` delegation decision from planning held, and the "vacuously-passing" observer tests (verified in the retro) correctly converted to real assertions once the observer was implemented.
61
+
62
+ #### What caused friction (agent side)
63
+
64
+ - `instruction-violation` (gate-caught, at ship) — wrote a second `[#421]:` link-reference definition into the retro file when appending the TDD stage entry via `Write` full-content, tripping `MD053` (duplicate definition).
65
+ The `markdown-conventions` skill states this rule verbatim ("Link reference definitions are file-scoped … a duplicate trips MD053").
66
+ Impact: one fix commit at ship time (`80d4d050`), caught by the root `pnpm run lint` pre-push gate rather than during TDD (no lint runs after stage-notes writing).
67
+ - `missing-context` (self-identified during ship investigation) — the TDD green-baseline lint was run package-scoped (`pnpm --filter @gotgenes/pi-subagents run lint`), which **silently passes** on `MD051` cross-file fragment failures.
68
+ Verified empirically: package `lint:md` (`rumdl check *.md docs/**/*.md`) returns "No issues" on the broken fragment, while root `rumdl check .` exits 1 on it — the cross-file fragment target (`../architecture.md`) is only resolved on a repo-root tree walk.
69
+ Impact: the pre-existing `MD051` failure slipped past the baseline gate (whose job is to catch exactly that) and surfaced mid-TDD via the reviewer, costing a fix commit (`86ed0c81`) and a reviewer FAIL/re-dispatch cycle.
70
+
71
+ #### What caused friction (user side)
72
+
73
+ - None.
74
+ The session ran end-to-end without user correction; the operator-authored, roadmap-aligned issue meant no clarification was needed.
75
+
76
+ ### Diagnostic details
77
+
78
+ - **Model-performance correlation** — two `pre-completion-reviewer` subagent dispatches ran on the agent's configured reviewer model and performed judgment-heavy review work (acceptance criteria, design review, cross-step invariants); appropriate match.
79
+ No high-cost model was spent on mechanical work and no reasoning-weak model on judgment work.
80
+ - **Feedback-loop gap analysis** — the lint feedback loop fired at the wrong scope and the wrong time: package-scoped at baseline (missed `MD051`), and absent after retro stage-notes writing (missed `MD053` until ship).
81
+ `pnpm run check` and `pnpm run test` were run incrementally per step and caught everything they should; only the markdown-lint loop was misconfigured.
82
+ - **Escalation-delay / unused-tool** — no `rabbit-hole` friction; no lens-2 or lens-3 findings.
83
+
84
+ ### Changes made
85
+
86
+ 1. `.pi/prompts/tdd-plan.md` — "Verify green baseline" step 2 and "After the last TDD step" step 3 now specify `pnpm run lint` runs **from the repo root**, with a one-clause rationale that package-scoped lint silently passes on `MD051` cross-file fragments and cross-package issues (Proposal 1).
87
+ 2. Proposal 2 (an `MD053` duplicate-link-definition reminder in the retro-writing prompts) was declined — the rule already exists verbatim in the `markdown-conventions` skill, so a prompt clause would duplicate it.
88
+ 3. Follow-up (post-retro): `#420` completed Phase 18 Step 1 but the roadmap Steps section was not marked done until the operator noticed.
89
+ Added a roadmap-step-status check so this is caught going forward: a WARN bullet in `pre-completion-reviewer.md` §2c (backstop), and a proactive step in `.pi/prompts/tdd-plan.md` step 7 and `.pi/prompts/build-plan.md` "After the last step" — mark the completed roadmap step done (`✅`/`Landed:`) and update the phase status row.
90
+ Also marked Step 1 complete in `architecture.md` (Steps list, dependency diagram node, phase table row → "In progress").
@@ -0,0 +1,105 @@
1
+ ---
2
+ issue: 421
3
+ issue_title: "pi-subagents: migrate activity-tracker readers to the record getters"
4
+ ---
5
+
6
+ # Retro: #421 — Migrate activity-tracker readers to the record getters
7
+
8
+ ## Stage: Planning (2026-06-17T00:00:00Z)
9
+
10
+ ### Session summary
11
+
12
+ Produced a 5-step TDD plan for Phase 18 Step 2 — migrating the five activity readers (`widget-renderer`, `conversation-viewer`, `agent-menu`, `foreground-runner`, `notification`) off `AgentActivityTracker` and onto the `Subagent` getters added in Step 1 ([#420]).
13
+ The plan files at `packages/pi-subagents/docs/plans/0421-migrate-activity-readers-to-record-getters.md`.
14
+
15
+ ### Observations
16
+
17
+ - **Two design forks surfaced and were resolved with the operator** via `ask_user`. (1) Finished agents currently show **no** turn count because the tracker is deleted on completion; reading off the record (which persists `turnCount`) makes finished lines show `⟳N`.
18
+ Operator accepted this as the more-correct behavior — so the widget step is `feat:`, not `refactor:`. (2) How `WidgetAgent` exposes context percent: chose a precomputed `contextPercent` **field** + a `Subagent` → `WidgetAgent` projection in `AgentWidget` (over a `getContextPercent()` method on the snapshot), honoring the renderer's pure-data contract and dropping its `getSessionContextPercent(session)` reach-through.
19
+ Operator's steer: "no sacred cows, especially in the UI — make the change that makes the change easy, then make the easy change."
20
+ - **Scope boundary with Step 3 ([#422]).**
21
+ The producer plumbing (`foreground-runner` / `background-spawner` constructing trackers, `subscribeUIObserver`, the `runtime.agentActivity` map) stays — it is removed in Step 3.
22
+ But the reader-held map deps in `notification`, `agent-widget`, and `agent-menu` are removed **here**, because Step 3's target list does not include those files.
23
+ This means `cleanupCompleted` (which only deleted a map entry) becomes vestigial and is removed from `NotificationSystem`, rippling to `SubagentEventsObserver`.
24
+ - **Transient map leak between Steps 2 and 3** is noted as an accepted risk: after this step the runtime map is write-only and read by nobody, so it is inert until Step 3 deletes it.
25
+ - **Prep step (tidy-first):** Step 1 adds `turnCount` / `activeTools` / `responseText` / `maxTurns` shorthands to `createTestSubagent` so the reader-migration tests can seed activity on records.
26
+ - **Living-doc updates identified:** `.pi/skills/package-pi-subagents/SKILL.md` line 56 (`widget ─polls─→ AgentActivityTracker map`) and the architecture roadmap's Phase 18 Step 2 `Landed:` line.
27
+ Completed historical plans under `docs/plans/` are point-in-time records and are not edited.
28
+ - **Shared-signature commits** flagged for `pnpm run check`: the widget step (`renderWidgetLines`) and the notification step (`NotificationSystem` + `index.ts` construction).
29
+ Both `buildDetails` call sites and both viewer/menu constructor call sites must land in the same commit as their signature changes.
30
+
31
+ [#420]: https://github.com/gotgenes/pi-packages/issues/420
32
+ [#422]: https://github.com/gotgenes/pi-packages/issues/422
33
+
34
+ ## Stage: Implementation — TDD (2026-06-17T17:00:00Z)
35
+
36
+ ### Session summary
37
+
38
+ All 5 TDD cycles completed in one session.
39
+ The five activity readers (`widget-renderer`, `conversation-viewer`, `agent-menu`, `foreground-runner`, `notification`) were migrated off `AgentActivityTracker` and onto the `Subagent` record getters added in Phase 18 Step 1.
40
+ Test count went from 1058 to 1066 (+8).
41
+
42
+ ### Observations
43
+
44
+ - **Step 2 (widget)** introduced the `WidgetAgent` → activity-field fold and the `AgentWidget.toWidgetAgent` projection.
45
+ The accepted behavior change (finished agents now show `⟳N` from the persisted record) landed as `feat:` as planned.
46
+ `renderWidgetLines` drops `activityMap`; renderer is now a pure function of plain data with no SDK-type reach.
47
+ - **Step 3 (viewer + menu)** was straightforward once `ConversationViewer` dropped `activity?: AgentActivityTracker`; the streaming indicator now reads `this.record.activeTools` / `this.record.responseText` directly.
48
+ `AgentActivityReader` interface removed; `AgentsMenuHandler` constructor lost one parameter.
49
+ - **Step 4 (buildDetails + foreground runner)** required care around the pre-`onSessionCreated` phase: `streamUpdate` falls back to `recordRef?.turnCount ?? 1`, `execution.effectiveMaxTurns`, empty `Map()`, and `""` before the record reference is assigned.
50
+ The `AgentTool` resume call site (`buildDetails(base, record)`) was already correct and needed no change.
51
+ - **Step 5 (notifications)** removed `NotificationSystem.cleanupCompleted` entirely (it only deleted a map entry).
52
+ `SubagentEventsObserver.onSubagentCompleted` now returns early on `resultConsumed` rather than calling the removed method.
53
+ The `NotificationManager` constructor drops the `agentActivity: Map` argument; `index.ts` needed one arg removed.
54
+ - **Post-commit SKILL.md fix**: the pre-completion reviewer (WARN) flagged that `subagent-events-observer.ts` was missing from the Observation domain table; fixed in a follow-up `docs:` commit.
55
+ - **Pre-completion reviewer**: WARN (non-blocking).
56
+ Reviewer warning: SKILL.md Observation domain table listed 4 modules and omitted `subagent-events-observer.ts`; corrected before writing these notes.
57
+
58
+ ## Stage: Final Retrospective (2026-06-17T18:30:00Z)
59
+
60
+ ### Session summary
61
+
62
+ Shipped Phase 18 Step 2 across one continuous session (planning, TDD, ship, retro): five activity readers migrated off `AgentActivityTracker` onto `Subagent` record getters, released as `pi-subagents` v16.6.0.
63
+ Execution was clean overall (+8 tests, all deterministic checks green, one-pass CI), but the architecture roadmap's per-step completion marker was missed during TDD and only fixed after the user caught it at ship time.
64
+
65
+ ### Observations
66
+
67
+ #### What went well
68
+
69
+ - **`ask_user` design-fork gate during planning paid off downstream.**
70
+ Resolving the two forks (finished-agent turn-count behavior change; `contextPercent` field-vs-method) up front meant TDD had zero design backtracking — every step landed as planned, including the `feat:`-vs-`refactor:` commit-type split the fork decided.
71
+ - **Lift-and-shift sequencing held.**
72
+ The prep step (`createTestSubagent` activity shorthands, commit `bcdb81c9`) made the four reader-migration steps mechanical; each shared-signature change (`renderWidgetLines`, `buildDetails`, `NotificationSystem`) landed with all its call sites in one commit, so `pnpm run check` stayed green at every step boundary.
73
+
74
+ #### What caused friction (agent side)
75
+
76
+ - `instruction-violation` (user-caught) — the architecture roadmap's **per-step** completion marker was not applied during TDD.
77
+ Step 7 of `/tdd-plan` says "mark that step done (`✅`/`Landed:`) and update the phase status row."
78
+ I added the `Landed:` line (commit `999e5ecb`) and confirmed the phase status row correctly stayed `In progress`, but did **not** add the `✅` prefix to the step heading or the Mermaid diagram node.
79
+ The established convention (Step 1 carries `✅` on both its heading and its `S1[...]` diagram node) treats the `✅` markers as the completion signal, with `Landed:` an optional detail line.
80
+ Impact: the user caught it at ship time ("we didn't check off the step in architecture.md in the phase Steps section"); fixed in commit `47644ff1`, after which ship resumed.
81
+ Root cause: the prompt phrasing `(✅/Landed:)` reads as either/or, so satisfying `Landed:` felt sufficient.
82
+ - `missing-context` (reviewer gap) — the pre-completion reviewer's roadmap-status check (added in `b8a938d8` for exactly this class of miss) passed the unchecked step.
83
+ Its report said "Architecture doc has a `Landed:` entry for Step 2; Phase 18 status row correctly remains 'In progress'" — it verified the `Landed:` line and the phase row but never checked the per-step `✅` on the heading and diagram node.
84
+ Impact: the guard built to catch this exact omission did not, leaving the user as the only backstop.
85
+ - `other` (tool friction) — a multi-edit `Edit` batch on `test/ui/agent-widget.test.ts` (Step 2) was rejected for overlapping `oldText` regions; re-issued as a single non-overlapping edit.
86
+ Impact: one retry, no rework.
87
+
88
+ #### What caused friction (user side)
89
+
90
+ - None substantive.
91
+ The single user intervention (the architecture step-checkbox catch) was the correct backstop for a gap the automated reviewer should have caught; surfacing it as a redirect rather than a silent fix kept the convention enforced.
92
+
93
+ ### Diagnostic details
94
+
95
+ - **Model-performance correlation** — the ship stage (CI watch, release-PR merge, issue close) ran on `opencode-go/deepseek-v4-flash`; appropriate for mechanical orchestration, and it executed the `UNSTABLE`-merge `GITHUB_TOKEN` branch correctly.
96
+ Planning/TDD ran on `anthropic/claude-opus-4-8` / `claude-sonnet-4-6` (judgment-heavy); retro on `claude-opus-4-8`.
97
+ No quality mismatch — the per-step-checkbox miss occurred during TDD on a high-reasoning model, so it is an instruction-clarity gap, not a model-capability gap.
98
+ - **Feedback-loop gap analysis** — `pnpm run check` ran after each shared-signature step as the plan flagged, and the full suite plus `fallow dead-code` ran before push; verification was incremental, not end-loaded.
99
+ - **Escalation-delay / unused-tool** — no `rabbit-hole` friction; no lens finding.
100
+
101
+ ### Changes made
102
+
103
+ 1. `.pi/prompts/tdd-plan.md` (step 7) — disambiguated roadmap-step completion: `✅` on both the step heading and its Mermaid diagram node; `Landed:` is not a substitute; phase status row flips only when every step is done.
104
+ 2. `.pi/prompts/build-plan.md` (step 4) — same disambiguation as tdd-plan.
105
+ 3. `.pi/agents/pre-completion-reviewer.md` (roadmap-status check) — reviewer now verifies `✅` on both the step heading and diagram node (not just a `Landed:` line) and checks the phase row against the actual step count.
@@ -0,0 +1,97 @@
1
+ ---
2
+ issue: 422
3
+ issue_title: "pi-subagents: delete AgentActivityTracker and ui-observer, drop the activity map from the core"
4
+ ---
5
+
6
+ # Retro: #422 — Delete AgentActivityTracker and ui-observer, drop the activity map from the core
7
+
8
+ ## Stage: Planning (2026-06-17T00:00:00Z)
9
+
10
+ ### Session summary
11
+
12
+ Planned Phase 18 Step 3 of the activity-tier disentanglement spine: deleting `AgentActivityTracker` and `ui-observer`, and removing `SubagentRuntime.agentActivity` plus the tracker wiring in the two spawn tools.
13
+ Verified the prerequisites (#420, #421) are both closed and that the trackers/map are now write-only dead state after the reader migration.
14
+ Wrote a four-step plan (two `refactor:` deletion commits, a module-delete commit, a `docs:` sweep) at `packages/pi-subagents/docs/plans/0422-delete-activity-tracker-ui-observer.md`.
15
+
16
+ ### Observations
17
+
18
+ - The change is **non-breaking** and internal-only: `AgentActivityTracker`, `ui-observer`, and `agentActivity` are absent from the public service surface (`service.ts`) and settings entry, so no `BREAKING CHANGE` footer.
19
+ Issue author is the operator (`gotgenes`) and the proposed change is unambiguous and roadmap-driven, so the `ask-user` gate was skipped.
20
+ - The foreground `observer.onSessionCreated` callback **stays** — it is still the only place `recordRef`/`fgId` bind mid-flight and where `widget.ensureTimer()` fires; only the tracker lines are stripped.
21
+ The background `observer` block, by contrast, did only tracker work and is removed entirely.
22
+ - Commit ordering matters: Step 1 (spawners stop passing `agentActivity`) must precede Step 2 (remove the runtime field), or the build breaks.
23
+ Both the param removal and the field/`AgentActivityAccess` removal cascade to call sites and tests at the type level, so each is folded into a single commit.
24
+ - Re-render cadence: dropping `subscribeUIObserver` removes event-driven foreground re-renders, leaving the existing 80 ms spinner poll.
25
+ Content is identical within ≤80 ms (the poll reads the same record the core observer populates) — pinned by the streaming-`onUpdate` test, noted as a risk not a regression.
26
+ - Found a **pre-existing stale doc** from #421: `architecture.md` still says "the widget reads agent state by polling a shared `Map<string, AgentActivityTracker>`", though #421 already moved the widget onto records.
27
+ Folded that correction into this plan's Step 4 doc sweep alongside the file tree, two Mermaid diagrams, and the SKILL.md domain counts (UI `12 → 10`, header `59 → 57` files).
28
+ - Confirmed no orphaned sibling exports: `SessionLike` (used by `subagent-session.ts`) and `SubscribableSession` (used by `record-observer.ts`, `subagent-session.ts`, `types.ts`) both survive the module deletion; `pnpm fallow dead-code` is the Step 3 backstop.
29
+
30
+ ## Stage: Implementation — TDD (2026-06-17T20:40:00Z)
31
+
32
+ ### Session summary
33
+
34
+ Executed all four planned steps as a deletion refactor: stripped tracker wiring + the `agentActivity` parameter from the spawners, removed the activity map from `SubagentRuntime`/`AgentToolRuntime`, deleted `agent-activity-tracker.ts` and `ui-observer.ts` (−145 LOC) plus their suites, and swept the architecture doc + SKILL.md.
35
+ Landed in six commits (four planned + one folded test removal + one `style:` lint fixup).
36
+ Test count dropped −34 (1066 → 1032) across 63 files (was 65); `check`, root `lint`, full `test`, and `fallow dead-code` all green.
37
+
38
+ ### Observations
39
+
40
+ - **Deviation (test removal moved earlier):** the agent-tool "registers activity in agentActivity map" test was planned for Step 2 but had to be removed in Step 1 — once the spawner stops populating the map, the test fails at runtime in that commit.
41
+ Folded into Step 1 per the testing skill's "account for tests that break" rule.
42
+ - **Deviation (atomic-batch trap):** the Step 2 multi-edit `Edit` on `runtime.ts` was rejected because edit[1] miscounted a decorative `─` rule, which silently dropped edit[0] (the `AgentActivityTracker` import removal).
43
+ `tsc` passed at Step 2 because the leftover was an elided `import type`; it only surfaced as a tsc/fallow error once Step 3 deleted the module.
44
+ Removed it in Step 3 and re-read the region after editing.
45
+ This is exactly the AGENTS.md warning about anchoring on decorative rules.
46
+ - **Lint fixup:** an unused `runtime` destructure remained in one `background-spawner.test.ts` case.
47
+ It belongs to Step 1's file but HEAD was the `docs:` commit (a fixup must not land in a `docs:` commit, and amending a non-HEAD `refactor:` commit needs a rebase), so it landed as a standalone `style:` commit.
48
+ - **No behavior regression:** foreground re-renders now rely solely on the 80 ms spinner poll (the second `subscribeUIObserver` subscription is gone); pinned by the surviving "calls onUpdate with streaming details while running" test.
49
+ - **Doc correction:** fixed the pre-existing stale `architecture.md` prose that still claimed the widget polls a `Map<string, AgentActivityTracker>` (the widget moved onto records in #421); now reads "polls the records exposed via `SubagentManager.listAgents()`".
50
+ - **Pre-completion reviewer: PASS** — all deterministic checks, code-design, test-artifact, Mermaid (`mmdc` parsed all 6 blocks), dead-code, and cross-step-invariant lenses passed; no warnings.
51
+
52
+ ## Stage: Final Retrospective (2026-06-18T01:19:58Z)
53
+
54
+ ### Session summary
55
+
56
+ Shipped Phase 18 Step 3 across plan → TDD → ship in three sessions: deleted `AgentActivityTracker` and `ui-observer`, removed the `agentActivity` map from `SubagentRuntime` and both spawn tools (−145 LOC, −34 tests), and swept the architecture doc + SKILL.md.
57
+ Six implementation commits, pre-completion reviewer PASS, CI green, issue closed; no release (all `refactor:`/`style:`/`docs:`).
58
+ Clean hands-off execution — the only user input was one `ask_user` answer ("Release now") and no corrections.
59
+
60
+ ### Observations
61
+
62
+ #### What went well
63
+
64
+ - Incremental verification cadence was exemplary: `pnpm run check` ran after every shared-type change (after Step 1, Step 2, and twice in Step 3), not just at end-of-cycle — exactly the feedback-loop discipline the TDD prompt asks for.
65
+ This caught the Step 2/3 type fallout immediately rather than as a late surprise.
66
+ - The atomic-batch trap (below) was self-identified and the recovery was clean and well-documented — the Step 3 commit body explains why the `runtime.ts` import removal landed there instead of Step 2.
67
+
68
+ #### What caused friction (agent side)
69
+
70
+ - `instruction-violation` (self-identified) — the Step 2 multi-edit `Edit` on `runtime.ts` anchored `edits[1]` on a decorative `─` rule and miscounted it, rejecting the whole atomic batch and silently dropping `edits[0]` (the `AgentActivityTracker` import removal).
71
+ This is the exact anti-pattern AGENTS.md § "Edit tool batches" warns against ("anchor on adjacent unique code lines rather than the rule itself").
72
+ The follow-up rule ("after a rejection, re-apply every intended edit and run `pnpm run check`") was also only half-followed: the field edit was re-applied but the import edit was not, and the `check` at Step 2 passed anyway because `tsc` elides an unused `import type`.
73
+ Impact: the dropped import surfaced only at Step 3 (once the module was deleted) as a tsc/fallow error, costing one investigation cycle and smearing the import removal into the Step 3 commit instead of Step 2.
74
+ - `other` (self-identified) — dropping the `runtime.agentActivity` argument from the `background-spawner.test.ts` calls left one test still destructuring an unused `runtime`.
75
+ Biome's `noUnusedVariables` is warning-level (exit 0), so it did not fail `lint`; it was caught by comparing the warning count to the green baseline at end-of-cycle.
76
+ Impact: one extra `style:` commit (could not amend — HEAD was a `docs:` commit).
77
+
78
+ #### What caused friction (user side)
79
+
80
+ - None blocking — execution was hands-off.
81
+ Opportunity, not criticism: the ship stage ran on `opencode-go/deepseek-v4-flash`, a weak model, and that stage carries a real judgment call (is a missing release-please PR expected, or a problem?).
82
+ It was answered correctly here, but the release-decision judgment on a weak model is a latent risk worth an operator's awareness.
83
+
84
+ ### Diagnostic details
85
+
86
+ - **Model-performance correlation** — Planning and TDD ran on `anthropic/claude-opus-4-8` (appropriate: design + commit-sequencing judgment, deviation recovery).
87
+ The pre-completion reviewer subagent ran to completion (245 s, 26 tool uses) and returned a thorough PASS.
88
+ Ship ran on `opencode-go/deepseek-v4-flash` — fine for the mechanical push/CI/close flow, but it also made the "no release expected" inference; correct here, latent risk in general (see user-side note).
89
+ - **Escalation-delay tracking** — No `rabbit-hole`s.
90
+ Both deviations resolved within 1–2 tool calls; no sequence exceeded 5 calls on one error.
91
+ - **Unused-tool detection** — No `missing-context` gaps. `grep` (not `colgrep`) was used throughout planning, correctly — every search was an exact symbol match (`agentActivity`, `AgentActivityTracker`, `subscribeUIObserver`), which is grep's lane per the colgrep decision table.
92
+ - **Feedback-loop gap analysis** — No gap; verification was incremental, not end-only (see "What went well").
93
+ The one escape (`import type` removal) is a tsc-tolerance gap, not a cadence gap — `check` ran on schedule but cannot flag an unused type import.
94
+
95
+ ### Changes made
96
+
97
+ 1. `AGENTS.md` § "Edit tool batches" — augmented the post-rejection rule with a caveat: `tsc` passes on a dropped `import type` removal (an unused type import is not an error), so re-read the affected region rather than trusting `pnpm run check` alone.