@gotgenes/pi-subagents 16.6.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.
@@ -54,3 +54,52 @@ Test count went from 1058 to 1066 (+8).
54
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
55
  - **Pre-completion reviewer**: WARN (non-blocking).
56
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.
@@ -0,0 +1,112 @@
1
+ ---
2
+ issue: 423
3
+ issue_title: "pi-subagents: make the agent widget self-drive from lifecycle events"
4
+ ---
5
+
6
+ # Retro: #423 — Make the agent widget self-drive from lifecycle events
7
+
8
+ ## Stage: Planning (2026-06-17T00:00:00Z)
9
+
10
+ ### Session summary
11
+
12
+ Planned Phase 18 Step 4 of the widget/tool decoupling track: making `AgentWidget` a `SubagentManagerObserver` that self-drives its 80 ms timer from lifecycle notifications, wired via a new `CompositeSubagentObserver` fan-out, and removing all inbound widget calls from the spawn tools.
13
+ Wrote a four-step plan (three `refactor:` commits + a `docs:` sweep) at `packages/pi-subagents/docs/plans/0423-widget-self-drive-from-lifecycle.md`.
14
+
15
+ ### Observations
16
+
17
+ - **Wiring mechanism was the live design decision.**
18
+ `SubagentManager` has a single `observer` slot.
19
+ Three options surfaced: (A) a `CompositeSubagentObserver` fan-out in `index.ts`, (B) make the manager hold an observer list, (C) subscribe the widget to the public `pi.events` channels.
20
+ The operator initially leaned toward B (matching the issue's literal file list) but was unsure; after reframing around the decouple + overridable-UI north star, they chose **A**.
21
+ Rationale recorded in the plan: A keeps the core closed for modification, B moves fan-out *into* the core (wrong direction), and C front-runs the Step 6 ([#425]) public-event-contract reconciliation.
22
+ Key insight that flattened the decision: all three options keep the widget's `manager.listAgents()` reference, so they only change the *trigger*, not the data source — full broadcast-plus-query decoupling is the Step 8 ([#427]) concern.
23
+ - **`markFinished` is fully redundant** and is deleted, not relocated: `seedFinishedAgents()` (added in [#421] / [#422]) already seeds any agent with `completedAt` on each poll tick.
24
+ This matters because the manager **never fires `onSubagentCompleted` for foreground agents** (`onRunFinished` guards on `isBackground`), so the widget could not learn of foreground completion via the observer anyway — polling covers it.
25
+ - **Construction cycle** (widget needs manager → manager needs observer → observer includes widget) is broken by constructing the composite with `[eventsObserver]`, passing it to the manager, then `observer.add(widget)` after the widget is built; the manager consults the observer only lazily at spawn time.
26
+ - **TDD ordering avoids a behavior gap:** Step 2 wires the widget as an observer *while the spawn tools still drive it* (idempotent double-drive), strictly before Step 3 removes the spawn-tool calls — so no commit leaves the widget without a timer-start signal.
27
+ The new widget observer methods need no `fallow-ignore` because they are invoked polymorphically through `SubagentManagerObserver` (the `SubagentEventsObserver` precedent).
28
+ - This step narrows `AgentToolWidget` to `setUICtx` only but keeps the `AgentTool` widget constructor param; full removal is Step 5 ([#424]).
29
+ - Non-breaking and internal-only (no public service/settings surface touched), so `refactor:`/`docs:` commits, no `BREAKING CHANGE` footer.
30
+
31
+ ## Stage: Implementation — TDD (2026-06-17T10:00:00Z)
32
+
33
+ ### Session summary
34
+
35
+ Executed the four-step plan exactly as written: added `CompositeSubagentObserver` (Step 1), made `AgentWidget` a `SubagentManagerObserver` and wired the composite in `index.ts` (Step 2), removed all spawn-tool widget wiring and deleted `markFinished` (Step 3), then swept the architecture doc + SKILL.md (Step 4).
36
+ Four commits (three `refactor:`, one `docs:`); test count `1032 → 1039` (+7 composite, +4 widget observer, −4 removed spawner/fixture widget-driving tests).
37
+ `check`, root `lint`, full `test`, and `fallow dead-code` all green; pre-completion reviewer returned PASS.
38
+
39
+ ### Observations
40
+
41
+ - **Mid-step correction (not a separate commit):** in Step 2 I initially over-reached by deleting `markFinished` and privatizing `ensureTimer`, which breaks `index.ts` typing because `AgentToolWidget` still required those methods until Step 3.
42
+ Caught it before committing and reverted both to Step 3 per the plan; Step 2 kept them public.
43
+ The plan's ordering (narrow `AgentToolWidget` and delete `markFinished` only in the atomic Step 3 removal) was correct — the lesson is to trust the step boundaries.
44
+ - **Biome `useIterableCallbackReturn` false trigger:** naming the composite's private fan-out helper `forEach` made Biome treat the call as `Array.prototype.forEach` and reject the value-returning arrow.
45
+ Renamed it to `dispatch` — a strictly better name anyway.
46
+ - **`vi.getTimerCount()` cleanly proves the timer started:** the widget observer tests assert `getTimerCount()` goes `0 → 1` on `onSubagentStarted`/`onSubagentCreated`, distinguishing `startLoop` (ensureTimer + render) from a bare `update()` (render only), with the manager-stub creating no other timers.
47
+ - **No dead-code window:** the widget's new observer methods are invoked polymorphically through `SubagentManagerObserver` (the `SubagentEventsObserver` precedent), so `fallow` saw them as used from Step 2 onward; `ensureTimer` became `private` in Step 3, dropping its now-stale `fallow-ignore`.
48
+ - **The Step 2 transient double-drive was harmless** as predicted: both the spawn tools and the composite drove the widget in that commit, and every driven method is idempotent — full suite green at that commit.
49
+ - **Reviewer notes (non-blocking, both PASS):** a pre-existing "six domains" vs "seven domains" inconsistency in `architecture.md` (Phase 17, out of scope, left alone); and `observer.add(widget)` is a justified post-construction write documented as the only construction-cycle break (widget needs manager, manager needs observer).
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.
51
+
52
+ ## Stage: Final Retrospective (2026-06-17T18:30:00Z)
53
+
54
+ ### Session summary
55
+
56
+ Shipped Phase 18 Step 4 across plan → TDD → ship in a single session: the `AgentWidget` became a `SubagentManagerObserver` that self-drives its 80 ms timer through a new `CompositeSubagentObserver` fan-out, and the spawn tools shed every widget call.
57
+ Four implementation commits (three `refactor:`, one `docs:`), pre-completion reviewer PASS, CI green, issue closed; no release (all commits non-releasing).
58
+ Execution was clean — two self-caught agent-side slips, no rework commits, and the only user input was one productive redirect plus a release-timing answer.
59
+
60
+ ### Observations
61
+
62
+ #### What went well
63
+
64
+ - **Two-round `ask_user` on the wiring decision (planning).**
65
+ The first round offered three mechanisms (composite / manager-list / `pi.events`); the operator picked B but flagged uncertainty.
66
+ Reframing around the decouple + overridable-UI north star and re-asking flipped the choice to A (`CompositeSubagentObserver`) with explicit buy-in.
67
+ The reframe surfaced the insight that flattened the decision: all three options keep the widget's `manager.listAgents()` reference, so they change only the *trigger*, not the data source.
68
+ - **`vi.getTimerCount()` as a precise timer-start assertion.**
69
+ The widget observer tests assert `getTimerCount()` goes `0 → 1` on `onSubagentStarted`/`onSubagentCreated`, cleanly distinguishing `startLoop` (timer + render) from a bare `update()` (render only).
70
+ - **"Double-drive" lift-and-shift ordering.**
71
+ Step 2 wired the new observer while the spawn tools still drove the widget (idempotent overlap), and Step 3 removed the old path — no commit left the widget without a timer-start signal, and the full suite was green at every commit.
72
+ - **Incremental verification cadence.**
73
+ `pnpm run check` after each shared-type change, `vitest` per-file in red/green, `mmdc` on all 6 Mermaid blocks, and `fallow`/`lint`/full-suite at the end of Step 3 — no end-only verification.
74
+
75
+ #### What caused friction (agent side)
76
+
77
+ - `scope-drift` (self-identified) — in TDD Step 2 I deleted `markFinished` and privatized `ensureTimer` ahead of schedule; both belonged to the atomic Step 3 removal because `AgentToolWidget` still required them until then (deleting early breaks `index.ts` typing).
78
+ Caught by reasoning before committing and reverted to Step 3.
79
+ Impact: a few corrective edits, no extra commit, no rework.
80
+ Lesson: re-read the step's exact scope before editing, rather than editing from the overall design held in memory.
81
+ - `missing-context` (self-identified) — naming the composite's private fan-out helper `forEach` tripped Biome's `useIterableCallbackReturn` (it treated the call as `Array.prototype.forEach` and rejected the value-returning arrow).
82
+ Renamed to `dispatch`.
83
+ Impact: one rename, caught immediately by the `pi-autoformat` hook; no rework.
84
+ - `other` — the ship stage asked the batch-vs-release question for a multi-issue sequence, but every unreleased commit was `refactor:`/`docs:`, so release-please produced no PR regardless of the answer.
85
+ The operator's "release now" answer was therefore unactionable (the work auto-batches until a `feat`/`fix` lands).
86
+ Impact: one unactionable `ask_user` round; correct outcome reached, but the question implied a choice that did not exist.
87
+
88
+ #### What caused friction (user side)
89
+
90
+ - None blocking — the planning redirect ("I'm not confident in my choice… the goal is to decouple… an event system would make sense but isn't on the roadmap") was the ideal intervention: a redirecting question that handed over the north star instead of a post-hoc correction, and it directly produced the better design.
91
+
92
+ ### Diagnostic details
93
+
94
+ - **Model-performance correlation** — Planning ran on `anthropic/claude-opus-4-8` (design + the two-round wiring `ask_user`; appropriate).
95
+ TDD ran on `anthropic/claude-sonnet-4-6` (implementation; handled the Step 2 self-correction and the Biome rename; adequate).
96
+ The `pre-completion-reviewer` subagent ran on `anthropic/claude-sonnet-4-6` (its frontmatter) and returned a thorough PASS.
97
+ Ship ran on `opencode-go/deepseek-v4-flash` — a reasoning-weak model carrying the batch-vs-release judgment and the "no release PR expected" inference.
98
+ It reached the correct outcome, but this is the same latent risk the #422 retro flagged: a weak model on the one ship-stage judgment call.
99
+ Proposal 1 mitigates it by making the release-trigger check deterministic so the stage needs no judgment.
100
+ - **Escalation-delay tracking** — no `rabbit-hole`s; both agent-side slips resolved in one tool call each (one rename, one revert); no sequence exceeded 5 calls on one error.
101
+ - **Unused-tool detection** — no `missing-context` gaps requiring a subagent; `grep` (not `colgrep`) was used in planning, correctly — every search was an exact symbol match (`ensureTimer`, `markFinished`, `observer`).
102
+ - **Feedback-loop gap analysis** — no gap; verification was incremental, not end-only (see "What went well").
103
+
104
+ ### Changes made
105
+
106
+ 1. `.pi/prompts/ship-issue.md` — step 4b now opens with a release-trigger gate: check `git log --oneline <last-tag>..HEAD`, and when every commit is a non-releasing type (`refactor:`/`docs:`/`style:`/`chore:`/`test:`), state that release-please cuts nothing now and skip the batch-vs-release question.
107
+
108
+ [#421]: https://github.com/gotgenes/pi-packages/issues/421
109
+ [#422]: https://github.com/gotgenes/pi-packages/issues/422
110
+ [#424]: https://github.com/gotgenes/pi-packages/issues/424
111
+ [#425]: https://github.com/gotgenes/pi-packages/issues/425
112
+ [#427]: https://github.com/gotgenes/pi-packages/issues/427
@@ -0,0 +1,89 @@
1
+ ---
2
+ issue: 424
3
+ issue_title: "pi-subagents: drop the widget and activity-map dependencies from the subagent tool"
4
+ ---
5
+
6
+ # Retro: #424 — pi-subagents: drop the widget and activity-map dependencies from the subagent tool
7
+
8
+ ## Stage: Planning (2026-06-18T15:01:42Z)
9
+
10
+ ### Session summary
11
+
12
+ Planned Phase 18 Step 5: dropping the `widget` constructor dependency from `AgentTool` and shedding the widget stub from `createToolDeps`.
13
+ Verified against current `main` that the `agentActivity` / activity-map dependency named in the issue and roadmap was already removed in Phase 18 Step 3 ([#422]) — only `widget` remains to drop, so the plan corrects that stale wording.
14
+ Produced `docs/plans/0424-drop-widget-dep-from-subagent-tool.md` and committed it.
15
+
16
+ ### Observations
17
+
18
+ - The issue body and the architecture roadmap's Step 5 entry both still say `agentActivity`, but `grep` found no live `agentActivity`/`AgentActivity` references in `src/` — the only hit is a comment in `test/lifecycle/usage.test.ts`.
19
+ Flagged the roadmap Step 5 description for a stale-wording fix during implementation.
20
+ - This is a purely subtractive, non-breaking refactor: `AgentTool` is internal (public exports are only the service and settings entries), and `ToolStartHandler` already captures UICtx on every `tool_execution_start`, which fires before any tool's `execute`.
21
+ So removing the tool's own `setUICtx` call loses no behavior — `test/handlers/tool-start.test.ts` already pins UICtx capture on its true owner.
22
+ - Folded all edits into one refactor commit because removing the constructor parameter breaks the `index.ts:152` call site, the `make-deps.ts` fixture, and `agent-tool.test.ts` at typecheck time — they cannot land separately.
23
+ - Two obsolete tests to remove: `agent-tool.test.ts` → `"sets UI context on runtime at start of execute"` and `make-deps.test.ts` → `describe("widget defaults")`.
24
+ The `UICtx` type itself stays (used by `agent-widget.ts`, `tool-start.ts`, and the widget test) — only the `UICtx` import in `agent-tool.ts` and the `AgentToolWidget` interface go.
25
+ - Skipped the `ask-user` gate: operator-authored issue (`gotgenes`), unambiguous proposed change following an established roadmap, clearly non-breaking.
26
+
27
+ ## Stage: Implementation — TDD (2026-06-18T15:10:13Z)
28
+
29
+ ### Session summary
30
+
31
+ Executed the two-step plan: one `refactor:` commit dropping the `widget` constructor dependency from `AgentTool` (plus the `UICtx` import, `AgentToolWidget` interface, the redundant `setUICtx` call, the `index.ts` call site, the `createToolDeps` field/stub, and two obsolete tests), and one `docs:` commit marking Phase 18 Step 5 complete in the architecture doc.
32
+ Test count went from 1039 to 1037 (−2 removed obsolete tests); `check`, root `lint`, full `test`, and `fallow dead-code` all green.
33
+
34
+ ### Observations
35
+
36
+ - The refactor was purely subtractive and folded all src + test edits into one commit, as the plan predicted — the constructor-signature change breaks `index.ts`, `make-deps.ts`, and `agent-tool.test.ts` at typecheck simultaneously.
37
+ - Renamed the unused `execute` `ctx` parameter to `_ctx` rather than removing it — the inner `defineTool` `execute` closure must keep the 5-arg signature, so the method keeps the positional slot but ignores it.
38
+ - Corrected the architecture doc's Step 5 `Outcome` from "fixture drops 2 fields" to "drops 1 field" and noted the `agentActivity` half was already done in [#422] — the plan flagged this stale wording in advance.
39
+ - Pre-completion reviewer: WARN (1 non-blocking finding).
40
+ The `package-pi-subagents` SKILL test count was stale (994/63 "as of Phase 17 Step 4"); refreshed to 1037/64 "as of Phase 18 Step 5" in a follow-up `docs:` commit.
41
+ All cross-step invariants verified — UICtx capture stays pinned by `handlers/tool-start.test.ts`, and no `setUICtx` calls remain in spawn tools or the tool.
42
+
43
+ ## Stage: Final Retrospective (2026-06-18T15:20:14Z)
44
+
45
+ ### Session summary
46
+
47
+ Planned, implemented, and shipped Phase 18 Step 5 across three stages: a subtractive refactor dropping the `widget` constructor dependency from `AgentTool`, landed as one `refactor:` commit plus two `docs:` commits.
48
+ CI passed, `#424` closed; all commits since `pi-subagents-v16.6.0` are non-releasing (`refactor:`/`docs:`/`style:`), so release-please batches until a `feat`/`fix` lands.
49
+ Net test count 1039 → 1037 (−2 obsolete tests); the pre-completion reviewer returned WARN on a single stale-doc finding that was fixed inline.
50
+
51
+ ### Observations
52
+
53
+ #### What went well
54
+
55
+ - Planning caught that the issue body and roadmap Step 5 both named an `agentActivity`/activity-map dependency that was already removed in [#422]; a `grep` of `src/` for `agentActivity` confirmed only `widget` remained.
56
+ The `/plan-issue` "treat the proposed change as a hypothesis, verify against current code" discipline worked exactly as intended and prevented planning around a phantom dependency.
57
+ - The plan correctly predicted the atomic-commit shape: removing the constructor parameter breaks `index.ts`, `make-deps.ts`, and `agent-tool.test.ts` at typecheck simultaneously, so the implementation folded all edits into one commit with zero rework.
58
+ - Verification ran incrementally — `pnpm run check` plus the affected test files after the edits, then the full suite + root `lint` + `fallow dead-code` at the end of TDD — so no feedback-loop gap.
59
+ - Clean handoff between stages: the planning retro entry flagged the stale `agentActivity` wording in advance, and the TDD stage acted on it without re-deriving the context.
60
+
61
+ #### What caused friction (agent side)
62
+
63
+ - `missing-context` (low impact, recurring) — the `package-pi-subagents` SKILL.md hardcoded test count (`994 tests across 63 files as of Phase 17 Step 4`) was stale by four issues.
64
+ The SKILL.md was edited by `docs:` commits in [#421], [#422], [#423], and [#424], yet none refreshed the count until the [#424] pre-completion reviewer flagged it.
65
+ Impact: one extra `docs:` commit (`18900d3f`) at ship time; no rework, but the precise count is structurally prone to per-step drift and the reviewer caught it only by chance at one of five Phase 18 steps.
66
+
67
+ #### What caused friction (user side)
68
+
69
+ - None.
70
+ The session was fully operator-authored and roadmap-driven; no user intervention or correction was needed at any stage.
71
+
72
+ ### Diagnostic details
73
+
74
+ - **Model-performance correlation** — the `pre-completion-reviewer` subagent ran on `anthropic/claude-sonnet-4-6` (per its agent frontmatter), appropriate for judgment-heavy review; it produced a thorough, correctly-structured PASS/WARN report.
75
+ The parent session switched models several times (`opus-4-8` → `sonnet-4-6` → `deepseek-v4-flash` → `opus-4-8`); execution stayed clean throughout, so no model-quality mismatch was observable.
76
+ - **Escalation-delay tracking** — no `rabbit-hole` friction; no error sequence exceeded one tool call.
77
+ - **Unused-tool detection** — nothing notable; the work was fully specified by the plan and needed no exploratory dispatch.
78
+ - **Feedback-loop gap analysis** — verification was incremental (typecheck + affected tests per edit, full gates at end of TDD, root `lint` + `fallow` before push); no end-only verification gap.
79
+
80
+ ### Changes made
81
+
82
+ 1. Rewrote the opening sentence of the `## Testing` section in `.pi/skills/package-pi-subagents/SKILL.md` to drop both the hardcoded test count and the fork framing.
83
+ The operator judged the precise count (`1037 tests across 64 files as of Phase 18 Step 5`) to provide no real value and to be structurally prone to per-step drift — it had gone stale across four issues ([#421]–[#424]) before the [#424] reviewer caught it.
84
+ The fork is already introduced at the top of the skill, so the Testing section need not re-reference upstream; the sentence now reads "The package has an extensive `vitest` suite."
85
+
86
+ [#421]: https://github.com/gotgenes/pi-packages/issues/421
87
+ [#422]: https://github.com/gotgenes/pi-packages/issues/422
88
+ [#423]: https://github.com/gotgenes/pi-packages/issues/423
89
+ [#424]: https://github.com/gotgenes/pi-packages/issues/424
@@ -0,0 +1,46 @@
1
+ ---
2
+ issue: 425
3
+ issue_title: "pi-subagents: reconcile the public SUBAGENT_EVENTS contract with emitted channels"
4
+ ---
5
+
6
+ # Retro: #425 — pi-subagents: reconcile the public SUBAGENT_EVENTS contract with emitted channels
7
+
8
+ ## Stage: Planning (2026-06-18T00:00:00Z)
9
+
10
+ ### Session summary
11
+
12
+ Planned Phase 18 Step 6: reconcile the public `SUBAGENT_EVENTS` constant map in `src/service/service.ts` with the agent-lifecycle channels the core actually emits.
13
+ The plan removes the vacant `ACTIVITY` constant (breaking) and adds `FAILED`, `COMPACTED`, `CREATED`, and `STEERED`, then updates the architecture doc's lifecycle-events table.
14
+ Two TDD steps: a `feat!:` constant-map reconciliation pinned from both sides (declaration test + existing emission tests), and a `docs:` table update.
15
+
16
+ ### Observations
17
+
18
+ - This is the operator's own issue (author `gotgenes` matches the gh CLI user), so the proposed change was treated as the working hypothesis.
19
+ - Two genuine design choices were surfaced via `ask_user` and resolved: (1) remove `ACTIVITY` rather than emit a real broadcast — the activity tier was already deleted in Phase 18 Steps 1–3, so there is no streaming-progress source; (2) declare all four emitted agent-lifecycle channels including `subagents:steered` (from `steer-tool.ts`), not just the three named in the issue body, so declared == emitted is fully true for the lifecycle bus.
20
+ - Classified as **breaking**: removing a key from the exported `SUBAGENT_EVENTS` `as const` map breaks any consumer referencing `SUBAGENT_EVENTS.ACTIVITY` at the type level.
21
+ Plan uses `feat!:` with a `BREAKING CHANGE:` footer; the footer notes there is no replacement for `ACTIVITY`.
22
+ - Scope boundaries decided: config-domain events (`subagents:settings_loaded`/`settings_changed`) and the child-session seam events (`subagents:child:*`) stay out of `SUBAGENT_EVENTS` — separate domains with their own constant homes. `subagents:record` is an `appendEntry`, not a `pi.events.emit`, so it is not a channel constant.
23
+ - Corrected a stale doc artifact found during planning: the architecture lifecycle-events table listed `subagents:completed` as `{ id, type, status, result?, error? }`, but `buildEventData` emits `{ id, type, description, result, error, status, toolUses, durationMs, tokens? }`.
24
+ The plan fixes this in the same doc step.
25
+ - Public-surface gate: the plan requires running `verify:public-types` in the code step before committing, since `SUBAGENT_EVENTS` is rolled into `dist/public.d.ts`.
26
+ - Value-only reconciliation — no new collaborator, no dependency-wiring change — so the `design-review` checklist surfaces nothing actionable; noted in the plan rather than run as a separate gate.
27
+ - Next step: `/tdd-plan` (the change has a red→green test cycle).
28
+
29
+ ## Stage: Implementation — TDD (2026-06-18T00:00:00Z)
30
+
31
+ ### Session summary
32
+
33
+ Executed both planned TDD steps in two commits: a `feat!:` reconciling the `SUBAGENT_EVENTS` constant map (removed `ACTIVITY`, added `FAILED`/`COMPACTED`/`CREATED`/`STEERED`) pinned by an expanded `service.test.ts` assertion plus an explicit "no vacant `ACTIVITY`" check, then a `docs:` update to the lifecycle-events table and the Phase 18 Step 6 roadmap entry.
34
+ Full suite green at 1038 tests (+1 from the planning baseline of 1037); `check`, root `lint`, `verify:public-types`, and `fallow dead-code` all pass.
35
+
36
+ ### Observations
37
+
38
+ - No deviations from the plan's design.
39
+ The only mid-stream addition was the extra `"ACTIVITY" in SUBAGENT_EVENTS` falsity assertion, which strengthens the breaking-removal coverage beyond what the plan sketched.
40
+ - The breaking change went smoothly: `SUBAGENT_EVENTS.ACTIVITY` had no live consumers, so removal broke only the one service test that asserted it — folded into the same `feat!:` step as planned.
41
+ - Ran `verify:public-types` before committing Step 1 (public-surface gate); the rolled `dist/public.d.ts` regenerated cleanly with the narrowed `as const` literal types.
42
+ - `git diff` since the last tag (`pi-subagents-v16.6.0`) lists files from prior unreleased issues #422/#423/#424; scoped the pre-completion reviewer to #425's two commits to avoid noise.
43
+ - Pre-completion reviewer: WARN (1 non-blocking finding).
44
+ The Phase 18 Mermaid node `S6` was missing the `✅` mark carried by completed nodes S1–S5; fixed by appending `✅` to the node label and amended into the unpushed `docs:` commit.
45
+ All other checklist items PASS or SKIP (no acceptance-criteria section; `service.ts` was not a target of any prior Phase 18 step, so no cross-step invariant at risk).
46
+ - Next step: `/ship-issue`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gotgenes/pi-subagents",
3
- "version": "16.6.0",
3
+ "version": "17.0.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
package/src/index.ts CHANGED
@@ -28,6 +28,7 @@ import { ConcurrencyLimiter } from "#src/lifecycle/concurrency-limiter";
28
28
  import { createSubagentSession, type SubagentSessionDeps } from "#src/lifecycle/create-subagent-session";
29
29
  import { buildParentSnapshot } from "#src/lifecycle/parent-snapshot";
30
30
  import { SubagentManager } from "#src/lifecycle/subagent-manager";
31
+ import { CompositeSubagentObserver } from "#src/observation/composite-subagent-observer";
31
32
  import { type NotificationDetails, NotificationManager } from "#src/observation/notification";
32
33
  import { createNotificationRenderer } from "#src/observation/renderer";
33
34
  import { SubagentEventsObserver } from "#src/observation/subagent-events-observer";
@@ -75,12 +76,18 @@ export default function (pi: ExtensionAPI) {
75
76
  settings.load();
76
77
 
77
78
  // Observer: receives agent lifecycle notifications and dispatches events/notifications.
78
- const observer = new SubagentEventsObserver({
79
+ const eventsObserver = new SubagentEventsObserver({
79
80
  emit: (channel, data) => pi.events.emit(channel, data),
80
81
  appendEntry: (customType, data) => pi.appendEntry(customType, data),
81
82
  notifications,
82
83
  });
83
84
 
85
+ // Fan-out observer: lets the widget subscribe as a second lifecycle consumer
86
+ // while the manager keeps its single-observer contract. The widget is added
87
+ // after construction (it needs the manager); the manager consults the observer
88
+ // only at spawn time, so registering late is safe.
89
+ const observer = new CompositeSubagentObserver([eventsObserver]);
90
+
84
91
  const subagentSessionDeps: SubagentSessionDeps = {
85
92
  io: {
86
93
  detectEnv,
@@ -128,8 +135,9 @@ export default function (pi: ExtensionAPI) {
128
135
  pi.on("session_shutdown", () => lifecycle.handleSessionShutdown());
129
136
 
130
137
  // Live widget: constructed after the manager (it polls listAgents()) and
131
- // injected directly into its consumers no post-construction field write.
138
+ // registered as a lifecycle observer so it self-drives its update timer.
132
139
  const widget = new AgentWidget(manager, registry);
140
+ observer.add(widget);
133
141
 
134
142
  // Grab UI context from first tool execution + clear lingering widget on new turn
135
143
  const toolStart = new ToolStartHandler(widget);
@@ -141,7 +149,7 @@ export default function (pi: ExtensionAPI) {
141
149
 
142
150
  // ---- Agent tool ----
143
151
 
144
- pi.registerTool(new AgentTool(manager, runtime, widget, settings, registry, getAgentDir()).toToolDefinition());
152
+ pi.registerTool(new AgentTool(manager, runtime, settings, registry, getAgentDir()).toToolDefinition());
145
153
 
146
154
  // ---- get_subagent_result tool ----
147
155
 
@@ -0,0 +1,49 @@
1
+ import { debugLog } from "#src/debug";
2
+ import type { SubagentManagerObserver } from "#src/lifecycle/subagent-manager";
3
+ import type { CompactionInfo, Subagent } from "#src/types";
4
+
5
+ /**
6
+ * Fans out SubagentManager lifecycle notifications to multiple observers.
7
+ *
8
+ * Lets the manager keep its single-observer contract while several independent
9
+ * consumers (event/notification dispatch, the reactive widget) subscribe.
10
+ * Each delegate is isolated: a throw in one does not suppress the others.
11
+ */
12
+ export class CompositeSubagentObserver implements SubagentManagerObserver {
13
+ private readonly delegates: SubagentManagerObserver[];
14
+
15
+ constructor(delegates: SubagentManagerObserver[]) {
16
+ this.delegates = [...delegates];
17
+ }
18
+
19
+ /** Register an additional observer (breaks the widget↔manager construction cycle). */
20
+ add(observer: SubagentManagerObserver): void {
21
+ this.delegates.push(observer);
22
+ }
23
+
24
+ onSubagentStarted(record: Subagent): void {
25
+ this.dispatch((o) => o.onSubagentStarted(record), "onSubagentStarted");
26
+ }
27
+
28
+ onSubagentCreated(record: Subagent): void {
29
+ this.dispatch((o) => o.onSubagentCreated(record), "onSubagentCreated");
30
+ }
31
+
32
+ onSubagentCompleted(record: Subagent): void {
33
+ this.dispatch((o) => o.onSubagentCompleted(record), "onSubagentCompleted");
34
+ }
35
+
36
+ onSubagentCompacted(record: Subagent, info: CompactionInfo): void {
37
+ this.dispatch((o) => o.onSubagentCompacted(record, info), "onSubagentCompacted");
38
+ }
39
+
40
+ private dispatch(call: (o: SubagentManagerObserver) => void, label: string): void {
41
+ for (const o of this.delegates) {
42
+ try {
43
+ call(o);
44
+ } catch (err) {
45
+ debugLog(`CompositeSubagentObserver.${label}`, err);
46
+ }
47
+ }
48
+ }
49
+ }
package/src/runtime.ts CHANGED
@@ -9,7 +9,6 @@
9
9
  import { buildParentSnapshot, type ParentSnapshot } from "#src/lifecycle/parent-snapshot";
10
10
  import type { ModelInfo } from "#src/tools/spawn-config";
11
11
  import type { SessionContext } from "#src/types";
12
- import type { AgentActivityTracker } from "#src/ui/agent-activity-tracker";
13
12
 
14
13
  /**
15
14
  * Narrow config subset read by Agent when driving the turn loop (defaultMaxTurns, graceTurns).
@@ -30,11 +29,6 @@ export class SubagentRuntime {
30
29
  // ── Session state (was closure-scoped in index.ts) ───────────────────────
31
30
  /** Active Pi session context — set on session_start, cleared on session_shutdown. */
32
31
  currentCtx: SessionContext | undefined = undefined;
33
- /**
34
- * Per-agent live activity state shared across the notification system,
35
- * widget, and tool handlers. The Map itself is never replaced.
36
- */
37
- readonly agentActivity: Map<string, AgentActivityTracker> = new Map();
38
32
 
39
33
  // ── Session-context methods ──────────────────────────────────────────────
40
34
 
@@ -97,7 +97,10 @@ export interface SubagentsService {
97
97
  export const SUBAGENT_EVENTS = {
98
98
  STARTED: "subagents:started",
99
99
  COMPLETED: "subagents:completed",
100
- ACTIVITY: "subagents:activity",
100
+ FAILED: "subagents:failed",
101
+ COMPACTED: "subagents:compacted",
102
+ CREATED: "subagents:created",
103
+ STEERED: "subagents:steered",
101
104
  } as const;
102
105
 
103
106
  // ---- Accessor functions ----