@gotgenes/pi-subagents 17.1.0 → 17.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/README.md +1 -1
- package/docs/architecture/architecture.md +2 -2
- package/docs/plans/0444-shrink-widget-to-background-agents.md +156 -0
- package/docs/retro/0444-shrink-widget-to-background-agents.md +43 -0
- package/docs/retro/0447-extract-subagents-settings-command.md +45 -0
- package/package.json +1 -1
- package/src/ui/agent-widget.ts +18 -8
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,18 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [17.2.0](https://github.com/gotgenes/pi-packages/compare/pi-subagents-v17.1.0...pi-subagents-v17.2.0) (2026-06-20)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* shrink agent widget to background runs only ([#444](https://github.com/gotgenes/pi-packages/issues/444)) ([76463b4](https://github.com/gotgenes/pi-packages/commit/76463b47227961226dbce5efb70a71d596fe092e))
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Documentation
|
|
17
|
+
|
|
18
|
+
* note background-only widget in README and roadmap ([#444](https://github.com/gotgenes/pi-packages/issues/444)) ([437332f](https://github.com/gotgenes/pi-packages/commit/437332fa4a1fbb2f0a0358495454847bc597ae13))
|
|
19
|
+
|
|
8
20
|
## [17.1.0](https://github.com/gotgenes/pi-packages/compare/pi-subagents-v17.0.1...pi-subagents-v17.1.0) (2026-06-20)
|
|
9
21
|
|
|
10
22
|
|
package/README.md
CHANGED
|
@@ -61,7 +61,7 @@ Background agents return an ID immediately and notify you on completion.
|
|
|
61
61
|
|
|
62
62
|
## UI
|
|
63
63
|
|
|
64
|
-
The extension renders a persistent widget above the editor showing
|
|
64
|
+
The extension renders a persistent widget above the editor showing active background agents (foreground runs are rendered inline by the `subagent` tool's progress stream):
|
|
65
65
|
|
|
66
66
|
```text
|
|
67
67
|
● Agents
|
|
@@ -966,7 +966,7 @@ Outcome: new `subagents-settings.ts` (~80 LOC) and focused command registered; `
|
|
|
966
966
|
|
|
967
967
|
`Release: independent`
|
|
968
968
|
|
|
969
|
-
### Step 3 — Shrink widget to background agents only ([#444])
|
|
969
|
+
### ✅ Step 3 — Shrink widget to background agents only ([#444])
|
|
970
970
|
|
|
971
971
|
Smell: Category C (coupling) — the widget shows all agents including foreground ones, duplicating the `subagent` tool's inline `onUpdate` stream for foreground runs.
|
|
972
972
|
Target files:
|
|
@@ -1063,7 +1063,7 @@ Outcome: test clone groups ≤ 10 (from 16); `subagent-manager.test.ts` uses sha
|
|
|
1063
1063
|
flowchart LR
|
|
1064
1064
|
S1["✅ Step 1 - Spike (#446)"]
|
|
1065
1065
|
S2["✅ Step 2 - Settings command (#447)"]
|
|
1066
|
-
S3["Step 3 - Background widget (#444)"]
|
|
1066
|
+
S3["✅ Step 3 - Background widget (#444)"]
|
|
1067
1067
|
S4["Step 4 - Native session nav (#445)"]
|
|
1068
1068
|
S5["Step 5 - Dissolve /agents + viewer (#442)"]
|
|
1069
1069
|
S6["Step 6 - Remove definition mgmt (#441)"]
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
---
|
|
2
|
+
issue: 444
|
|
3
|
+
issue_title: "pi-subagents: shrink the agent widget to background runs only"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Shrink the agent widget to background runs only
|
|
7
|
+
|
|
8
|
+
## Release Recommendation
|
|
9
|
+
|
|
10
|
+
**Release:** ship independently
|
|
11
|
+
|
|
12
|
+
Phase 19 Step 3 ([architecture roadmap][arch]) is tagged `Release: independent` — it is not a member of any release batch.
|
|
13
|
+
The change is self-contained to `agent-widget.ts` and its tests, touches no public surface, and ADR-0004 explicitly notes it is "independent of the spike."
|
|
14
|
+
|
|
15
|
+
## Problem Statement
|
|
16
|
+
|
|
17
|
+
The above-editor agent widget currently renders every agent — foreground and background alike.
|
|
18
|
+
For foreground runs the `subagent` tool's inline `onUpdate` stream already renders live progress, so the widget duplicates that display (ADR-0004 Decision A).
|
|
19
|
+
The widget should survive only as the **background-agent status surface**: the one place with no inline tool-call display, which must represent N parallel background agents at once.
|
|
20
|
+
|
|
21
|
+
There is also a latent inconsistency to fix.
|
|
22
|
+
`AgentWidget` calls `manager.listAgents()` at two sites — `update()` (which feeds `seedFinishedAgents`, `assembleWidgetState`, and `clearWidget`) and `renderWidget()` (the tree map).
|
|
23
|
+
Filtering at only one site would leave the other rendering foreground agents.
|
|
24
|
+
|
|
25
|
+
## Goals
|
|
26
|
+
|
|
27
|
+
- The widget shows only background agents (`record.invocation?.runInBackground === true`); foreground agents never appear.
|
|
28
|
+
- Both `listAgents()` call sites are funneled through a single private accessor that applies the background predicate once at the source.
|
|
29
|
+
- The foreground/widget duplication called out by ADR-0004 Decision A is eliminated.
|
|
30
|
+
- This is a UI behavior refinement, not a public-API or config change — it is **non-breaking** (no exported surface, default, or config shape changes on upgrade).
|
|
31
|
+
|
|
32
|
+
## Non-Goals
|
|
33
|
+
|
|
34
|
+
- Relabeling the widget heading from `Agents` to `Background agents` (see Open Questions) — not requested; keep scope tight.
|
|
35
|
+
- Any change to `widget-renderer.ts` rendering logic — it has no foreground-specific path; the fix lives entirely at the data source in `agent-widget.ts`.
|
|
36
|
+
- Other `listAgents()` consumers (`agent-menu.ts`) — those are removed wholesale by Phase 19 Step 5 ([#442]), out of scope here.
|
|
37
|
+
- Native session navigation ([#445], Step 4) and the `/subagents-settings` extraction ([#447], Step 2).
|
|
38
|
+
|
|
39
|
+
## Background
|
|
40
|
+
|
|
41
|
+
Relevant modules:
|
|
42
|
+
|
|
43
|
+
- `src/ui/agent-widget.ts` — `AgentWidget implements SubagentManagerObserver`; self-drives an 80 ms render loop from lifecycle events (Step 4 self-drive, [#423]).
|
|
44
|
+
`update()` reads `manager.listAgents()` into a local `allAgents`, then passes it to `seedFinishedAgents()`, `assembleWidgetState()`, and (on the idle path) `clearWidget()`.
|
|
45
|
+
`renderWidget(tui, theme)` independently calls `manager.listAgents().map(r => this.toWidgetAgent(r))` to build the tree.
|
|
46
|
+
- `src/ui/widget-renderer.ts` — pure rendering; `renderWidgetLines()` categorizes agents into running/queued/finished and emits tree lines.
|
|
47
|
+
It has no knowledge of foreground vs. background — it renders whatever list it receives.
|
|
48
|
+
- `Subagent.invocation` (`src/lifecycle/subagent.ts`) — a readonly `AgentInvocation | undefined` set once at construction.
|
|
49
|
+
`AgentInvocation.runInBackground` (`src/types.ts`) is the reliable signal: set by `spawn-config.ts` → `AgentInvocation.runInBackground` → stored on `Subagent.invocation`.
|
|
50
|
+
- `AgentSummary` (in `agent-widget.ts`) — the minimal `{ id, status, completedAt }` shape consumed by `assembleWidgetState`/`clearWidget`.
|
|
51
|
+
`Subagent` is structurally assignable to it, so the accessor can return `Subagent[]` without an interface change.
|
|
52
|
+
|
|
53
|
+
Constraint from AGENTS.md / package skill: pi-subagents is a minimal core; this is a consumer-side UI behavior change with no policy or core impact.
|
|
54
|
+
|
|
55
|
+
## Design Overview
|
|
56
|
+
|
|
57
|
+
Introduce one private accessor on `AgentWidget` and route both existing call sites through it:
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
/** Background agents only — the widget's sole audience (ADR-0004 Decision A). */
|
|
61
|
+
private listBackgroundAgents(): Subagent[] {
|
|
62
|
+
return this.manager
|
|
63
|
+
.listAgents()
|
|
64
|
+
.filter(record => record.invocation?.runInBackground === true);
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Call-site changes:
|
|
69
|
+
|
|
70
|
+
- `update()` — replace `const allAgents = this.manager.listAgents();` with `const backgroundAgents = this.listBackgroundAgents();` and rename the downstream references (`seedFinishedAgents`, `assembleWidgetState`, `clearWidget`).
|
|
71
|
+
- `renderWidget()` — replace `this.manager.listAgents().map(...)` with `this.listBackgroundAgents().map(...)`.
|
|
72
|
+
|
|
73
|
+
The predicate `record.invocation?.runInBackground === true` is applied exactly once, at the funnel.
|
|
74
|
+
`undefined` invocation (or `runInBackground` absent/false) is treated as foreground and excluded.
|
|
75
|
+
|
|
76
|
+
Edge cases verified:
|
|
77
|
+
|
|
78
|
+
- **`clearWidget` stale-purge:** it deletes `finishedTurnAge` entries for IDs "no longer in the list."
|
|
79
|
+
Because `seedFinishedAgents` only ever seeds from the background-filtered list, no foreground agent is ever tracked, so purging against the background-only list cannot drop a still-relevant entry — no behavior regression.
|
|
80
|
+
- **Queued agents:** the concurrency limiter queues only background agents (foreground bypasses the queue), so they carry `runInBackground === true` and remain visible; the queued-count display stays accurate.
|
|
81
|
+
- **All-foreground case:** `update()` sees an empty background list → `assembleWidgetState` reports no active/finished → `clearWidget` → widget never registers. `renderWidget()` is consequently never invoked.
|
|
82
|
+
- **Mixed case (the latent bug):** one background running + one foreground running → `update()` registers the widget (1 background active); `renderWidget()` must also filter, or it renders both.
|
|
83
|
+
Routing both sites through the accessor closes this.
|
|
84
|
+
|
|
85
|
+
## Module-Level Changes
|
|
86
|
+
|
|
87
|
+
- `src/ui/agent-widget.ts` — add private `listBackgroundAgents()`; route `update()` and `renderWidget()` through it; rename the `update()` local `allAgents` → `backgroundAgents`.
|
|
88
|
+
- `src/ui/widget-renderer.ts` — **no change.**
|
|
89
|
+
Verified there is no foreground-specific rendering path; filtering at the data source is sufficient.
|
|
90
|
+
Listed only to record the verification.
|
|
91
|
+
- `test/ui/agent-widget.test.ts` — migrate existing widget fixtures to set `invocation: { runInBackground: true }` (otherwise the new filter excludes them); add background-only filtering tests.
|
|
92
|
+
- `packages/pi-subagents/README.md` — line ~64, change "showing all active agents" → "showing active background agents" (stale prose once foreground is excluded).
|
|
93
|
+
- `packages/pi-subagents/docs/architecture/architecture.md` — mark Step 3 ✅ with a `Landed`/`Outcome` note; the `src/ui/agent-widget.ts` tree caption at line ~342 stays accurate (generic "live status widget").
|
|
94
|
+
|
|
95
|
+
Grep sweep performed for stale references to "all agents" / "showing all active agents" across `src/`, `test/`, `.pi/skills/package-pi-subagents/SKILL.md`, and `docs/`:
|
|
96
|
+
|
|
97
|
+
- `README.md:64` — stale, updated above.
|
|
98
|
+
- `.pi/skills/package-pi-subagents/SKILL.md:57` ("widget ─polls─→ Subagent records (listAgents)") — still accurate (it does poll `listAgents`, now filtered); no edit.
|
|
99
|
+
- `docs/comparison-with-upstream.md:29`, `architecture.md:342` — generic "live above-editor widget"; remain accurate.
|
|
100
|
+
|
|
101
|
+
## Test Impact Analysis
|
|
102
|
+
|
|
103
|
+
1. **New tests enabled by the change:**
|
|
104
|
+
- `update()` excludes foreground agents — an all-foreground agent list leaves the widget unregistered (`lastContent()` undefined).
|
|
105
|
+
- `renderWidget()` excludes foreground agents — a mixed background+foreground list renders only the background agent's description in the tree (the foreground description is absent).
|
|
106
|
+
This pins the previously-latent two-site inconsistency.
|
|
107
|
+
2. **Tests that become redundant:** none are removed.
|
|
108
|
+
The existing widget-behavior fixtures (`makeWidget` in the two `describe` blocks, plus the projection test's `createTestSubagent`) are migrated to set `invocation: { runInBackground: true }` so they continue to exercise the same lifecycle/seeding/self-drive behavior under the new filter.
|
|
109
|
+
3. **Tests that must stay as-is:** the `assembleWidgetState` pure-function suite — it calls the pure function directly with `AgentSummary[]`, not through the accessor, so it is unaffected by the filter and continues to pin the counting logic.
|
|
110
|
+
|
|
111
|
+
## Invariants at risk
|
|
112
|
+
|
|
113
|
+
This step touches `agent-widget.ts`, which prior phases already refactored.
|
|
114
|
+
The fixture migration must keep these invariants pinned (the tests stay, only their `invocation` field is added):
|
|
115
|
+
|
|
116
|
+
- **Self-drive from lifecycle ([#423]):** `onSubagentStarted`/`onSubagentCreated` start the 80 ms timer and render; `onSubagentCompleted`/`onSubagentCompacted` render.
|
|
117
|
+
Pinned by the "self-drives from lifecycle notifications" describe block — fixtures migrated to background invocation so the manager stub's agents survive the filter.
|
|
118
|
+
- **`seedFinishedAgents` idempotency / linger aging ([#374]):** finished agents seed once, age out after 1 turn (errors after 2), and `update()` never advances the age without a turn.
|
|
119
|
+
Pinned by the "update self-seeds finished agents" describe block — fixtures migrated to background invocation.
|
|
120
|
+
|
|
121
|
+
A later step must not regress these with a green suite; the migration preserves the assertions verbatim apart from the `invocation` field.
|
|
122
|
+
|
|
123
|
+
## TDD Order
|
|
124
|
+
|
|
125
|
+
1. **`test:` migrate widget fixtures to background invocation (preparatory / tidy-first).**
|
|
126
|
+
Surface: `test/ui/agent-widget.test.ts`.
|
|
127
|
+
Add `invocation: { runInBackground: true }` to the `makeWidget` agent stubs (both `describe` blocks) and to the projection test's `createTestSubagent` call.
|
|
128
|
+
No production change — the filter does not exist yet, so the field is inert and the suite stays green.
|
|
129
|
+
This makes the next step a clean addition rather than a mass fixture rewrite.
|
|
130
|
+
Commit: `test: set background invocation on agent-widget fixtures (#444)`.
|
|
131
|
+
2. **`feat:` filter the widget to background agents only.**
|
|
132
|
+
Red: add (a) an all-foreground list leaves the widget unregistered, and (b) a mixed background+foreground list renders only the background agent in `renderWidget()`.
|
|
133
|
+
Green: add the private `listBackgroundAgents()` accessor; route both `update()` and `renderWidget()` through it; rename the `update()` local to `backgroundAgents`.
|
|
134
|
+
Commit: `feat: shrink agent widget to background runs only (#444)`.
|
|
135
|
+
3. **`docs:` update widget docs for background-only behavior.**
|
|
136
|
+
Update `README.md` ("all active agents" → "active background agents") and mark Phase 19 Step 3 ✅ with a `Landed`/`Outcome` note in `docs/architecture/architecture.md`.
|
|
137
|
+
Commit: `docs: note background-only widget in README and roadmap (#444)`.
|
|
138
|
+
|
|
139
|
+
## Risks and Mitigations
|
|
140
|
+
|
|
141
|
+
- **Mass test breakage when the filter lands** — every `update()`-driven fixture would be filtered out and assertions would fail.
|
|
142
|
+
Mitigation: TDD step 1 migrates all fixtures first (inert until the filter exists), so step 2 only adds new tests.
|
|
143
|
+
- **A foreground agent the operator expected to see disappears** — intended per ADR-0004 Decision A; the inline `onUpdate` stream is authoritative for foreground runs.
|
|
144
|
+
- **`clearWidget` stale-purge dropping a relevant entry** — analyzed in Design Overview; foreground agents are never seeded, so purging against the background-only list is correct.
|
|
145
|
+
|
|
146
|
+
## Open Questions
|
|
147
|
+
|
|
148
|
+
- Should the widget heading read `Background agents` instead of `Agents`?
|
|
149
|
+
Deferred — not in the issue scope; revisit if the bare label reads as ambiguous in practice.
|
|
150
|
+
|
|
151
|
+
[arch]: ../architecture/architecture.md
|
|
152
|
+
[#442]: https://github.com/gotgenes/pi-packages/issues/442
|
|
153
|
+
[#445]: https://github.com/gotgenes/pi-packages/issues/445
|
|
154
|
+
[#447]: https://github.com/gotgenes/pi-packages/issues/447
|
|
155
|
+
[#423]: https://github.com/gotgenes/pi-packages/issues/423
|
|
156
|
+
[#374]: https://github.com/gotgenes/pi-packages/issues/374
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
---
|
|
2
|
+
issue: 444
|
|
3
|
+
issue_title: "pi-subagents: shrink the agent widget to background runs only"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Retro: #444 — pi-subagents: shrink the agent widget to background runs only
|
|
7
|
+
|
|
8
|
+
## Stage: Planning (2026-06-20T18:47:05Z)
|
|
9
|
+
|
|
10
|
+
### Session summary
|
|
11
|
+
|
|
12
|
+
Produced a numbered implementation plan for Phase 19 Step 3: shrink `AgentWidget` to background agents only by funneling both `manager.listAgents()` call sites (`update()` and `renderWidget()`) through a single private `listBackgroundAgents()` accessor that applies `record.invocation?.runInBackground === true` once at the source.
|
|
13
|
+
Confirmed the change is single-package (`pi-subagents`), non-breaking, and `Release: independent` per the architecture roadmap.
|
|
14
|
+
|
|
15
|
+
### Observations
|
|
16
|
+
|
|
17
|
+
- Issue author is the operator (`gotgenes`) and the proposed change is unambiguous, so the `ask-user` gate was skipped.
|
|
18
|
+
The design is a legitimate improvement (removes the two-site duplication and fixes the latent inconsistency), not procedure-splitting.
|
|
19
|
+
- `widget-renderer.ts` needs **no** code change — it has no foreground-specific path; filtering at the data source in `agent-widget.ts` is sufficient.
|
|
20
|
+
Listed in Module-Level Changes only to record the verification.
|
|
21
|
+
- Key risk is mass test breakage: the existing `update()`-driven fixtures (two `makeWidget` helpers + the projection test's `createTestSubagent`) would all be filtered out once the predicate lands.
|
|
22
|
+
Mitigated with a tidy-first TDD step 1 that adds `invocation: { runInBackground: true }` to fixtures while the filter does not yet exist (inert), so step 2 only adds new tests.
|
|
23
|
+
- `assembleWidgetState` pure-function tests are unaffected — they call the function directly with `AgentSummary[]`, bypassing the accessor.
|
|
24
|
+
- Verified `clearWidget`'s stale-purge has no regression: foreground agents are never seeded into `finishedTurnAge`, so purging against the background-only list is correct.
|
|
25
|
+
- Doc grep surfaced one stale prose line (`README.md:64` "showing all active agents"); SKILL.md and `comparison-with-upstream.md` references remain accurate.
|
|
26
|
+
- Deferred (Open Question): relabeling the widget heading `Agents` → `Background agents` — out of scope for this issue.
|
|
27
|
+
|
|
28
|
+
## Stage: Implementation — TDD (2026-06-20T19:47:34Z)
|
|
29
|
+
|
|
30
|
+
### Session summary
|
|
31
|
+
|
|
32
|
+
Executed all three planned TDD cycles plus one reviewer-driven cleanup: (1) `test:` migrated the `agent-widget.test.ts` fixtures to a background invocation, (2) `feat:` added the private `listBackgroundAgents()` accessor and routed both `update()` and `renderWidget()` through it, (3) `docs:` updated `README.md` and marked roadmap Step 3 ✅.
|
|
33
|
+
Test count went from 24 to 26 in `agent-widget.test.ts` (two new background-only filtering tests); full pi-subagents suite is 1064 passing.
|
|
34
|
+
|
|
35
|
+
### Observations
|
|
36
|
+
|
|
37
|
+
- The tidy-first fixture migration worked exactly as planned — adding `invocation: { runInBackground: true }` was inert (suite stayed green) until the filter landed, so step 2 was a pure addition with no fixture churn.
|
|
38
|
+
- New-test fixtures default the shared `makeWidget` helper to background invocation via `{ invocation: { runInBackground: true }, ...a }`, letting per-agent `invocation` override for the mixed/foreground cases in the new `describe` block.
|
|
39
|
+
- `widget-renderer.ts` needed no change, as the plan predicted — the filter at the single `listBackgroundAgents()` funnel is sufficient.
|
|
40
|
+
- Pre-completion reviewer: PASS.
|
|
41
|
+
One non-blocking naming WARN — `clearWidget`'s `allAgents` parameter and JSDoc were stale after the refactor (it now receives only background agents); fixed in a follow-up `refactor:` commit (`backgroundAgents`).
|
|
42
|
+
Landed as a separate commit rather than amending the feat commit because HEAD was already the `docs:` commit and the fix must not land in a `docs:` commit.
|
|
43
|
+
- All gates green: `pnpm run check`, root `pnpm run lint`, full vitest (1064), `pnpm fallow dead-code`.
|
|
@@ -44,3 +44,48 @@ Full suite, `tsc`, root lint, and `fallow dead-code` all green.
|
|
|
44
44
|
- Pre-completion reviewer: WARN.
|
|
45
45
|
Reviewer warnings: one non-blocking finding — `.pi/skills/package-pi-subagents/SKILL.md` records `ui/` as 10 modules (now 11); the plan intentionally deferred this coarse-summary update to a later Phase 19 doc-sync.
|
|
46
46
|
No FAILs; all deterministic checks PASS, verbatim-lift fidelity and ISP of both narrow interfaces confirmed.
|
|
47
|
+
|
|
48
|
+
## Stage: Final Retrospective (2026-06-20T18:36:58Z)
|
|
49
|
+
|
|
50
|
+
### Session summary
|
|
51
|
+
|
|
52
|
+
Shipped Phase 19 Step 2 (#447) end to end across planning, TDD, and ship stages, releasing `pi-subagents` `17.1.0`.
|
|
53
|
+
The one off-script event was a user-reported Mermaid syntax error in `architecture.md`, which I localized by extracting all six diagram blocks and running `mmdc` on each, then fixed by quoting the offending flowchart node labels.
|
|
54
|
+
Execution was otherwise clean: two green TDD cycles (+11 tests), a WARN pre-completion review (one intentionally-deferred doc finding), and a correct `UNSTABLE`/`GITHUB_TOKEN` release-merge fallback.
|
|
55
|
+
|
|
56
|
+
### Observations
|
|
57
|
+
|
|
58
|
+
#### What went well
|
|
59
|
+
|
|
60
|
+
- Localizing the Mermaid error with the diagram name wrong: the user called it the "State dependency diagram" but the broken block was the "Step dependency diagram" flowchart.
|
|
61
|
+
Rather than guess from the ambiguous name (I had glanced at the `stateDiagram-v2` lifecycle and the `classDiagram` first), I scripted extraction of all six `mermaid` fences and ran `mmdc` on each — block 6 failed with `got 'PS'`, pinpointing the exact line.
|
|
62
|
+
Brute-force validation beat name-matching.
|
|
63
|
+
- The release-merge fallback worked exactly as the ship prompt describes: `release_pr_merge` refused on `merge_state: UNSTABLE`, the status-check rollup was empty (the `GITHUB_TOKEN` no-checks case), and `gh pr merge 450 --rebase` + `git pull --ff-only` landed `17.1.0` linearly.
|
|
64
|
+
|
|
65
|
+
#### What caused friction (agent side)
|
|
66
|
+
|
|
67
|
+
- `missing-context` (prior session) — the broken diagram was authored in `74e2374f docs: mark Phase 19 Step 1 spike complete (#446)`: a flowchart node label `S1[✅ Step 1 - Spike (#446)]` with unquoted parentheses, which Mermaid parses as a nested round-node shape (`Expecting ... got 'PS'`).
|
|
68
|
+
It was committed without an `mmdc` pass and slipped through `pnpm run lint` (rumdl validates markdown, not Mermaid semantics) and CI.
|
|
69
|
+
Impact: surfaced two sessions later as a user-caught defect; one extra `docs:` fix commit (`90ca6e2d`) this session.
|
|
70
|
+
Root cause is a coverage gap: the `mermaid` skill's pitfall list does not mention parentheses/shape-delimiter characters in node labels, so the exact failure mode was undocumented.
|
|
71
|
+
|
|
72
|
+
#### What caused friction (user side)
|
|
73
|
+
|
|
74
|
+
- None material.
|
|
75
|
+
The diagram-name mismatch ("State" vs "Step" dependency diagram) added one or two orienting reads but did not cause rework — the all-blocks `mmdc` sweep absorbed the ambiguity.
|
|
76
|
+
|
|
77
|
+
### Diagnostic details
|
|
78
|
+
|
|
79
|
+
- **Model-performance correlation** — the only subagent dispatch was the `pre-completion-reviewer` on `anthropic/claude-sonnet-4-6` (judgment-heavy review work); appropriate match, no over/under-powered mismatch.
|
|
80
|
+
- **Escalation-delay tracking** — the Mermaid detour resolved in ~5 progress-making tool calls (extract blocks → `mmdc` each → read error → quote labels → re-validate); not a rabbit-hole, no subagent escalation warranted.
|
|
81
|
+
- **Unused-tool detection** — none; `mmdc` (the correct validator) was used directly.
|
|
82
|
+
- **Feedback-loop gap analysis** — verification ran incrementally throughout TDD (per-file `vitest` in each red/green, `pnpm run check` right after the interface-bearing wiring step, full suite + root lint + `fallow dead-code` at the end), not just terminally.
|
|
83
|
+
|
|
84
|
+
### Changes made
|
|
85
|
+
|
|
86
|
+
1. `.pi/skills/mermaid/SKILL.md` — added a "Parentheses and special characters in node labels" pitfall (with the `S1[✅ Step 1 - Spike (#446)]` WRONG/RIGHT example from this session) covering the `(`/`[`/`{`/`:` shape-delimiter trap that produced the `got 'PS'` parse error.
|
|
87
|
+
|
|
88
|
+
### Follow-up (not implemented — suggest a GitHub issue + `/plan-issue`)
|
|
89
|
+
|
|
90
|
+
1. **CI Mermaid-validation gate** — run `mmdc` over every `mermaid` fence as part of `pnpm run lint` or a pre-commit hook, so a broken diagram (like `74e2374f`'s unquoted-parens node) fails at author time instead of surviving to a user-caught defect.
|
|
91
|
+
Out of retro scope (infra, touches `package.json`/lint config/hooks); record as its own issue.
|
package/package.json
CHANGED
package/src/ui/agent-widget.ts
CHANGED
|
@@ -148,6 +148,16 @@ export class AgentWidget implements SubagentManagerObserver {
|
|
|
148
148
|
return age < maxAge;
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
+
/**
|
|
152
|
+
* Background agents only — the widget's sole audience (ADR-0004 Decision A).
|
|
153
|
+
* Foreground runs are rendered by the `subagent` tool's inline `onUpdate` stream,
|
|
154
|
+
* so funneling both `listAgents()` call sites through this accessor applies the
|
|
155
|
+
* background predicate exactly once at the source.
|
|
156
|
+
*/
|
|
157
|
+
private listBackgroundAgents(): Subagent[] {
|
|
158
|
+
return this.manager.listAgents().filter(record => record.invocation?.runInBackground === true);
|
|
159
|
+
}
|
|
160
|
+
|
|
151
161
|
/** Project a live Subagent record onto a pure-data WidgetAgent snapshot. */
|
|
152
162
|
private toWidgetAgent(record: Subagent): WidgetAgent {
|
|
153
163
|
return {
|
|
@@ -172,7 +182,7 @@ export class AgentWidget implements SubagentManagerObserver {
|
|
|
172
182
|
/** Delegate rendering to the pure widget-renderer module. */
|
|
173
183
|
private renderWidget(tui: any, theme: Theme): string[] {
|
|
174
184
|
return renderWidgetLines({
|
|
175
|
-
agents: this.
|
|
185
|
+
agents: this.listBackgroundAgents().map(r => this.toWidgetAgent(r)),
|
|
176
186
|
registry: this.registry,
|
|
177
187
|
spinnerFrame: this.widgetFrame,
|
|
178
188
|
terminalWidth: tui.terminal.columns,
|
|
@@ -183,10 +193,10 @@ export class AgentWidget implements SubagentManagerObserver {
|
|
|
183
193
|
|
|
184
194
|
/**
|
|
185
195
|
* Unregister the widget, clear the status bar, stop the interval timer, and
|
|
186
|
-
* purge stale `finishedTurnAge` entries for agents no longer in `
|
|
196
|
+
* purge stale `finishedTurnAge` entries for agents no longer in `backgroundAgents`.
|
|
187
197
|
* Called only from `update`'s idle path — not from `dispose`.
|
|
188
198
|
*/
|
|
189
|
-
private clearWidget(
|
|
199
|
+
private clearWidget(backgroundAgents: readonly AgentSummary[]): void {
|
|
190
200
|
if (this.widgetRegistered) {
|
|
191
201
|
this.uiCtx!.setWidget("agents", undefined);
|
|
192
202
|
this.widgetRegistered = false;
|
|
@@ -198,7 +208,7 @@ export class AgentWidget implements SubagentManagerObserver {
|
|
|
198
208
|
}
|
|
199
209
|
if (this.widgetInterval) { clearInterval(this.widgetInterval); this.widgetInterval = undefined; }
|
|
200
210
|
for (const [id] of this.finishedTurnAge) {
|
|
201
|
-
if (!
|
|
211
|
+
if (!backgroundAgents.some(a => a.id === id)) this.finishedTurnAge.delete(id);
|
|
202
212
|
}
|
|
203
213
|
}
|
|
204
214
|
|
|
@@ -240,12 +250,12 @@ export class AgentWidget implements SubagentManagerObserver {
|
|
|
240
250
|
update() {
|
|
241
251
|
if (!this.uiCtx) return;
|
|
242
252
|
|
|
243
|
-
const
|
|
244
|
-
this.seedFinishedAgents(
|
|
245
|
-
const state = assembleWidgetState(
|
|
253
|
+
const backgroundAgents = this.listBackgroundAgents();
|
|
254
|
+
this.seedFinishedAgents(backgroundAgents);
|
|
255
|
+
const state = assembleWidgetState(backgroundAgents, (id, status) => this.shouldShowFinished(id, status));
|
|
246
256
|
|
|
247
257
|
if (!state.hasActive && !state.hasFinished) {
|
|
248
|
-
this.clearWidget(
|
|
258
|
+
this.clearWidget(backgroundAgents);
|
|
249
259
|
return;
|
|
250
260
|
}
|
|
251
261
|
|