@gotgenes/pi-subagents 16.6.0 → 17.0.1

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 (36) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/public.d.ts +4 -1
  3. package/docs/architecture/architecture.md +238 -169
  4. package/docs/architecture/history/phase-18-reconsider-ui.md +166 -0
  5. package/docs/decisions/0004-reconsider-ui-direction.md +220 -0
  6. package/docs/plans/0422-delete-activity-tracker-ui-observer.md +204 -0
  7. package/docs/plans/0423-widget-self-drive-from-lifecycle.md +264 -0
  8. package/docs/plans/0424-drop-widget-dep-from-subagent-tool.md +166 -0
  9. package/docs/plans/0425-reconcile-subagent-events-contract.md +144 -0
  10. package/docs/plans/0426-consolidate-residual-test-clone-families.md +235 -0
  11. package/docs/plans/0427-reconsider-ui-direction.md +212 -0
  12. package/docs/plans/0446-spike-session-navigation-entry-criteria.md +171 -0
  13. package/docs/plans/0448-block-disabled-agent-spawn.md +207 -0
  14. package/docs/retro/0421-migrate-activity-readers-to-record-getters.md +49 -0
  15. package/docs/retro/0422-delete-activity-tracker-ui-observer.md +97 -0
  16. package/docs/retro/0423-widget-self-drive-from-lifecycle.md +112 -0
  17. package/docs/retro/0424-drop-widget-dep-from-subagent-tool.md +89 -0
  18. package/docs/retro/0425-reconcile-subagent-events-contract.md +101 -0
  19. package/docs/retro/0426-consolidate-residual-test-clone-families.md +100 -0
  20. package/docs/retro/0427-reconsider-ui-direction.md +102 -0
  21. package/docs/retro/0446-spike-session-navigation-entry-criteria.md +99 -0
  22. package/docs/retro/0448-block-disabled-agent-spawn.md +42 -0
  23. package/package.json +1 -1
  24. package/src/index.ts +11 -3
  25. package/src/observation/composite-subagent-observer.ts +49 -0
  26. package/src/runtime.ts +0 -6
  27. package/src/service/service.ts +4 -1
  28. package/src/tools/agent-tool.ts +1 -36
  29. package/src/tools/background-spawner.ts +1 -25
  30. package/src/tools/foreground-runner.ts +1 -30
  31. package/src/tools/helpers.ts +3 -2
  32. package/src/tools/spawn-config.ts +6 -0
  33. package/src/types.ts +1 -1
  34. package/src/ui/agent-widget.ts +34 -14
  35. package/src/ui/agent-activity-tracker.ts +0 -84
  36. package/src/ui/ui-observer.ts +0 -61
