@gotgenes/pi-subagents 17.0.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.
@@ -0,0 +1,235 @@
1
+ ---
2
+ issue: 426
3
+ issue_title: "pi-subagents: consolidate residual test clone families"
4
+ ---
5
+
6
+ # Consolidate residual test clone families
7
+
8
+ ## Release Recommendation
9
+
10
+ **Release:** ship independently
11
+
12
+ The architecture roadmap (`docs/architecture/architecture.md`, Phase 18, step 7) tags this issue "Independent of the disentanglement spine — can land at any time" and carries no `Release: batch` annotation.
13
+ It is a test-only consolidation with no production-surface change, so it ships on its own.
14
+
15
+ ## Problem Statement
16
+
17
+ fallow reports a long tail of test clone groups in `pi-subagents` (currently 24 test clone groups across the suite).
18
+ A handful of these clone families are stable test scaffolding — tmp-directory setup, repeated arrange-then-act blocks, and near-identical menu-assertion cases — that the Phase 18 disentanglement spine does not rewrite as a side effect.
19
+ They warrant their own shared fixtures rather than riding along with a production refactor.
20
+
21
+ The goal is to extract shared fixtures for the clone families fallow identifies in the four named test files, dropping the package's test clone-group count below 15.
22
+
23
+ ## Goals
24
+
25
+ - Eliminate the cross-file tmp-directory + `writeGlobal`/`writeProject` duplication between `test/settings.test.ts` and `test/layered-settings.test.ts` via a shared fixture helper.
26
+ - Eliminate the three internal clone groups in `test/lifecycle/create-subagent-session.test.ts` by extracting the repeated arrange (factory session + deps) while keeping each `createSubagentSession(...)` act explicit.
27
+ - Eliminate the four internal clone groups in `test/ui/agent-config-editor.test.ts` — convert the menu-option-structure cases to a table-driven `it.each` and hoist the shared `filePath`/arrange in the Edit/Delete blocks.
28
+ - Land the package below 15 test clone groups (the four files contribute exactly 10 of the current 24 groups → 14 remaining).
29
+ - Keep the full `vitest` suite green and the assertion strength unchanged (no behavioral coverage loss).
30
+
31
+ This change is **not breaking** — it touches only test files and adds test-only helpers; no production module, export, or default changes.
32
+
33
+ ## Non-Goals
34
+
35
+ - The production-code clone in `src/ui/agent-config-editor.ts` (the `location`/`targetDir`/`targetPath` block, dup `ff960d84`) — the issue scopes to *test* clone families; a production extraction is a separate concern.
36
+ - The remaining test clone groups outside the four named files (`subagent-manager.test.ts`, `subagent.test.ts`, `concurrency-limiter.test.ts`, `workspace-bracket.test.ts`, `session-config.test.ts`, `get-result-tool.test.ts`, `spawn-config.test.ts`, `agent-creation-wizard.test.ts`, `agent-file-ops.test.ts`, `agent-menu.test.ts`, `agent-widget.test.ts`).
37
+ The cross-file group `5d8dbd48` (`subagent-manager.test.ts` ↔ `subagent.test.ts`) is explicitly left alone — neither side is a target file.
38
+ - The cross-package `vitest.config.ts` clone (`e5e6691e`, 6 instances) — not a `pi-subagents` test-suite group and not in scope.
39
+ - Phase 18 step 8 ([#427]), the UI-direction ADR — a separate, later step.
40
+
41
+ ## Background
42
+
43
+ Relevant existing test-helper conventions live in `packages/pi-subagents/test/helpers/`:
44
+
45
+ - `make-deps.ts`, `make-spawn-config.ts`, `make-subagent.ts`, `manager-stubs.ts`, `mock-session.ts`, `stub-ctx.ts`, `subagent-session-io.ts`, `ui-stubs.ts` — each exports `createX`/`makeX` factory functions with matching `*.test.ts` self-tests.
46
+ - `subagent-session-io.ts` already exports `createFactorySession`, `createSubagentSessionDeps`, `createSubagentSessionIO`, `createAgentLookup`, `createChildLifecycleMock` — the create-subagent-session consolidation builds on these.
47
+ - `ui-stubs.ts` already exports `createTestSubagentConfig`, `makeFileOps`, `makeMenuUI` — the agent-config-editor consolidation builds on these.
48
+
49
+ Constraints from the `testing` skill that shape this plan:
50
+
51
+ - **Do not wrap the system-under-test call in a helper to eliminate a duplication-metric clone — the repeated act is the test subject, not duplication to remove.**
52
+ Consolidate the *arrange*, not the *act*.
53
+ For `create-subagent-session`, extract the factory-session + deps arrange but keep `await createSubagentSession(...)` written out in each test.
54
+ - **Group shared setup in a describe-scoped `beforeEach` and keep the act explicit** when the arrangement is uniform.
55
+ - A table-driven `it.each` keeps the act visible in the parameterized body (run once per row) and is the right tool for the menu-option-structure cases — it is not a helper that hides the act.
56
+ - Test-only refactors carry the `test:` conventional-commit type.
57
+
58
+ Each helper module in `test/helpers/` ships a paired `*.test.ts`; any new shared helper follows that convention.
59
+
60
+ ## Design Overview
61
+
62
+ This is a refactor verified by the existing suite staying green plus a falling fallow clone count — there are no new red→green behavior cycles.
63
+ Three independent consolidations, each scoped to its file(s).
64
+
65
+ ### 1. Settings tmp-dir fixture (cross-file)
66
+
67
+ `test/settings.test.ts` and `test/layered-settings.test.ts` both stand up two `mkdtempSync` directories in `beforeEach`, tear them down in `afterEach`, and define identical `writeGlobal`/`writeProject` helpers (clone groups `21d1fb01`, `4003c0e7` span both files; `4fc062db` is internal to `layered-settings.test.ts`).
68
+
69
+ Extract a shared fixture into `test/helpers/tmp-settings-dirs.ts`:
70
+
71
+ ```typescript
72
+ export interface SettingsDirs {
73
+ globalDir: string; // agentDir / global scope
74
+ projectDir: string; // cwd / project scope
75
+ globalFile: (filename?: string) => string;
76
+ projectFile: (filename?: string) => string;
77
+ writeGlobal: (obj: unknown, filename?: string) => void;
78
+ writeProject: (obj: unknown, filename?: string) => void;
79
+ }
80
+
81
+ // Caller wires it into beforeEach/afterEach:
82
+ export function createSettingsDirs(filename: string): SettingsDirs;
83
+ export function disposeSettingsDirs(dirs: SettingsDirs): void;
84
+ ```
85
+
86
+ Each test file calls `createSettingsDirs(...)` in `beforeEach` and `disposeSettingsDirs(...)` in `afterEach`, then references `dirs.writeGlobal` / `dirs.writeProject`.
87
+ `settings.test.ts` uses `"subagents.json"`; `layered-settings.test.ts` uses its `FILENAME` constant.
88
+ The malformed-settings warn-assertion clone inside `layered-settings.test.ts` (`4fc062db`, two adjacent `it` blocks) collapses into a single `it.each` over `[malformed-global, malformed-project]` rows that share the spy-assert body.
89
+
90
+ Interaction sketch (consumer call site, ~5 lines):
91
+
92
+ ```typescript
93
+ let dirs: SettingsDirs;
94
+ beforeEach(() => { dirs = createSettingsDirs("subagents.json"); });
95
+ afterEach(() => { disposeSettingsDirs(dirs); });
96
+ it("loads from global when no project file", () => {
97
+ dirs.writeGlobal({ maxConcurrent: 16 });
98
+ expect(loadSettings(dirs.globalDir, dirs.projectDir)).toEqual({ maxConcurrent: 16 });
99
+ });
100
+ ```
101
+
102
+ The helper owns only filesystem scaffolding (Tell-Don't-Ask: callers ask it to write, not for raw paths to write themselves); it does not import or know about `loadSettings`/`loadLayeredSettings`, so the act stays in the test.
103
+
104
+ ### 2. create-subagent-session arrange helper (internal)
105
+
106
+ Clone groups `48ff1484`, `c92feb70`, `5fbe9ebb` all live in the `createSubagentSession — post-bind recursion guard` describe block (lines ~204–262).
107
+ Each test repeats:
108
+
109
+ ```typescript
110
+ const session = createFactorySession({ toolsBeforeBind, toolsAfterBind });
111
+ io.createSession.mockResolvedValue({ session });
112
+ await createSubagentSession(
113
+ { snapshot: STUB_SNAPSHOT, type: "Explore" },
114
+ createSubagentSessionDeps({ io, exec, registry: mockAgentLookup }),
115
+ );
116
+ const postBindArgs = session.setActiveToolsByName.mock.calls[0][0];
117
+ ```
118
+
119
+ The variation is the factory tool-list config and the assertions; the act is identical.
120
+ Add two file-local arrange helpers (the factory mocks differ per test, so a describe-scoped `beforeEach` cannot fully arrange):
121
+
122
+ ```typescript
123
+ function arrangeFactory(opts?: Parameters<typeof createFactorySession>[0]) {
124
+ const session = createFactorySession(opts);
125
+ io.createSession.mockResolvedValue({ session });
126
+ return session;
127
+ }
128
+ function defaultDeps() {
129
+ return createSubagentSessionDeps({ io, exec, registry: mockAgentLookup });
130
+ }
131
+ ```
132
+
133
+ Each test becomes `const session = arrangeFactory({...}); await createSubagentSession({ snapshot: STUB_SNAPSHOT, type: "Explore" }, defaultDeps());` — the act line stays written out.
134
+ The remaining two-line arrange+act sequence falls below fallow's clone threshold.
135
+ These helpers stay file-local (one consumer file), so no new `test/helpers/` module is needed.
136
+
137
+ ### 3. agent-config-editor table-driven menu cases (internal)
138
+
139
+ Clone groups `4ac7b228`, `a8e71e9c` (×3), `e36d2314` cluster in the "Menu option structure" block (lines ~104–139); `23fe1f93` is in the Edit/Delete blocks (~180–210).
140
+
141
+ - **Menu-option-structure cases** → one `it.each` table.
142
+ Each existing case differs only in (a) the `resolveAgentConfig` mock (default vs. custom vs. disabled) and (b) the expected option array.
143
+ Model the rows as `{ name, config, filePath, expected }` and run the shared act once per row:
144
+
145
+ ```typescript
146
+ it.each([
147
+ { name: "default agent, no file", config: testDefaultConfig, filePath: undefined,
148
+ expected: ["Eject (export as .md)", "Disable", "Back"] },
149
+ // …one row per existing menu-structure case…
150
+ ])("shows $name options", async ({ config, filePath, expected }) => {
151
+ vi.spyOn(testRegistry, "resolveAgentConfig").mockReturnValue(config);
152
+ const { editor, ui } = setupDetail([undefined], filePath ? { filePath } : {});
153
+ await editor.showAgentDetail(ui, "test-agent");
154
+ expect((ui.select.mock.calls[0][1] as string[])).toEqual(expected);
155
+ });
156
+ ```
157
+
158
+ The act (`editor.showAgentDetail(...)`) stays visible in the table body.
159
+ - **Edit/Delete block (`23fe1f93`)** → hoist the shared `const filePath = "/project/.pi/agents/test-agent.md"` to a describe-scoped constant and keep each `await editor.showAgentDetail(...)` act explicit; the residual per-test arrange (`setupDetail`, `ui.editor`/`ui.confirm` resolution) stays inline because it carries the test's intent.
160
+
161
+ No change to `setupDetail`, `makeEditor`, or `ui-stubs.ts` — they already provide the right arrange seam.
162
+
163
+ ## Module-Level Changes
164
+
165
+ - `test/helpers/tmp-settings-dirs.ts` — **new**: shared tmp-dir fixture (`createSettingsDirs`, `disposeSettingsDirs`, `SettingsDirs` type, `writeGlobal`/`writeProject`).
166
+ - `test/helpers/tmp-settings-dirs.test.ts` — **new**: self-test for the fixture (follows the `test/helpers/*.test.ts` convention — every helper module ships a paired test).
167
+ - `test/settings.test.ts` — **changed**: replace inline tmp-dir `beforeEach`/`afterEach`/`writeGlobal`/`writeProject` with the shared fixture.
168
+ - `test/layered-settings.test.ts` — **changed**: same fixture swap; fold the two adjacent malformed-settings warn cases into one `it.each`.
169
+ - `test/lifecycle/create-subagent-session.test.ts` — **changed**: add file-local `arrangeFactory`/`defaultDeps`; rewrite the post-bind-guard tests to call them while keeping each `createSubagentSession(...)` act explicit.
170
+ - `test/ui/agent-config-editor.test.ts` — **changed**: convert the menu-option-structure cases to `it.each`; hoist the shared `filePath` in the Edit/Delete blocks.
171
+
172
+ No `src/` files change.
173
+ No SKILL.md, architecture-doc, or README references to the affected test internals exist to update — these are intra-test fixtures (verified: the architecture roadmap references the *issue* `#426`, not the specific helpers).
174
+ The roadmap step's status flips to ✅ in a follow-up doc pass at ship time, not in this plan (consistent with how steps 1–6 were marked).
175
+
176
+ ## Test Impact Analysis
177
+
178
+ This issue *is* a test-maintenance change, so the standard extraction lens is inverted:
179
+
180
+ 1. **New tests enabled** — only the new `tmp-settings-dirs.test.ts`, a self-test pinning the shared fixture's filesystem behavior (writes land at the right global/project paths; dispose removes both dirs).
181
+ No production seam is newly testable.
182
+ 2. **Tests becoming redundant** — none are removed outright.
183
+ The two malformed-settings cases in `layered-settings.test.ts` merge into one `it.each` (two rows = same two assertions, no coverage loss); the agent-config-editor menu cases merge into one `it.each` (one row per former case, identical assertions).
184
+ The total assertion set is preserved; only the surrounding arrange is deduplicated.
185
+ 3. **Tests that must stay as-is** — the act in every consolidated test (`loadSettings`/`loadLayeredSettings`/`createSubagentSession`/`showAgentDetail`) stays written out per the `testing` skill rule; the distinct intent of each Edit/Delete case (write vs. no-write vs. cancel) stays inline.
186
+
187
+ ## Invariants at risk
188
+
189
+ The four target files belong to surfaces earlier Phase 17/18 steps refactored (`subagent-session-io.ts` fixtures from #378/#412; `create-subagent-session` from #257/#265; `agent-config-editor` UI from the disentanglement spine).
190
+ The invariant at risk is purely **coverage preservation** — a consolidation must not silently drop an assertion.
191
+ Each consolidation step is gated by:
192
+
193
+ - The full `vitest` suite staying green (test count must not *drop* except by the deliberate `it.each` merges, whose row count equals the former case count).
194
+ - Running `pnpm fallow dupes` after each step to confirm the targeted clone groups disappear and no new ones appear.
195
+
196
+ No production `Outcome:`/`Landed:` invariant from a prior roadmap step is touched, because no `src/` file changes.
197
+
198
+ ## Refactor Order
199
+
200
+ These are refactor (not red→green) steps: each makes a consolidation, verifies the suite stays green and the targeted clone groups drop, then commits with `test:`.
201
+ Order is independent — the three consolidations do not depend on each other — but listed for clean, reviewable commits.
202
+
203
+ 1. **Shared settings tmp-dir fixture.**
204
+ Add `test/helpers/tmp-settings-dirs.ts` + `tmp-settings-dirs.test.ts`; migrate `test/settings.test.ts` and `test/layered-settings.test.ts` to it; fold the two malformed-settings cases in `layered-settings.test.ts` into one `it.each`.
205
+ Verify: `pnpm --filter @gotgenes/pi-subagents exec vitest run test/settings.test.ts test/layered-settings.test.ts test/helpers/tmp-settings-dirs.test.ts` green; `pnpm fallow dupes` shows `21d1fb01`, `4003c0e7`, `4fc062db` gone.
206
+ Commit: `test: extract shared settings tmp-dir fixture (#426)`.
207
+ 2. **create-subagent-session arrange helpers.**
208
+ Add file-local `arrangeFactory`/`defaultDeps`; rewrite the post-bind-guard tests to use them with the act kept explicit.
209
+ Verify: `pnpm --filter @gotgenes/pi-subagents exec vitest run test/lifecycle/create-subagent-session.test.ts` green; clone groups `48ff1484`, `c92feb70`, `5fbe9ebb` gone.
210
+ Commit: `test: consolidate create-subagent-session post-bind arrange (#426)`.
211
+ 3. **agent-config-editor table-driven menu cases.**
212
+ Convert the menu-option-structure cases to `it.each`; hoist the shared `filePath` in the Edit/Delete blocks.
213
+ Verify: `pnpm --filter @gotgenes/pi-subagents exec vitest run test/ui/agent-config-editor.test.ts` green; clone groups `4ac7b228`, `a8e71e9c`, `e36d2314`, `23fe1f93` gone.
214
+ Commit: `test: table-drive agent-config-editor menu cases (#426)`.
215
+ 4. **Final verification.**
216
+ Run the full suite (`pnpm --filter @gotgenes/pi-subagents exec vitest run`), `pnpm run check`, `pnpm run lint`, `pnpm fallow dead-code` (catch orphaned imports left by removed inline helpers), and `pnpm fallow dupes` to confirm the package's test clone-group count is below 15 (expected: 24 → 14).
217
+ No separate commit unless cleanup is needed.
218
+
219
+ ## Risks and Mitigations
220
+
221
+ - **Risk: an `it.each` merge silently drops an assertion.**
222
+ Mitigation: map each former `it` to exactly one table row with the same assertions; confirm the post-merge test count equals (old count − merged duplicates) and the suite stays green.
223
+ - **Risk: removing an inline `writeGlobal`/`writeProject` leaves an orphaned import** (Biome `noUnusedImports` is warning-level, exit 0).
224
+ Mitigation: step 4 runs `pnpm fallow dead-code`; re-check each migrated file's imports (`mkdirSync`, `writeFileSync`, `mkdtempSync`, `rmSync`, `tmpdir`, `join`) after the fixture swap.
225
+ - **Risk: a helper that wraps the act re-introduces the "act is the subject" smell.**
226
+ Mitigation: the design keeps every `loadSettings`/`createSubagentSession`/`showAgentDetail` call written out in the test body; helpers cover arrange only.
227
+ - **Risk: the fixture's `filename` parameterization diverges from the two call sites' expectations** (`subagents.json` vs. `FILENAME`).
228
+ Mitigation: `createSettingsDirs(filename)` takes the filename explicitly; the self-test pins both path shapes.
229
+
230
+ ## Open Questions
231
+
232
+ - Whether to also dissolve the production clone in `src/ui/agent-config-editor.ts` (dup `ff960d84`) — deferred as a Non-Goal; revisit if the Phase 18 UI-direction ADR (#427) reworks that file anyway.
233
+ - Whether a shared tmp-dir fixture should live in a cross-package location (other packages stand up similar dirs) — out of scope; keep it `pi-subagents`-local until a second consumer appears.
234
+
235
+ [#427]: https://github.com/gotgenes/pi-packages/issues/427
@@ -0,0 +1,212 @@
1
+ ---
2
+ issue: 427
3
+ issue_title: "pi-subagents: reconsider the UI direction from first principles (ADR)"
4
+ ---
5
+
6
+ # Reconsider the UI direction from first principles (ADR)
7
+
8
+ ## Release Recommendation
9
+
10
+ **Release:** ship independently
11
+
12
+ Phase 18 carries no `Release:` batch annotation in the architecture roadmap, so this issue ships on its own.
13
+ It is a decision-only ADR plus an architecture-doc update — docs-only, no runtime change — and it completes the Phase 18 roadmap, gateway-ing the (separately planned) Phase 19 implementation.
14
+
15
+ ## Problem Statement
16
+
17
+ Phase 18's spine (Steps 1–7) disentangled the activity tier from the core, leaving the inherited UI a pure reactive consumer of the broadcast-plus-query surface.
18
+ The UI is now _substitutable_.
19
+ This final step (Step 8, [#427]) decides the UI's _direction and distribution_, not whether substitution is possible — and records the decision in an ADR that gateways Phase 19.
20
+
21
+ The decision is **per component** (widget, conversation viewer, `/agents` menu) and per the architecture's own first principles: keep, shrink, extract to a separate package, or remove — judged on our terms, not preserved by default.
22
+ Two standing concerns are the evidence:
23
+
24
+ - **Foreground widget redundancy** — 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.
25
+ - **Truncated transcript** — the bespoke conversation viewer renders a custom, capped transcript, yet each child is already persisted as a standard Pi session JSONL (`outputFile`); the limit is the bespoke overlay, not data access.
26
+
27
+ ## Goals
28
+
29
+ - Write `docs/decisions/0004-reconsider-ui-direction.md` recording a per-component decision for the widget, the conversation viewer, and the `/agents` menu.
30
+ - Record the **distribution** decision: the surviving UI stays in-core as a reactive consumer (substitutable, not extracted to a separate package now).
31
+ - Record the open feasibility questions for the native-session-navigation direction as Phase 19 spike gates (not pretend-resolved).
32
+ - Update `docs/architecture/architecture.md`: mark Phase 18 Step 8 complete, mark Phase 18 complete, and add a forward pointer to ADR-0004 / Phase 19.
33
+ - This is a **decision-only** ADR — no `src/` or `test/` changes; the implementation lands in the separately-planned Phase 19.
34
+
35
+ ## Non-Goals
36
+
37
+ - No UI code changes (no widget edit, no viewer removal, no menu decomposition, no new `/subagents:settings` command).
38
+ Those are Phase 19 implementation, planned and shipped separately.
39
+ - No new `@gotgenes/pi-subagents-ui` package — extraction is explicitly _not_ chosen now.
40
+ - No resolution of the `switchSession` mechanics (root-continuity, view-only-vs-interactive) — the ADR records these as Phase 19 spike entry criteria.
41
+ - No full Phase 19 roadmap — the arch-doc update adds only a forward pointer; Phase 19's steps are planned in their own pass.
42
+ - No SKILL.md edits — `package-pi-subagents` SKILL describes the UI domain's module count, which changes only when Phase 19 code lands.
43
+
44
+ ## Background
45
+
46
+ Relevant modules and how they relate:
47
+
48
+ - `src/ui/agent-widget.ts` (~290 LOC) — `AgentWidget implements SubagentManagerObserver`; self-drives an 80 ms render loop from lifecycle events (Step 4, [#423]), renders running/completed agents above the editor.
49
+ - `src/ui/conversation-viewer.ts` (~241 LOC) — bespoke `ConversationViewer` overlay; subscribes to `record.subscribeToUpdates`, renders a live, width-capped transcript of one agent's `messages`, plus a streaming indicator.
50
+ - `src/ui/agent-menu.ts` (~331 LOC) + `agent-config-editor.ts`, `agent-creation-wizard.ts`, `agent-file-ops.ts` — the `/agents` command.
51
+ Four entries: **Running agents** (→ `ConversationViewer`), **Agent types** (→ `AgentConfigEditor` view/edit/enable/disable), **Create new agent** (→ `AgentCreationWizard`), **Settings** (max concurrency / default max turns / grace turns, written to layered settings).
52
+ - `src/tools/foreground-runner.ts` — foreground runs stream progress via the tool's inline `onUpdate` callback; the result is rendered by `result-renderer.ts` (caps display at 50 lines).
53
+ - The core persists each child as a standalone Pi session JSONL at `Subagent.outputFile`; `Subagent.messages` exposes full history.
54
+
55
+ Pi SDK surface relevant to the candidate redesign (verified against `@earendil-works/pi-coding-agent@0.79.1`):
56
+
57
+ - `ExtensionActions.switchSession(sessionPath, { withSession })` — switches the **active** session to a different session file; fires `session_before_switch` / `session_shutdown` and invalidates the current session context (`setBeforeSessionInvalidate` exists for host-owned UI teardown).
58
+ Returns `{ cancelled }`.
59
+ The switched-to session is fully interactive — `ReplacedSessionContext` exposes `sendUserMessage`.
60
+ - `session-manager` exports `loadEntriesFromFile(filePath)` / `parseSessionEntries(content)` — read a session file's entries without switching (the read-only alternative).
61
+
62
+ Constraint from AGENTS.md / the package SKILL: pi-subagents is a minimal core, open for extension and closed for modification; the UI is an _observational consumer_ (unlimited, the core never waits on it), distinct from the rationed generative `WorkspaceProvider` seam.
63
+ The ADR records a consumer-design decision; it must not introduce any new inbound dependency from the core onto the UI.
64
+
65
+ ## Design Overview
66
+
67
+ The ADR records four decisions, each motivated by the first principles in the architecture doc's "first-principles refinement."
68
+
69
+ ### Decision A — Foreground widget: shrink to background agents only
70
+
71
+ The above-editor widget duplicates the foreground tool's inline `onUpdate` stream.
72
+ Decision: the widget survives **only** as the background-agent status surface (concern (b): background agents have no tool-call display, so _something_ must indicate their state).
73
+ Foreground runs suppress the widget; the inline stream is authoritative there.
74
+
75
+ Multiple subagents can run in parallel, so the background surface must represent N concurrent agents at once — the widget's existing per-agent tree already does this; the change is _when_ it shows (background-only), not _what_ it shows.
76
+
77
+ ### Decision B — Conversation viewer: replace the bespoke overlay with native session navigation
78
+
79
+ Concern (c) — "let the operator switch into a subagent's session, scroll/read it, switch between subagents, and exit back to root" — is a richer interaction than a live overlay.
80
+ The core already persists each child as a standalone Pi session JSONL, so this maps onto Pi's own session machinery rather than a bespoke renderer.
81
+
82
+ Decision (direction): **remove `ConversationViewer`**; the operator navigates into a child's persisted session via Pi's native viewer and back to root.
83
+ The bespoke, width-capped transcript is replaced by the same viewer Pi uses for any session — the recursive-Pi insight applied to the already-persisted session file.
84
+
85
+ Candidate call-site shape (Phase 19, illustrative — verifies the interaction pattern, not final):
86
+
87
+ ```typescript
88
+ // "View running agents" → pick a child → switch into its persisted session
89
+ const child = manager.getRecord(id);
90
+ if (child?.outputFile) {
91
+ await ctx.switchSession(child.outputFile);
92
+ // operator reads/scrolls in Pi's native viewer; a later switch returns to root
93
+ }
94
+ ```
95
+
96
+ This is Tell-Don't-Ask (hand Pi the session path; Pi owns the viewer) and keeps the core free of transcript-rendering code.
97
+
98
+ Open mechanics (recorded in the ADR, resolved by a Phase 19 spike — see Open Questions): `switchSession` is a full active-session takeover (not a view-only overlay), it invalidates the current session context, and the switched-to session is interactive.
99
+ The spike decides whether the operator UX is (i) true `switchSession` round-trips, or (ii) a read-only transcript built from `loadEntriesFromFile` that renders Pi-standard entries without leaving root.
100
+ The ADR records the _direction_ (native session machinery over a bespoke renderer) and gates the mechanism on the spike.
101
+
102
+ ### Decision C — `/agents` menu: dissolve the monolithic command into focused surfaces
103
+
104
+ The single `/agents` command bundles four unrelated jobs.
105
+ Decision (direction): split them; do not keep all in one command.
106
+ The operator does not value managing agent definitions through the menu at all — creating or editing agents is firmly better done with other tools (directly in Pi, or a real text editor / IDE), so both of those surfaces are **removed outright**, not merely deprioritized.
107
+
108
+ - **Create new agent (wizard)** → **remove.**
109
+ An operator generates a new agent `.md` by asking a Pi agent directly (more capable than a fixed wizard) or by writing the file in an editor — the wizard earns no keep.
110
+ - **Agent types (list + config editor)** → **remove.**
111
+ Viewing and editing agent definitions is better served by opening the `.md` files directly in an editor/IDE; the in-menu config editor earns no keep.
112
+ - **Running agents (visibility)** → **keep the responsibility, re-home it.**
113
+ _Something_ must own running-agent visibility; it moves onto the background widget (Decision A) plus the native session navigation (Decision B), not a bespoke in-menu overlay.
114
+ - **Settings (concurrency / max turns / grace turns)** → **extract to a focused command** (e.g. `/subagents:settings`).
115
+ Some value, but it does not belong bundled with agent management.
116
+
117
+ ### Decision D — Distribution: keep the surviving UI in-core (substitutable, not extracted)
118
+
119
+ The spine already made the UI substitutable — a replacement UI is a downstream concern that targets the public broadcast-plus-query surface.
120
+ Decision: the surviving UI (background widget + a settings command + session-navigation glue) **stays in-core** as a reactive consumer.
121
+ Extraction to `@gotgenes/pi-subagents-ui` is _not_ chosen now; it remains an available future option precisely because the core is byte-for-byte identical with or without a given UI consumer (the composition invariant).
122
+
123
+ This answers the issue's headline question — the UI's _distribution_ — with "keep in core, substitutable," recorded explicitly rather than left implicit.
124
+
125
+ ## Module-Level Changes
126
+
127
+ This is a decision-only ADR; the only files touched are docs.
128
+
129
+ - **New:** `packages/pi-subagents/docs/decisions/0004-reconsider-ui-direction.md`.
130
+ Frontmatter (`status: accepted`, `date: <ISO date>`), then `# 0004 — ...`, `## Status`, `## Context`, `## Decision` (Decisions A–D), `## Consequences`, `## Phase 19 entry criteria` (the spike gates + per-component implementation handles).
131
+ - **Changed:** `packages/pi-subagents/docs/architecture/architecture.md`:
132
+ - Phase 18 Step 8 (`### Steps`, item 8) — mark complete with a `Landed:` bullet referencing ADR-0004 and the recorded decisions.
133
+ - Step dependency diagram node `S8` — append a ✅ marker (matching S1–S7) so the diagram is consistent.
134
+ - The phase summary table row `| 18 | Reconsider UI (first principles) | In progress |` → `Complete`, and add the per-phase pointer to the ADR.
135
+ - Add a forward-pointer line gateway-ing Phase 19 (implementation of the recorded decisions, planned separately).
136
+ - Add a `[#427]` reference usage is already present; no new link-ref definition needed.
137
+ Add an ADR-0004 reference link if the doc cites it by path.
138
+
139
+ No `src/`, `test/`, or SKILL.md changes — every removed-symbol / reworded-mechanism check is therefore vacuous for this issue (no symbols removed, no runtime mechanism reworded).
140
+ The architecture doc's UI-domain module count and health metrics are _not_ edited here — they change when Phase 19 code lands.
141
+
142
+ ## Test Impact Analysis
143
+
144
+ None — this is a docs-only ADR.
145
+
146
+ 1. No new unit tests are enabled (no extraction, no behavior change).
147
+ 2. No existing tests become redundant (no code removed).
148
+ 3. No tests must stay-as-is for a layer being extracted (nothing is extracted in this issue).
149
+
150
+ The Phase 19 implementation that acts on these decisions will carry its own Test Impact Analysis when it is planned.
151
+
152
+ ## Invariants at risk
153
+
154
+ None regressed by this issue — it changes no code, so every Phase 18 spine outcome stays green by construction.
155
+
156
+ The ADR _records_ that Phase 19 must preserve these spine invariants when it implements the decisions:
157
+
158
+ - Step 3 ([#422]) outcome "runtime holds zero UI state" — Phase 19 must not re-introduce UI state on the core when wiring the background-only widget or session navigation.
159
+ - Step 4/5 ([#423], [#424]) outcome "the widget is a reactive consumer; no inbound calls from core spawn tools / the LLM tool depends only on manager/runtime/settings/registry" — the background-only restriction and the `/agents` decomposition must keep the dependency direction inward.
160
+ - Step 6 ([#425]) outcome "declared channels equal emitted channels; no vacant hook" — any new navigation surface must consume existing broadcast/query channels, not add a vacant one.
161
+
162
+ These are pinned today by the existing observer/widget/event-contract suites; Phase 19 inherits them.
163
+
164
+ ## Build Order
165
+
166
+ Docs-only — no red→green test cycles.
167
+ Numbered build steps, each a single reviewable commit.
168
+
169
+ 1. **Write ADR-0004.**
170
+ Author `docs/decisions/0004-reconsider-ui-direction.md` with Status / Context / Decision (A–D) / Consequences / Phase 19 entry criteria, capturing the `switchSession` findings and open mechanics.
171
+ Verify: `pnpm run lint` passes (rumdl markdown rules); the ADR states a decision for every component named in [#427].
172
+ Commit: `docs: add ADR-0004 reconsidering the UI direction (#427)`.
173
+ 2. **Update the architecture doc.**
174
+ Mark Phase 18 Step 8 complete (Landed bullet → ADR-0004), append ✅ to the `S8` diagram node, flip the phase table row to Complete, and add the Phase 19 forward pointer.
175
+ Verify: `pnpm run lint` passes; the `[#427]` reference still resolves; no orphaned link-ref definitions (MD053); Phase 18 reads as complete end-to-end.
176
+ Commit: `docs: mark Phase 18 complete and gateway Phase 19 (#427)`.
177
+
178
+ (Both steps may be folded into one commit if preferred; kept separate here because they touch different documents with different review concerns.)
179
+
180
+ ## Risks and Mitigations
181
+
182
+ - **Risk:** the ADR over-commits to `switchSession` before its mechanics are understood, locking Phase 19 into an infeasible UX.
183
+ **Mitigation:** the ADR records the _direction_ (native session machinery over a bespoke renderer) and explicitly gates the _mechanism_ on a Phase 19 spike, listing the read-only `loadEntriesFromFile` fallback as a first-class alternative.
184
+ - **Risk:** removing the conversation viewer and the running-agents menu entry leaves a visibility gap before Phase 19 ships.
185
+ **Mitigation:** this issue removes _nothing_ — it only records decisions; the viewer/menu stay live until Phase 19 replaces them, so there is no interim regression.
186
+ - **Risk:** the arch-doc edits silently drop an enclosing structure (the Steps list, the diagram fence, the phase table).
187
+ **Mitigation:** anchor edits on unique adjacent lines, re-read each edited region after editing, and run `pnpm run lint` to confirm the Mermaid fence and tables still parse.
188
+ - **Risk:** a future reader treats the recorded "keep in core" distribution decision as permanent.
189
+ **Mitigation:** the ADR frames extraction as a still-available option enabled by the composition invariant, with the conditions under which it would be revisited.
190
+
191
+ ## Open Questions
192
+
193
+ These are recorded in the ADR as Phase 19 spike entry criteria, not resolved here:
194
+
195
+ - **Root-continuity during a session switch.**
196
+ `switchSession` invalidates the current session context — does the root's in-flight turn survive a switch-out-and-return, and what is the correct "return to root" gesture?
197
+ Spike before committing Phase 19 to true `switchSession` round-trips.
198
+ - **View-only vs interactive.**
199
+ A switched-to child session is interactive (`sendUserMessage`).
200
+ Is steering a child from its own session desirable, or should the viewer be strictly read-only (favoring the `loadEntriesFromFile` transcript path)?
201
+ - **Parallel-agent navigation.**
202
+ With N background agents running, what is the operator's gesture to pick which child to view and to cycle between them — driven from the background widget, a dedicated command, or both?
203
+ - **Settings command namespace.**
204
+ Confirm the final command name/namespace for the extracted settings surface (`/subagents:settings` vs another form) against how sibling packages register namespaced commands.
205
+
206
+ The agent create/edit surfaces are _not_ open questions: the operator firmly removes both (managing agent definitions belongs in an editor/IDE or a Pi agent, not the menu).
207
+
208
+ [#422]: https://github.com/gotgenes/pi-packages/issues/422
209
+ [#423]: https://github.com/gotgenes/pi-packages/issues/423
210
+ [#424]: https://github.com/gotgenes/pi-packages/issues/424
211
+ [#425]: https://github.com/gotgenes/pi-packages/issues/425
212
+ [#427]: https://github.com/gotgenes/pi-packages/issues/427
@@ -0,0 +1,171 @@
1
+ ---
2
+ issue: 446
3
+ issue_title: "pi-subagents: spike — resolve ADR-0004 session-navigation entry criteria"
4
+ ---
5
+
6
+ # Spike: resolve ADR-0004 session-navigation entry criteria
7
+
8
+ ## Release Recommendation
9
+
10
+ **Release:** ship independently
11
+
12
+ Phase 19 Step 1 ([#446]) carries `Release: independent` in the architecture roadmap, and the roadmap's "Release batches" subsection lists it among the independently releasable steps (the only batch is "dissolve-agents" = Steps 5, 6).
13
+ The deliverable is an ADR addendum that unblocks Step 4 ([#445]); it ships on its own with no batch coupling.
14
+
15
+ ## Problem Statement
16
+
17
+ ADR-0004 (Phase 18) decided the UI *direction* — replace the bespoke `ConversationViewer` overlay with Pi's own session machinery applied to each child's already-persisted session JSONL — but deliberately left the *mechanism* open behind four "Phase 19 entry criteria."
18
+ Each criterion changes the shape of the Step 4 session-navigation code, so all four must be answered before that work can begin.
19
+ This is a spike: produce a minimal, throwaway investigation that answers each criterion against the real SDK surface, then record the answers as an addendum to `docs/decisions/0004-reconsider-ui-direction.md`.
20
+ No production source files change; the spike closes when the ADR addendum is merged.
21
+
22
+ ## Goals
23
+
24
+ - Answer all four ADR-0004 entry criteria with evidence from the bundled `@earendil-works/pi-coding-agent` SDK surface and a real child session JSONL:
25
+ 1. Root-continuity during a session switch (and the correct "return to root" gesture).
26
+ 2. View-only vs interactive (switch-takeover vs `loadEntriesFromFile` read-only transcript).
27
+ 3. Parallel-agent navigation gesture (widget, command, or both).
28
+ 4. Settings command name (`/subagents-settings` vs `/agents-settings` vs a `:`-namespaced form).
29
+ - Record the answers as a dated addendum to ADR-0004 so Step 4 ([#445]) can commit to a mechanism.
30
+ - Validate the read-only transcript path empirically: a throwaway vitest harness exercises `loadEntriesFromFile` against a real child session file and confirms entries render without a session switch.
31
+
32
+ ## Non-Goals
33
+
34
+ - No production source changes — `session-navigator.ts`, the widget background-only filter, the settings command, and the `/agents` dissolution are all later Phase 19 steps ([#445], [#444], [#447], [#442], [#441]).
35
+ - Do not implement the chosen mechanism; the spike only chooses it.
36
+ - Do not commit the vitest spike harness — it is throwaway evidence, discarded after observation (operator decision: ADR addendum only).
37
+ - Do not modify `0004-reconsider-ui-direction.md`'s existing Status/Decision/Consequences body beyond appending the addendum.
38
+
39
+ ## Background
40
+
41
+ Relevant SDK surface, verified against the bundled `@earendil-works/pi-coding-agent` types in `packages/pi-subagents/node_modules/.../dist`:
42
+
43
+ - `ExtensionCommandContext.switchSession(sessionPath, { withSession })` — a **full active-session takeover** (`core/extensions/types.d.ts:276`).
44
+ It returns `{ cancelled }` and hands the replacement session to an optional `withSession(ctx: ReplacedSessionContext)` callback.
45
+ - `ReplacedSessionContext extends ExtensionCommandContext` (`types.d.ts:289`) — exposes `sendUserMessage` and `sendMessage`, so a switched-to child session is **interactive**, not read-only.
46
+ - `session_before_switch` (cancellable; `reason: "new" | "resume"`) and `session_shutdown` (`reason: "quit" | "reload" | "new" | "resume" | "fork"`, with `targetSessionFile`) events (`types.d.ts:414`, `:440`) fire around a switch — the current runtime is torn down when the active session is replaced.
47
+ - `setBeforeSessionInvalidate` lives on the **host** runtime (`core/agent-session-runtime.*`, `modes/interactive/interactive-mode.*`), not on the extension command context — it is a host-owned UI-teardown seam, not something this extension calls.
48
+ - `loadEntriesFromFile(filePath): FileEntry[]` and `parseSessionEntries(content): FileEntry[]` (`core/session-manager.d.ts:151`, `:141`, both re-exported from the package root `index.d.ts`) — read a session file's entries **without** switching.
49
+ - `Subagent.outputFile: string | undefined` (`src/lifecycle/subagent.ts:120`, delegating to `subagentSession?.outputFile`) — already exposes each child's persisted session JSONL path; no new SDK dependency is needed.
50
+ - Sibling command registration uses **flat, hyphenated** names with no `:` namespace: `registerCommand("agents", …)` (this package), `registerCommand("colgrep-reindex", …)`, `registerCommand("permission-system", …)`.
51
+
52
+ AGENTS.md constraint: pi-subagents is a minimal core; the surviving UI is an in-core reactive consumer (ADR-0004 Decision D).
53
+ The spike chooses the navigation mechanism but adds no policy and no new core dependency.
54
+
55
+ ## Design Overview
56
+
57
+ The spike is an investigation, not a feature.
58
+ Each criterion is answered from the SDK surface above; the one path with genuine runtime uncertainty (read-only transcript rendering) is confirmed with a throwaway vitest harness against a real child session JSONL.
59
+
60
+ ### Criterion 1 — Root-continuity during a session switch
61
+
62
+ `switchSession` replaces the active session: it fires `session_before_switch` (cancellable) then tears the current runtime down via `session_shutdown` (the `targetSessionFile` field marks a replacement-driven shutdown).
63
+ The root's in-flight turn does **not** survive a takeover — the runtime that owns that turn is invalidated.
64
+ The "return to root" gesture under a true round-trip would be a second `switchSession(rootSessionFile)`, re-incurring the same teardown/replay cost on the way back.
65
+
66
+ Expected finding: a true `switchSession` round-trip is hostile to a root that may have a turn in flight (background agents run precisely while the operator keeps working at root).
67
+ This pushes the recommendation toward the read-only `loadEntriesFromFile` path, which never touches the active session and so has no return gesture to get wrong.
68
+
69
+ ### Criterion 2 — View-only vs interactive
70
+
71
+ `ReplacedSessionContext` exposes `sendUserMessage`/`sendMessage`, so switching makes the child interactive — the operator could steer a child from inside its own session.
72
+ ADR-0004 frames operator visibility (concern 3) as "switch in, scroll/read, switch between, exit back to root" — a **navigation** interaction, not a live steering overlay.
73
+ Steering already has a home (`steer_subagent` tool / the widget), so conflating read-navigation with in-session steering adds a second, redundant steering surface.
74
+
75
+ Expected finding: the viewer should be **read-only**, favoring `loadEntriesFromFile` to render the child's persisted entries without leaving the root session.
76
+ This also resolves Criterion 1 by construction (no switch, no root-continuity problem).
77
+
78
+ ### Criterion 3 — Parallel-agent navigation
79
+
80
+ With N background agents running, the operator needs a gesture to pick which child to view.
81
+ The background widget (ADR-0004 Decision A, [#444]) already represents N parallel agents as a per-agent tree, making it the natural **selection** surface.
82
+ A flat command (Criterion 4 naming) gives a non-widget entry point that lists running background agents and lets the operator pick one keyed on `record.outputFile`.
83
+
84
+ Expected finding: provide a **command** as the primary, testable selection surface (lists background agents → operator picks → render that child's transcript read-only), with a widget gesture as an optional later enhancement.
85
+ The command is unit-testable and does not depend on the widget landing first; "both" is the eventual target, command-first is the spike's recommended Step 4 starting point.
86
+
87
+ ### Criterion 4 — Settings command name
88
+
89
+ Sibling packages register flat, hyphenated command names with no `:` namespace (`agents`, `colgrep-reindex`, `permission-system`).
90
+ A `/subagents:settings` form would be inconsistent with every existing command in the repo.
91
+
92
+ Expected finding: confirm **`/subagents-settings`** (flat, hyphenated) — already the name used in the architecture roadmap's Step 2 ([#447]).
93
+ Reject the ADR's tentative `/subagents:settings` and the `/agents-settings` alternative (the latter implies it manages agent definitions, which Decision C removes).
94
+
95
+ ### Throwaway vitest harness (discarded, not committed)
96
+
97
+ A single throwaway spec confirms the read-only path end to end:
98
+
99
+ ```typescript
100
+ // throwaway — discarded after observation, never committed
101
+ import { loadEntriesFromFile } from "@earendil-works/pi-coding-agent";
102
+
103
+ // against a REAL child session JSONL produced by a background subagent run
104
+ const entries = loadEntriesFromFile(childOutputFile);
105
+ // assert: returns FileEntry[] with the expected message/turn entries,
106
+ // renderable as a transcript, with no switchSession / no active-session mutation.
107
+ ```
108
+
109
+ The harness sources `childOutputFile` from a real run (or an existing session fixture under the Pi session dir), not a synthetic stub, so the observation reflects the actual on-disk JSONL shape the Step 4 viewer will consume.
110
+ It asserts the entries are well-formed and that the read path requires no session switch — confirming `loadEntriesFromFile(Subagent.outputFile)` is a viable read-only transcript source.
111
+
112
+ ## Module-Level Changes
113
+
114
+ - `packages/pi-subagents/docs/decisions/0004-reconsider-ui-direction.md` — **append** an addendum section (e.g. `## Addendum (2026-06-..): Phase 19 entry-criteria answers`) recording the four answers with their SDK evidence and the resulting Step 4 mechanism decision (read-only `loadEntriesFromFile` transcript, command-first selection, `/subagents-settings` name).
115
+ Do not rewrite the existing "Phase 19 entry criteria" section — leave it as the question of record and let the addendum answer it.
116
+ - `packages/pi-subagents/docs/architecture/architecture.md` — optional doc-sync only: the Step 1 entry already states its `Outcome`; if marking the spike resolved here is desired, update only that step's status line.
117
+ Keep out of scope unless the operator wants it folded in at ship time.
118
+
119
+ No `src/` or committed `test/` files change.
120
+ The vitest spike harness is throwaway and is **not** added to the committed tree (operator decision: ADR addendum only).
121
+
122
+ ## Test Impact Analysis
123
+
124
+ Not applicable in the usual sense — this spike commits no production code and no retained tests.
125
+ The throwaway vitest harness exists only to observe `loadEntriesFromFile` behavior against a real child session JSONL and is discarded; it enables no new committed test surface and makes no existing test redundant.
126
+ Step 4 ([#445]) will introduce the committed tests for the chosen mechanism.
127
+
128
+ ## Invariants at risk
129
+
130
+ None.
131
+ The spike changes no runtime code, so the Phase 18 spine invariants ADR-0004 lists (runtime holds zero UI state [#422]; widget is a reactive consumer with no inbound core calls [#423]; the `subagent` tool depends only on manager/runtime/settings/registry [#424]; declared event channels equal emitted channels [#425]) are untouched and stay pinned by their existing suites.
132
+ The addendum must not recommend a Step 4 mechanism that would later violate them — the read-only `loadEntriesFromFile` path is chosen partly because it keeps transcript rendering out of core and adds no inbound call to the core from the UI.
133
+
134
+ ## Build Order
135
+
136
+ This is a docs/spike deliverable (next stage: `/build-plan`), so the order is investigate → confirm → write → discard, not red→green→commit.
137
+
138
+ 1. **Investigate the SDK surface and confirm the read path.**
139
+ Re-verify the `switchSession`/`ReplacedSessionContext`/event semantics and `loadEntriesFromFile` signatures in the bundled SDK types, then run the throwaway vitest harness against a real child session JSONL to confirm read-only transcript rendering is viable.
140
+ No commit (throwaway harness is discarded).
141
+ 2. **Write the ADR-0004 addendum.**
142
+ Append the dated addendum answering all four criteria with their SDK evidence and the resulting Step 4 mechanism decision.
143
+ Verify: addendum present, four criteria each answered with a recommendation, markdown lints clean (`pnpm --filter @gotgenes/pi-subagents run lint` or repo `pnpm run lint`).
144
+ Commit: `docs: resolve ADR-0004 session-navigation entry criteria (#446)`.
145
+ 3. **Optional doc-sync.**
146
+ If folding in the architecture status update, amend only Step 1's status line in `architecture.md` in the same or a follow-up `docs:` commit; otherwise leave it for ship time.
147
+
148
+ ## Risks and Mitigations
149
+
150
+ - **Risk: root-continuity cannot be fully proven without a live multi-session run, and the chosen method is an automated harness only.**
151
+ Mitigation: the answer is derivable from the documented event semantics (a switch tears down the active runtime via `session_shutdown`), which is exactly why the recommendation avoids `switchSession` for navigation; the harness confirms the read-only alternative works, sidestepping the unproven path.
152
+ - **Risk: the addendum recommends a mechanism that Step 4 cannot honor (e.g. a TUI rendering limitation in `loadEntriesFromFile`).**
153
+ Mitigation: the throwaway harness exercises the real entries shape before the recommendation is written; Step 4 retains the freedom to revisit if a rendering gap surfaces, since the ADR records direction, not an irreversible commitment.
154
+ - **Risk: scope creep into implementing the chosen surface.**
155
+ Mitigation: Non-Goals explicitly defer all production changes to [#445]/[#444]/[#447]/[#442]/[#441]; the only committed file is the ADR addendum.
156
+
157
+ ## Open Questions
158
+
159
+ - Whether to also land the `architecture.md` Step 1 status update in this issue or defer it — decide at ship time (Build Order step 3).
160
+ - The widget gesture for parallel-agent navigation (Criterion 3 "both") is left to Step 4 — the spike recommends command-first and notes the widget gesture as an optional follow-up.
161
+
162
+ [#422]: https://github.com/gotgenes/pi-packages/issues/422
163
+ [#423]: https://github.com/gotgenes/pi-packages/issues/423
164
+ [#424]: https://github.com/gotgenes/pi-packages/issues/424
165
+ [#425]: https://github.com/gotgenes/pi-packages/issues/425
166
+ [#441]: https://github.com/gotgenes/pi-packages/issues/441
167
+ [#442]: https://github.com/gotgenes/pi-packages/issues/442
168
+ [#444]: https://github.com/gotgenes/pi-packages/issues/444
169
+ [#445]: https://github.com/gotgenes/pi-packages/issues/445
170
+ [#446]: https://github.com/gotgenes/pi-packages/issues/446
171
+ [#447]: https://github.com/gotgenes/pi-packages/issues/447