package/CHANGELOG.md CHANGED
@@ -5,6 +5,25 @@ 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.0.1](https://github.com/gotgenes/pi-packages/compare/pi-subagents-v17.0.0...pi-subagents-v17.0.1) (2026-06-20)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * **pi-subagents:** exclude disabled agents from the subagent tool description ([#448](https://github.com/gotgenes/pi-packages/issues/448)) ([9a43414](https://github.com/gotgenes/pi-packages/commit/9a43414b3e15f0c978db9d468b737b13b801fdb2))
14
+ * **pi-subagents:** return an error when spawning a disabled agent type ([#448](https://github.com/gotgenes/pi-packages/issues/448)) ([0e0225e](https://github.com/gotgenes/pi-packages/commit/0e0225e167596217dc7b1d99dd6269600d65326b))
15
+
16
+ ## [17.0.0](https://github.com/gotgenes/pi-packages/compare/pi-subagents-v16.6.0...pi-subagents-v17.0.0) (2026-06-18)
17
+
18
+
19
+ ### ⚠ BREAKING CHANGES
20
+
21
+ * SUBAGENT_EVENTS.ACTIVITY ("subagents:activity") is removed. It was never emitted, so no consumer could act on it, and there is no replacement — the activity tier was removed in Phase 18. Consumers should subscribe to the now-declared FAILED, COMPACTED, CREATED, and STEERED channels instead.
22
+
23
+ ### Features
24
+
25
+ * reconcile SUBAGENT_EVENTS with emitted channels ([#425](https://github.com/gotgenes/pi-packages/issues/425)) ([ba0c48b](https://github.com/gotgenes/pi-packages/commit/ba0c48b294123c1498db2040b1037adcd7ad4e4e))
26
+
8
27
  ## [16.6.0](https://github.com/gotgenes/pi-packages/compare/pi-subagents-v16.5.0...pi-subagents-v16.6.0) (2026-06-17)
9
28
 
10
29
 
package/dist/public.d.ts CHANGED
@@ -154,7 +154,10 @@ interface SubagentsService {
154
154
  declare const SUBAGENT_EVENTS: {
155
155
  readonly STARTED: "subagents:started";
156
156
  readonly COMPLETED: "subagents:completed";
157
- readonly ACTIVITY: "subagents:activity";
157
+ readonly FAILED: "subagents:failed";
158
+ readonly COMPACTED: "subagents:compacted";
159
+ readonly CREATED: "subagents:created";
160
+ readonly STEERED: "subagents:steered";
158
161
  };
159
162
  /** Publish the SubagentsService on globalThis for cross-extension access. */
160
163
  declare function publishSubagentsService(service: SubagentsService): void;
@@ -13,8 +13,8 @@ This document describes the architecture of the pi-subagents fork: a focused, co
13
13
  4. **No time-based scheduling** — cron-style timed dispatch (upstream's `schedule.ts` subsystem) is removed from the core (#52).
14
14
  Timed dispatch is a separate concern that any extension can implement by calling `spawn()` on the published API.
15
15
  The max-concurrent admission gate is not scheduling in this sense — concurrency management stays in core.
16
- 5. **UI extraction is deferred** — the widget, conversation viewer, and `/agents` command menu stay in the core for now.
17
- They are the first candidate for extraction once the API boundary is proven stable.
16
+ 5. **UI is an in-core, substitutable consumer** — [ADR-0004](../decisions/0004-reconsider-ui-direction.md) records the per-component decision: the widget shrinks to background agents only, the bespoke conversation viewer is replaced by native session navigation, the `/agents` command is dissolved into focused surfaces, and the surviving UI stays in the core as a reactive consumer (not extracted to a separate package).
17
+ Extraction remains an available future option because the composition invariant holds — the core is byte-for-byte identical with or without a given UI consumer.
18
18
  6. **Snapshot, don't capture** — mutable parent state (ctx, session, model) is read once at spawn time and frozen into a `ParentSnapshot` data object.
19
19
  No live references survive past the spawn call.
20
20
  7. **Subscribe, don't thread** — observation of agent progress uses direct session-event subscription, not callback parameters threaded through multiple layers.
@@ -63,9 +63,8 @@ flowchart TB
63
63
 
64
64
  subgraph observation["Observation domain"]
65
65
  direction TB
66
- RecordObserver["record-observer<br/>(stats via events)"]
66
+ RecordObserver["record-observer<br/>(stats + live activity via events)"]
67
67
  Notification["notification<br/>(completion nudges)"]
68
- UIObserver["ui-observer<br/>(streaming state)"]
69
68
  end
70
69
 
71
70
  subgraph tools["Tools domain"]
@@ -95,8 +94,8 @@ flowchart TB
95
94
  SessionConfig --> Prompts & Env
96
95
  AgentTypeRegistry --> DefaultAgents & CustomAgents
97
96
  RecordObserver -.->|subscribes| SubagentSession
98
- UIObserver -.->|subscribes| SubagentSession
99
97
  Widget -.->|polls| SubagentManager
98
+ SubagentManager -.->|notifies| Widget
100
99
  ```
101
100
 
102
101
  ### Key domain types
@@ -258,7 +257,7 @@ sequenceDiagram
258
257
  Asm-->>Factory: SessionConfig
259
258
  Factory->>Child: create session + bind extensions
260
259
  Factory-->>Ag: SubagentSession (born complete)
261
- Note over Ag: agent-observer + ui-observer subscribe to session events
260
+ Note over Ag: record-observer subscribes to session events
262
261
  Ag->>Sub: runTurnLoop(prompt, opts)
263
262
  Sub->>Child: prompt + drive turn loop
264
263
  Child-->>Sub: result text
@@ -322,6 +321,7 @@ src/
322
321
  │ ├── notification.ts completion nudges
323
322
  │ ├── notification-state.ts per-agent notification tracking
324
323
  │ ├── renderer.ts notification TUI component
324
+ │ ├── composite-subagent-observer.ts fans manager notifications out to multiple observers
325
325
  │ └── subagent-events-observer.ts manager lifecycle observer (event emission + persistence + notification)
326
326
 
327
327
  ├── service/ cross-extension API boundary
@@ -346,10 +346,8 @@ src/
346
346
  │ ├── agent-creation-wizard.ts agent creation (AgentCreationWizard class)
347
347
  │ ├── conversation-viewer.ts scrollable session overlay
348
348
  │ ├── message-formatters.ts pure per-message-type formatters (extracted from conversation-viewer)
349
- │ ├── agent-activity-tracker.ts live activity state tracker
350
349
  │ ├── agent-file-ops.ts filesystem abstraction
351
350
  │ ├── agent-file-writer.ts overwrite-guard + write + reload + notify helper
352
- │ ├── ui-observer.ts session-event observer for streaming
353
351
  │ └── display.ts pure formatters and shared types
354
352
 
355
353
  └── handlers/ event handlers
@@ -361,11 +359,11 @@ src/
361
359
 
362
360
  ### Observation model
363
361
 
364
- Record statistics (tool uses, token usage, compaction counts) are updated by `record-observer.ts`, which subscribes directly to session events.
365
- UI streaming (active tools, response text, turn counts) is handled by `ui/ui-observer.ts`, which subscribes to the same session events independently.
366
- Neither observer wraps or forwards the other — both subscribe directly to the session.
362
+ Record statistics (tool uses, token usage, compaction counts) and live activity (active tools, response text, turn counts) are updated by `record-observer.ts`, which subscribes directly to session events.
363
+ This is the single per-child session subscription all run state lives on the `Subagent` record.
367
364
 
368
- The widget reads agent state by polling a shared `Map<string, AgentActivityTracker>` on `SubagentRuntime` every 80 ms. The conversation viewer subscribes to session events via `Subagent.subscribeToUpdates()` and reads messages via `Subagent.messages` no direct `AgentSession` reference (#277).
365
+ The widget reads agent state by polling the records exposed via `SubagentManager.listAgents()` every 80 ms; that poll loop is now started by the manager's lifecycle notifications (the widget subscribes as a `SubagentManagerObserver` fanned out through `CompositeSubagentObserver`), not by inbound calls from the spawn tools.
366
+ The conversation viewer subscribes to session events via `Subagent.subscribeToUpdates()` and reads messages via `Subagent.messages` — no direct `AgentSession` reference (#277).
369
367
 
370
368
  ## Cross-extension architecture
371
369
 
@@ -483,11 +481,14 @@ If Pi gains a native service registry ([earendil-works/pi#4207]), these accessor
483
481
 
484
482
  The core emits events on `pi.events` that any extension can observe:
485
483
 
486
- | Channel | Payload | When |
487
- | --------------------- | ------------------------------------------- | -------------------- |
488
- | `subagents:started` | `{ id, type, description }` | Agent begins running |
489
- | `subagents:completed` | `{ id, type, status, result?, error? }` | Agent finishes |
490
- | `subagents:activity` | `{ id, toolName?, textDelta?, turnCount? }` | Streaming progress |
484
+ | Channel | Payload | When |
485
+ | --------------------- | ----------------------------------------------------------------------------------- | --------------------------------------------- |
486
+ | `subagents:started` | `{ id, type, description }` | Agent begins running |
487
+ | `subagents:completed` | `{ id, type, description, status, result?, error?, toolUses, durationMs, tokens? }` | Agent finishes successfully |
488
+ | `subagents:failed` | same as `completed` (`buildEventData` shape) | Agent ends in `error`/`stopped`/`aborted` |
489
+ | `subagents:compacted` | `{ id, type, description, reason, tokensBefore, compactionCount }` | Child session compacts |
490
+ | `subagents:created` | `{ id, type, description, isBackground }` | Background agent created (pre-admission) |
491
+ | `subagents:steered` | `{ id, message }` | Steering message delivered to a running agent |
491
492
 
492
493
  These are fire-and-forget broadcast events — no request IDs, no reply channels.
493
494
 
@@ -894,173 +895,232 @@ All nine steps are closed: [#381], [#373], [#374], [#375], [#376], [#377], [#378
894
895
  [#415] migrated `pi-subagents-worktrees` to `loadLayeredSettings` after the Step 9 published release.
895
896
  See [phase-17-core-consolidation.md](history/phase-17-core-consolidation.md) for the full findings, step outcomes, dependency diagram, and tracks.
896
897
 
897
- ## Phase 18 Reconsider UI from first principles
898
-
899
- After 17 phases the core is in very good shape: health 78/100 (B), zero dead code, zero fallow refactoring targets, average cyclomatic 1.4.
900
- The one structural debt that remains is not spread thin across the core — it is concentrated in a single place: the UI's live-streaming state (the "activity tier") is woven into the core's runtime, spawn tools, the LLM-facing `subagent` tool, and the notification path.
901
- This is the entanglement the [first-principles refinement](#first-principles-refinement-and-the-deeper-target) predicted — "the activity/metrics push tier is provisional; its only reactive consumer is the inherited widget" — observed in the code.
902
- Disentangling it is what lets the core reach a great state, and it is the prerequisite to any "reconsider the UI" decision: today the UI cannot be a clean consumer because the core does its streaming bookkeeping for it.
903
-
904
- ### Findings
905
-
906
- Six findings tell one story — the activity tier has metastasized out of the UI and into the core.
907
-
908
- 1. **UI state lives on the core runtime.**
909
- `SubagentRuntime.agentActivity: Map<string, AgentActivityTracker>` puts a UI streaming-state map on the core composition root.
910
- `SubagentRuntime` owns session-scoped lifecycle state; `AgentActivityTracker` (active tools, response text, turn count) is pure rendering state the core never reads.
911
- Category C (coupling).
912
- 2. **Core spawn tools wire UI streaming.**
913
- `foreground-runner.ts` and `background-spawner.ts` construct `AgentActivityTracker`, call `subscribeUIObserver` (a second session subscription parallel to the core's own `record-observer`), call `setSession`, and populate or delete the activity map and drive the widget (`ensureTimer`, `update`, `markFinished`).
914
- Their own doc comments list "AgentActivityTracker creation, UI observer subscription" as responsibilities.
915
- Category C (mixed responsibility, parameter relay).
916
- 3. **The LLM-facing tool depends on the widget.**
917
- `AgentTool` takes a 4-method `widget` dependency plus `runtime.agentActivity` and calls `this.widget.setUICtx(ctx.ui)`.
918
- Every `AgentTool` unit test must stub the widget and the activity map (`createToolDeps`), although the tool's real concern is dispatch — testability friction marking a domain seam.
919
- Category C/D.
920
- 4. **Two parallel observers split the run-metric domain.**
921
- Each child session gets two subscriptions: `record-observer` accumulates `SubagentState` (tool uses, usage, compactions) and `ui-observer` accumulates `AgentActivityTracker` (active tools, response text, turn count).
922
- `turnCount` is a genuine run metric that lives only in the UI tracker, so `notification.ts` and the foreground result text reach into the tracker to recover it.
923
- Category C (anemic split, duplication).
924
- 5. **A core observation concern reaches into UI state.**
925
- `NotificationManager` holds the activity map and reads `turnCount`/`maxTurns` from it to build notification details, and deletes entries from it.
926
- Category C (Law of Demeter).
927
- 6. **The public event contract is both vacant and incomplete.**
928
- `SUBAGENT_EVENTS.ACTIVITY = "subagents:activity"` is declared in the service surface and the doc's lifecycle-events table but is never emitted anywhere — a vacant hook the doc's own "no vacant hooks" rule forbids.
929
- Meanwhile three emitted channels (`subagents:failed`, `subagents:compacted`, `subagents:created`) are absent from the constant map.
930
- Category A/E.
898
+ ## Phase 18 (complete)
931
899
 
932
- ### Health metrics
900
+ Phase 18 disentangled the activity tier from the core and recorded a first-principles decision about the UI's direction.
901
+ Steps 1–5 (the spine) consolidated all run state onto `SubagentState`, deleted `AgentActivityTracker` and `ui-observer` (−145 LOC), and made the widget a pure reactive consumer of lifecycle events with no inbound calls from core spawn tools.
902
+ Step 6 reconciled the public event contract (breaking: removed the vacant `SUBAGENT_EVENTS.ACTIVITY` channel; added `FAILED`, `COMPACTED`, `CREATED`, `STEERED`).
903
+ Step 7 consolidated residual test clone families, dropping from 24 to 14 clone groups (below the <15 target).
904
+ Step 8 captured the per-component UI decisions in [ADR-0004]: the widget shrinks to background agents only; the bespoke `ConversationViewer` is replaced by native session navigation (a read-only transcript via `parseSessionEntries`, per the Step 1 spike — see [ADR-0004]'s addendum); the `/agents` command is dissolved (remove the create wizard and agent-types editor, extract settings to a focused command); the surviving UI stays in-core as a substitutable reactive consumer.
905
+ Source LOC decreased from 7,751 (62 files) to 7,650 (61 files); tests grew from 1,031 to 1,047.
906
+ All eight steps are closed: [#420], [#421], [#422], [#423], [#424], [#425], [#426], [#427].
907
+ See [phase-18-reconsider-ui.md](history/phase-18-reconsider-ui.md) for the full findings, step outcomes, dependency diagram, and tracks.
908
+
909
+ ## Phase 19 (proposed)
910
+
911
+ Phase 19 implements the per-component UI decisions recorded in [ADR-0004]: shrink the widget to background-only, replace the bespoke conversation viewer with native session navigation, dissolve the monolithic `/agents` menu, and keep the surviving UI in-core.
912
+
913
+ The sequencing follows Kent Beck's "make the change easy, then make the easy change."
914
+ The end state deletes `agent-menu.ts` — the god-command that bundles four unrelated jobs — and everything reachable only from it.
915
+ Rather than surgically mutate that doomed module (and the #1 churn hotspot `index.ts`) once per option, Phase 19 first stands up the replacement surfaces additively, then removes the now-orphaned subtree in a single terminal cut.
916
+ This keeps every responsibility's old surface live until its replacement exists (ADR-0004's no-interim-regression invariant), turns the three replacement steps into genuinely parallel work (none touch `agent-menu.ts`), and reduces `index.ts` edits from four surgical removals to one deregistration.
917
+
918
+ Seven steps in three phases:
919
+
920
+ - **Phase A — stand up replacements (additive):** spike, settings command, background widget, native session navigation (Steps 1–4).
921
+ - **Phase B — dissolve `/agents` (terminal cut):** delete the orphaned subtree in two deletion commits, one per subtree (Steps 5–6).
922
+ - **Phase C — test health:** consolidate the test clones that survive the cut (Step 7).
923
+
924
+ ### Health metrics (Phase 18 → Phase 19 target)
925
+
926
+ | Metric | Phase 18 (current) | Phase 19 target |
927
+ | ---------------------- | ------------------------ | -------------------- |
928
+ | Health score | 78/100 (B) | 83/100 (B+) |
929
+ | Source LOC | 7,650 (61 files) | ~6,780 (~55 files) |
930
+ | Production duplication | 11 lines (1 group) | 0 lines |
931
+ | Test clone groups | 16 | ≤ 10 |
932
+ | Top churn hotspot | `index.ts` (103 commits) | `index.ts` (cooling) |
933
+
934
+ ### ✅ Step 1 — Spike: resolve ADR-0004 entry criteria ([#446])
935
+
936
+ Smell: Category C (coupling boundary) — four open decisions block the session-navigation implementation.
937
+ Target: `docs/decisions/0004-reconsider-ui-direction.md` addendum.
938
+
939
+ The four entry criteria from ADR-0004:
940
+
941
+ 1. **Root-continuity:** Does the root's in-flight turn survive `ctx.switchSession()` and a return gesture?
942
+ 2. **View-only vs interactive:** `switchSession` (full interactive takeover) or `loadEntriesFromFile` (read-only transcript built from JSONL)?
943
+ 3. **Parallel-agent navigation:** Operator gesture to select which of N background agents to view (from the widget, a command, or both).
944
+ 4. **Settings command name:** `/subagents-settings`, `/agents-settings`, or another form consistent with sibling packages?
945
+
946
+ Produce a minimal spike (observed test or PoC against a real session) that answers each question, then record the answers as an addendum to ADR-0004.
947
+ No production source files change; the spike closes when the ADR addendum is merged.
948
+
949
+ Outcome: ADR-0004 updated with all four entry-criteria answers; Step 4 unblocked.
950
+
951
+ `Release: independent`
952
+
953
+ ### Step 2 — Extract settings to a focused `/subagents-settings` command ([#447])
954
+
955
+ Smell: Category E (naming/organization) — settings are buried inside the monolithic `/agents` command per ADR-0004 Decision C. This step is purely additive: it stands up the new surface without touching `agent-menu.ts`.
956
+ Target files:
957
+
958
+ - New `src/ui/subagents-settings.ts` — `SubagentsSettingsHandler` lifted from `AgentsMenuHandler.showSettings`, carrying its own narrow `SubagentsSettingsManager` interface (the three `apply*` methods and three readonly accessors only).
959
+ - `src/index.ts` — register the new command (name confirmed by Step 1); pass `settings` directly.
960
+ - New `test/ui/subagents-settings.test.ts` — unit tests for the extracted handler.
961
+
962
+ `showSettings` depends only on `this.settings` (the self-contained `AgentMenuSettings` shape), so the extraction copies that logic into a new file with zero coupling to the wizard, editor, or viewer.
963
+ The old in-menu Settings option keeps working until the terminal cut deletes `agent-menu.ts` wholesale — there is no surgical removal of `showSettings` or `AgentMenuSettings` from the doomed file.
964
+
965
+ Outcome: new `subagents-settings.ts` (~80 LOC) and focused command registered; `agent-menu.ts` untouched.
966
+
967
+ `Release: independent`
968
+
969
+ ### Step 3 — Shrink widget to background agents only ([#444])
970
+
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
+ Target files:
973
+
974
+ - `src/ui/agent-widget.ts` — funnel both `manager.listAgents()` call sites (`update()` and `renderWidget()`) through a single private accessor, then flip that accessor to background-only via `record.invocation?.runInBackground === true`.
975
+ - `src/ui/widget-renderer.ts` — verify no foreground-specific rendering path survives.
976
+ - `test/ui/agent-widget.test.ts` — add background-only filtering tests; update assertions.
977
+
978
+ The widget calls `listAgents()` at two sites today — `update()` (feeding `seedFinishedAgents`, `assembleWidgetState`, and `clearWidget`) and `renderWidget()` (the tree map).
979
+ Filtering at only one site leaves the other rendering foreground agents, so the enabling move is to route both through one accessor and apply the predicate once at the source.
980
+ `Subagent.invocation.runInBackground` is the reliable signal: set by `spawn-config.ts` → `AgentInvocation.runInBackground` → stored on `Subagent.invocation`.
981
+ ADR-0004 Decision A: foreground runs suppress the widget; the inline `onUpdate` stream is authoritative there.
982
+
983
+ Outcome: widget shows only background agents; foreground/widget duplication eliminated; the background predicate lives at a single funnel.
984
+
985
+ `Release: independent`
933
986
 
934
- | Metric | Value |
935
- | ---------------------------------------- | ------------------------------------------------------------------ |
936
- | Health score | 78/100 (B) |
937
- | Source LOC | 7,751 (62 files) |
938
- | Dead code | 0 files, 0 exports |
939
- | Maintainability index | 90.9 (good) |
940
- | Avg cyclomatic complexity | 1.4 |
941
- | P90 cyclomatic complexity | 2 |
942
- | Fallow refactoring targets | 0 |
943
- | Production duplication | 11 lines (1 internal clone group in `agent-config-editor.ts`) |
944
- | Test duplication | 28 clone groups, 503 lines |
945
- | Activity-tier modules slated for removal | `ui-observer.ts` (61) + `agent-activity-tracker.ts` (84) = 145 LOC |
946
-
947
- ### Steps
948
-
949
- The spine (Steps 1–5) removes the activity-tier entanglement, leaving the core a pure orchestrator whose run state lives in one place and whose UI is a reactive consumer of broadcast events plus discrete queries.
950
- Steps 6–7 are independent hygiene.
951
- Step 8 is the user-driven UI reconsideration the disentangled core finally makes possible — a decision about the UI's distribution once it is substitutable, not optional.
952
-
953
- The deeper target in the [first-principles refinement](#first-principles-refinement-and-the-deeper-target) metrics as a pure observer projection rather than mutable fields — is deliberately **not** forced here.
954
- Folding the live activity onto the record (the single owner of run state, consistent with Phase 17's `SubagentState`) removes the duplication without inventing the asynchronous-observation seam the `improvement-discovery` skill warns is essential, not structural.
955
-
956
- 1. **✅ Fold run metrics and live activity onto the core record (pure addition) — complete (v16.5.0).**
957
- ([#420]) Target: `lifecycle/subagent-state.ts`, `observation/record-observer.ts`, `lifecycle/subagent.ts`.
958
- Extend the single owned run-state value object with `turnCount`, active tools, and response text; have the already-subscribed `record-observer` handle `turn_end`, `tool_execution_start`, `message_start`, `message_update`; expose read-only `turnCount`/`maxTurns`/`activeTools`/`responseText` getters on `Subagent`.
959
- `AgentActivityTracker` still exists; nothing reads the new getters yet (tidy-first).
960
- Smell: Category C. Outcome: `Subagent` is the single home for all run state; getters available for migration.
961
- Landed: `SubagentState` owns `turnCount`/`activeTools`/`responseText` plus their transition methods; `record-observer` populates them on a single subscription; `Subagent` exposes the four read-only getters; +27 tests (1031 → 1058).
962
- 2. **✅ Migrate every activity reader to the record getters complete.**
963
- ([#421]) Target: `ui/widget-renderer.ts`, `ui/conversation-viewer.ts`, `ui/agent-menu.ts`, `tools/foreground-runner.ts`, `observation/notification.ts`.
964
- Switch each reader from `AgentActivityTracker` to the record getters added in Step 1 (widget-renderer reads activity off `listAgents()`; viewer/menu drop the `activity` param; notification reads `turnCount`/`maxTurns` off the record).
965
- Smell: Category C (Law of Demeter).
966
- Outcome: no consumer references `AgentActivityTracker`.
967
- Landed: `WidgetAgent` folds the live-activity fields and a `contextPercent` projection; `AgentWidget` projects records; `ConversationViewer`, `AgentsMenuHandler`, `buildDetails`, and `buildNotificationDetails` read off the record; `NotificationSystem.cleanupCompleted` removed; `SubagentEventsObserver` returns early on consumed results; +8 tests (1058 → 1066).
968
- 3. **Delete `AgentActivityTracker` and `ui-observer`; drop the activity map from the runtime and spawn tools.**
969
- ([#422]) Target: `ui/agent-activity-tracker.ts` (delete), `ui/ui-observer.ts` (delete), `runtime.ts`, `tools/foreground-runner.ts`, `tools/background-spawner.ts`.
970
- The spawn tools stop constructing trackers, subscribing, and populating maps; `SubagentRuntime.agentActivity` is removed.
971
- Smell: Category A + C. Outcome: −145 LOC, one session subscription per child, runtime holds zero UI state.
972
- 4. **Make the widget self-drive from lifecycle events.**
973
- ([#423]) Target: `ui/agent-widget.ts`, `lifecycle/subagent-manager.ts` (observer), `index.ts`.
974
- The widget starts/stops its timer in response to started/created/completed notifications instead of tool calls; spawn tools no longer call `ensureTimer`/`update`/`markFinished`.
975
- Smell: Category C (coupling direction).
976
- Outcome: the widget is a reactive consumer; no inbound calls from core spawn tools.
977
- 5. **Drop the widget and activity-map dependencies from the `subagent` tool.**
978
- ([#424]) Target: `tools/agent-tool.ts`, `test/helpers/make-deps.ts`.
979
- `AgentTool` loses its `widget` and `agentActivity` constructor params (UICtx capture stays in `ToolStartHandler`); `createToolDeps` sheds the widget and map stubs.
980
- Smell: Category C/D.
981
- Outcome: the LLM tool depends only on manager/runtime/settings/registry; fixture drops 2 fields.
982
- 6. **Reconcile the public event contract.**
983
- ([#425]) Target: `service/service.ts`, this document's lifecycle-events table.
984
- Remove the vacant `ACTIVITY` channel (or emit a real broadcast for it) and add the emitted `failed`/`compacted`/`created` channels so declared constants match emitted events.
985
- Smell: Category A/E.
986
- Outcome: declared channels equal emitted channels; no vacant hook.
987
- 7. **Consolidate residual test clone families.**
988
- ([#426]) Target: `test/settings.test.ts` + `test/layered-settings.test.ts`, `test/lifecycle/create-subagent-session.test.ts`, `test/ui/agent-config-editor.test.ts`.
989
- Extract shared fixtures for the clone families fallow reports that the spine does not already rewrite.
990
- Smell: Category D. Outcome: test clone groups drop below 15.
991
- 8. **Reconsider the UI direction (first-principles ADR).**
992
- ([#427]) Target: `docs/decisions/`, `ui/`.
993
- The spine already made the UI _substitutable_; this step decides its _distribution_, not whether the experiment is possible.
994
- The goal is **substitutable, not optional**: a human needs some surface, but the specific UI is replaceable the way Pi ships a default TUI built on the same public API any extension targets.
995
- The disentangled core stays byte-for-byte identical whether or not a given UI consumer is installed (the composition test), so a replacement UI is a downstream concern even though _some_ UI is not.
996
- Unlike the worktrees provider seam (generative, rationed — one provider the core consults), the UI is an observational consumer (unlimited, the core never waits on it) reading the broadcast-plus-query surface, which is why packaging it is the secondary question and decoupling it was the real win.
997
- Two standing concerns are the evidence this decision weighs and the better boundaries the reconsideration is meant to surface:
998
- - **Truncated transcript.**
999
- The conversation viewer shows a truncated view, yet `Subagent.messages` already exposes the full history and the core already persists each child as a standard Pi session JSONL (`outputFile`) — the limit is the bespoke overlay's rendering, not data access.
1000
- - **Foreground widget redundancy.**
1001
- In foreground the tool's inline `onUpdate` stream already shows progress, so the above-editor widget duplicates it; the widget earns its keep only for background agents — a per-mode judgment the fused UI cannot vary.
1002
- Candidate redesign to record: replace the bespoke overlay with "open the child session in the same viewer Pi uses for any session," following the recursive-Pi insight and the already-persisted session file.
1003
- Judge the widget, conversation viewer, and `/agents` menu per component — keep, shrink, extract to `@gotgenes/pi-subagents-ui`, or remove and capture the decision in an ADR that gateways Phase 19.
1004
- Smell: Category E (organization / boundary).
1005
- Outcome: a recorded per-component decision motivated by the two concerns; the inherited UI is substitutable and no longer preserved by default.
987
+ ### Step 4 — Implement native session navigation ([#445])
988
+
989
+ Smell: Category C (coupling) — the bespoke `ConversationViewer` re-implements session-transcript rendering when Pi's own machinery targets the already-persisted child session JSONL.
990
+ This step adds the new surface alongside the existing viewer; it does not touch `agent-menu.ts`.
991
+ Target files:
992
+
993
+ - New `src/ui/session-navigator.ts` (or a widget gesture, per Step 1's answer to parallel-agent navigation) — operator picks one of N background agents and views its persisted session via the spike-chosen mechanism, keyed on `record.outputFile`.
994
+ - `src/index.ts` register the new navigation surface (command and/or widget gesture, per the spike).
995
+
996
+ ADR-0004 Decision B: "Tell-Don't-Ask hand Pi the session path; Pi owns the viewer."
997
+ Mechanism (confirmed by the Step 1 spike, [ADR-0004] addendum): a **read-only** transcript rendered from `parseSessionEntries(readFileSync(record.outputFile, "utf8"))`, surfaced through a flat command — **not** `switchSession` (a full takeover that invalidates the root's in-flight turn) and **not** `loadEntriesFromFile` (a test-only export of the SDK's `core/session-manager` module that the package's public barrel does not re-export, in both `0.79.1` and `0.79.8`).
998
+ `Subagent.outputFile` already exposes the persisted child session JSONL path via `subagentSession?.outputFile` no new SDK dependency.
999
+ Rendering uses Pi's own public APIs (no bespoke renderer): `parseSessionEntries` → drop the `SessionHeader` → `buildSessionContext(...).messages` → Pi's public entry components (`AssistantMessageComponent` / `ToolExecutionComponent` / …) or `serializeConversation` (see the [ADR-0004] addendum, Findings 0 and 1).
1000
+ The new surface stands up while the old `viewAgentConversation`/`ConversationViewer` path still works; the bespoke viewer is removed only by the terminal cut (Step 5).
1001
+
1002
+ Outcome: operator views a child agent's persisted session through Pi's native machinery; the new surface coexists with the old viewer until Step 5.
1003
+
1004
+ `Release: independent` (spike-gated)
1005
+
1006
+ ### Step 5 Dissolve `/agents` and remove the conversation-viewer subtree ([#442])
1007
+
1008
+ Smell: Category A (dead subsystem) plus Category B (oversized) — once Steps 2–4 re-home all four menu responsibilities, the `/agents` command and everything reachable only from `agent-menu.ts` is an unreferenced subtree.
1009
+ This is the first of two deletion commits (split by subtree).
1010
+ The hub `agent-menu.ts` is deleted here, not surgically narrowed, and deleting it is what orphans the leaf subtrees — so it must precede the definition-management deletion (Step 6), because `agent-menu.ts` statically imports the wizard, editor, and file-ops, and dynamically imports the viewer.
1011
+ Target files:
1012
+
1013
+ - `src/index.ts` remove the `registerCommand("agents", …)` block, the `AgentsMenuHandler` construction and import, and the `FsAgentFileOps` import/construction (its only use is wiring the menu).
1014
+ - Delete `src/ui/agent-menu.ts` (331 LOC) and `test/ui/agent-menu.test.ts` (185 LOC).
1015
+ - Delete `src/ui/conversation-viewer.ts` (241 LOC) and `test/conversation-viewer.test.ts` (239 LOC) its only consumer is `agent-menu.ts`'s dynamic import, gone with the hub.
1016
+ - Delete `src/ui/message-formatters.ts` (195 LOC) and `test/message-formatters.test.ts` (388 LOC, the largest test function by LOC) — its only consumer is `ConversationViewer`.
1017
+
1018
+ Running-agent visibility is now owned by the background widget (Step 3); session navigation replaces the bespoke overlay (Step 4); settings live in `/subagents-settings` (Step 2).
1019
+ Deleting the hub in one move avoids any surgical edit to the doomed file and leaves the definition-management leaves orphaned for Step 6.
1020
+
1021
+ Outcome: `/agents` dissolved; −767 LOC source (menu hub + viewer + formatters); −812 LOC test; largest test function eliminated; `index.ts` edited once (deregistration), never surgically narrowed.
1022
+
1023
+ `Release: batch "dissolve-agents"`
1024
+
1025
+ ### Step 6 — Remove the orphaned agent-definition management subtree ([#441])
1026
+
1027
+ Smell: Category A (dead subsystem) the creation wizard and config editor are removed per ADR-0004 Decision C; after Step 5 deletes their only importer (`agent-menu.ts`), they and their file-ops helpers are pure orphans.
1028
+ This is the second deletion commit (split by subtree).
1029
+ Target files:
1030
+
1031
+ - Delete `src/ui/agent-creation-wizard.ts` (233 LOC) and `test/ui/agent-creation-wizard.test.ts` (296 LOC).
1032
+ - Delete `src/ui/agent-config-editor.ts` (199 LOC) and `test/ui/agent-config-editor.test.ts` (392 LOC) eliminates the 11-line internal production clone in `disableAgent`/`ejectAgent`, the package's only remaining production duplication.
1033
+ - Delete `src/ui/agent-file-ops.ts` (59 LOC) and `test/ui/agent-file-ops.test.ts` (112 LOC) — only consumers were wizard + editor.
1034
+ - Delete `src/ui/agent-file-writer.ts` (55 LOC) and `test/ui/agent-file-writer.test.ts` (148 LOC) only consumers were wizard + editor.
1035
+ - `test/helpers/ui-stubs.ts` delete `makeFileOps`, `createTestSubagentConfig`, and `spawnAndWait` from `makeMenuManager` if no surviving consumer remains; delete the file outright once all consumers are gone.
1036
+
1037
+ An operator generates a new agent `.md` by asking a Pi session directly (more capable than a fixed wizard) or by writing the file in an editor; viewing and editing definitions is served by opening the `.md` files in an editor or IDE.
1038
+ These files are orphaned by Step 5, so this is a pure `git rm` with no surviving references and no edit to any doomed file.
1039
+
1040
+ Outcome: −546 LOC source (wizard + editor + file-ops + file-writer); −948 LOC test; production duplication → 0 lines; 1 production and 1 test clone group eliminated.
1041
+
1042
+ `Release: batch "dissolve-agents"`
1043
+
1044
+ ### Step 7 Consolidate remaining test clone families ([#443])
1045
+
1046
+ Smell: Category D (testability) 16 clone groups at Phase 18 end; the terminal cut (Steps 5–6) removes ~4 groups; remaining groups are extraction targets.
1047
+ Run after the cut so no helper is extracted into a file the cut then deletes.
1048
+ Target files:
1049
+
1050
+ - `test/lifecycle/subagent-manager.test.ts` extract a shared assertion helper for 3 clone families (23 lines across groups at :92/:109, :282/:330, and :323 shared with `subagent.test.ts`).
1051
+ - `test/ui/agent-widget.test.ts` — merge the duplicate `makeWidget` helper defined twice across two `describe` blocks (14-line clone at :225/:284).
1052
+ - `test/session/session-config.test.ts` extract a shared fixture for the 16-line internal clone (lines 131–146 / 151–166).
1053
+ - `test/lifecycle/concurrency-limiter.test.ts` extract shared setup for the 10-line clone (lines 21–30 / 148–155).
1054
+ - `test/tools/spawn-config.test.ts` extract a shared fixture for the 9-line clone (lines 22–30 / 35–43).
1055
+
1056
+ Outcome: test clone groups 10 (from 16); `subagent-manager.test.ts` uses shared factory helpers.
1057
+
1058
+ `Release: independent`
1006
1059
 
1007
1060
  ### Step dependency diagram
1008
1061
 
1009
1062
  ```mermaid
1010
- flowchart TB
1011
- S1["1 Fold metrics + activity onto record (#420) ✅"]
1012
- S2["2 Migrate readers to record getters (#421) ✅"]
1013
- S3["3 — Delete tracker + ui-observer, drop activity map (#422)"]
1014
- S4["4 Widget self-drives on events (#423)"]
1015
- S5["5 Drop widget dep from subagent tool (#424)"]
1016
- S6["6 Reconcile public event contract (#425)"]
1017
- S7["7 Consolidate test clone families (#426)"]
1018
- S8["8 — Reconsider UI direction, ADR (#427)"]
1019
-
1020
- S1 --> S2 --> S3 --> S4 --> S5 --> S8
1021
- S6
1022
- S7
1063
+ flowchart LR
1064
+ S1[ Step 1 - Spike (#446)]
1065
+ S2[Step 2 - Settings command (#447)]
1066
+ S3[Step 3 - Background widget (#444)]
1067
+ S4[Step 4 - Native session nav (#445)]
1068
+ S5[Step 5 - Dissolve /agents + viewer (#442)]
1069
+ S6[Step 6 - Remove definition mgmt (#441)]
1070
+ S7[Step 7 - Test clones (#443)]
1071
+
1072
+ S1 --> S4
1073
+ S2 --> S5
1074
+ S3 --> S5
1075
+ S4 --> S5
1076
+ S5 --> S6
1077
+ S6 --> S7
1023
1078
  ```
1024
1079
 
1080
+ The terminal cut (Step 5) depends on all three replacements — settings (Step 2), widget (Step 3), and session navigation (Step 4) — because each of the four `/agents` options must have its responsibility re-homed before its branch can die.
1081
+ The old `S1 → S6 → S7` chain hid the widget dependency; this diagram makes it explicit.
1082
+
1025
1083
  ### Parallel tracks
1026
1084
 
1027
- - **Track A — Disentangle the activity tier (spine):** Steps 1, 2, 3.
1028
- Strictly ordered; each is a safe lift-and-shift over the previous.
1029
- - **Track B Decouple widget and tool wiring:** Steps 4, 5.
1030
- Begins once the activity map is gone (Step 3).
1031
- - **Track C Public-contract hygiene:** Step 6.
1032
- Independent; can land any time.
1033
- - **Track D — Test consolidation:** Step 7.
1034
- Independent; can land any time.
1035
- - **Track E — UI reconsideration:** Step 8.
1036
- Gated on the spine and Track B (the UI must be a clean consumer first).
1085
+ - **Track A — Replacements (Steps 1–4):** the spike gates session navigation (Step 1 → Step 4); settings (Step 2) and the background widget (Step 3) are independent of the spike and of each other.
1086
+ None of these steps edits `agent-menu.ts`, so they carry no shared-file collision on the menu — genuinely parallelizable, unlike the prior plan's Steps 2/3/5, which all collided on `agent-menu.ts` and `index.ts`.
1087
+ Steps 2 and 4 each append a command registration to `index.ts` (additive, low-conflict).
1088
+ - **Track B — Dissolution (Steps 5 → 6):** the terminal cut, gated on all of Track A landing.
1089
+ Hub-first ordering is forced: Step 5 deletes `agent-menu.ts` (orphaning the leaves), then Step 6 `git rm`s the now-orphaned definition-management subtree.
1090
+ - **Track C Test health (Step 7):** clone consolidation, run after the cut so no surviving helper is extracted into a doomed file.
1091
+
1092
+ ### Release batches
1093
+
1094
+ - **Batch "dissolve-agents":** Steps 5, 6 (ship together; tail = Step 6).
1095
+ Depends on Steps 2, 3, 4 already merged.
1096
+ - Independently releasable: Steps 1, 2, 3, 4, 7.
1037
1097
 
1038
1098
  ## Refactoring history
1039
1099
 
1040
- Phases 1–5, 7–17 are complete.
1041
- Phase 6 (UI extraction to a separate package) is deferred Phase 18.
1100
+ Phases 1–5, 7–18 are complete.
1101
+ Phase 6 (UI extraction to a separate package) was deferred to Phase 18; its intent was resolved by [ADR-0004] (Phase 18 Step 8).
1042
1102
  Detailed records are preserved in per-phase history files:
1043
1103
 
1044
- | Phase | Title | Status | History |
1045
- | ----- | --------------------------------------------------- | ------------------- | ------------------------------------------------------------------------------------ |
1046
- | 1 | Export SubagentsService API boundary | Complete | [phase-1-api-boundary.md](history/phase-1-api-boundary.md) |
1047
- | 2 | Remove scheduling subsystem | Complete | [phase-2-remove-scheduling.md](history/phase-2-remove-scheduling.md) |
1048
- | 3 | Remove group-join, RPC; replace output-file | Complete | [phase-3-remove-rpc-groupjoin.md](history/phase-3-remove-rpc-groupjoin.md) |
1049
- | 4 | Implement and publish SubagentsService | Complete | [phase-4-implement-service.md](history/phase-4-implement-service.md) |
1050
- | 5 | Decompose index.ts | Complete | [phase-5-decompose-index.md](history/phase-5-decompose-index.md) |
1051
- | 6 | Extract UI to separate package | Deferred Phase 18 | — |
1052
- | 7 | Encapsulation and dependency narrowing | Complete | [phase-7-encapsulation.md](history/phase-7-encapsulation.md) |
1053
- | 8 | Testability, display extraction, menu decomposition | Complete | [phase-8-testability.md](history/phase-8-testability.md) |
1054
- | 9 | Observation consolidation, ctx elimination | Complete | [phase-9-observation-ctx.md](history/phase-9-observation-ctx.md) |
1055
- | 10 | Domain organization, bag decomposition, complexity | Complete | [phase-10-structural-decomposition.md](history/phase-10-structural-decomposition.md) |
1056
- | 11 | Closure factories to classes | Complete | [phase-11-closure-to-class.md](history/phase-11-closure-to-class.md) |
1057
- | 12 | Complexity reduction and test fixture extraction | Complete | [phase-12-complexity-test-fixtures.md](history/phase-12-complexity-test-fixtures.md) |
1058
- | 13 | Remaining structural smells | Complete | [phase-13-remaining-smells.md](history/phase-13-remaining-smells.md) |
1059
- | 14 | Strip policy from core | Complete | [phase-14-strip-policy.md](history/phase-14-strip-policy.md) |
1060
- | 15 | Domain model evolution | Complete | [phase-15-domain-model-evolution.md](history/phase-15-domain-model-evolution.md) |
1061
- | 16 | Invert dependencies (extensions on a minimal core) | Complete | [phase-16-invert-dependencies.md](history/phase-16-invert-dependencies.md) |
1062
- | 17 | Core consolidation | Complete | [phase-17-core-consolidation.md](history/phase-17-core-consolidation.md) |
1063
- | 18 | Reconsider UI (first principles) | In progress | this document, [Phase 18](#phase-18-reconsider-ui-from-first-principles) |
1104
+ | Phase | Title | Status | History |
1105
+ | ----- | --------------------------------------------------- | ---------------------- | ------------------------------------------------------------------------------------ |
1106
+ | 1 | Export SubagentsService API boundary | Complete | [phase-1-api-boundary.md](history/phase-1-api-boundary.md) |
1107
+ | 2 | Remove scheduling subsystem | Complete | [phase-2-remove-scheduling.md](history/phase-2-remove-scheduling.md) |
1108
+ | 3 | Remove group-join, RPC; replace output-file | Complete | [phase-3-remove-rpc-groupjoin.md](history/phase-3-remove-rpc-groupjoin.md) |
1109
+ | 4 | Implement and publish SubagentsService | Complete | [phase-4-implement-service.md](history/phase-4-implement-service.md) |
1110
+ | 5 | Decompose index.ts | Complete | [phase-5-decompose-index.md](history/phase-5-decompose-index.md) |
1111
+ | 6 | Extract UI to separate package | Superseded by ADR-0004 | — |
1112
+ | 7 | Encapsulation and dependency narrowing | Complete | [phase-7-encapsulation.md](history/phase-7-encapsulation.md) |
1113
+ | 8 | Testability, display extraction, menu decomposition | Complete | [phase-8-testability.md](history/phase-8-testability.md) |
1114
+ | 9 | Observation consolidation, ctx elimination | Complete | [phase-9-observation-ctx.md](history/phase-9-observation-ctx.md) |
1115
+ | 10 | Domain organization, bag decomposition, complexity | Complete | [phase-10-structural-decomposition.md](history/phase-10-structural-decomposition.md) |
1116
+ | 11 | Closure factories to classes | Complete | [phase-11-closure-to-class.md](history/phase-11-closure-to-class.md) |
1117
+ | 12 | Complexity reduction and test fixture extraction | Complete | [phase-12-complexity-test-fixtures.md](history/phase-12-complexity-test-fixtures.md) |
1118
+ | 13 | Remaining structural smells | Complete | [phase-13-remaining-smells.md](history/phase-13-remaining-smells.md) |
1119
+ | 14 | Strip policy from core | Complete | [phase-14-strip-policy.md](history/phase-14-strip-policy.md) |
1120
+ | 15 | Domain model evolution | Complete | [phase-15-domain-model-evolution.md](history/phase-15-domain-model-evolution.md) |
1121
+ | 16 | Invert dependencies (extensions on a minimal core) | Complete | [phase-16-invert-dependencies.md](history/phase-16-invert-dependencies.md) |
1122
+ | 17 | Core consolidation | Complete | [phase-17-core-consolidation.md](history/phase-17-core-consolidation.md) |
1123
+ | 18 | Reconsider UI (first principles) | Complete | [phase-18-reconsider-ui.md](history/phase-18-reconsider-ui.md) |
1064
1124
 
1065
1125
  ### Structural refactoring issues
1066
1126
 
@@ -1084,6 +1144,7 @@ Detailed records are preserved in per-phase history files:
1084
1144
  | Phase 16 (abandoned) | #256 (superseded), #257 (parked), #258, #259 (not planned) | Agent collaborator architecture — replaced by the inversion approach above ([ADR-0002]) |
1085
1145
  | Phase 17 | #381, #373, #374, #375, #376, #377, #378, #379, #380 | ConcurrencyLimiter, SubagentState, run-start encapsulation, run collaborators, events observer, widget decoupling, lifecycle test fixtures, UI/tools test fixtures, settings-loader extraction |
1086
1146
  | Phase 17 (follow-on) | #412, #415 | Session-mock builder unification, worktrees settings-helper migration |
1147
+ | Phase 18 | #420, #421, #422, #423, #424, #425, #426, #427 | Fold metrics onto record, migrate readers, delete activity tier, widget self-drives, drop widget from tool, reconcile event contract, consolidate test clones, UI-direction ADR |
1087
1148
 
1088
1149
  The remaining open issue is #22 (parent-session resolution), a cross-extension track that does not gate the structural work.
1089
1150
 
@@ -1147,4 +1208,12 @@ The upstream test suite is run periodically as a regression canary for the sessi
1147
1208
  [#425]: https://github.com/gotgenes/pi-packages/issues/425
1148
1209
  [#426]: https://github.com/gotgenes/pi-packages/issues/426
1149
1210
  [#427]: https://github.com/gotgenes/pi-packages/issues/427
1211
+ [#441]: https://github.com/gotgenes/pi-packages/issues/441
1212
+ [#442]: https://github.com/gotgenes/pi-packages/issues/442
1213
+ [#443]: https://github.com/gotgenes/pi-packages/issues/443
1214
+ [#444]: https://github.com/gotgenes/pi-packages/issues/444
1215
+ [#445]: https://github.com/gotgenes/pi-packages/issues/445
1216
+ [#446]: https://github.com/gotgenes/pi-packages/issues/446
1217
+ [#447]: https://github.com/gotgenes/pi-packages/issues/447
1150
1218
  [ADR-0002]: ../decisions/0002-extensions-on-a-minimal-core.md
1219
+ [ADR-0004]: ../decisions/0004-reconsider-ui-direction.md