@gotgenes/pi-subagents 18.0.1 → 18.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/docs/architecture/architecture.md +157 -204
- package/docs/architecture/history/phase-19-implement-ui-decisions.md +282 -0
- package/package.json +5 -6
- package/src/lifecycle/subagent.ts +5 -9
- package/src/observation/notification.ts +23 -10
- package/src/observation/subagent-events-observer.ts +1 -3
- package/src/tools/get-result-report.ts +60 -0
- package/src/tools/get-result-tool.ts +25 -40
- package/src/types.ts +1 -1
- package/src/ui/session-navigator.ts +0 -2
- package/.prettierignore +0 -5
- package/AGENTS.md +0 -7
- package/docs/plans/0048-implement-subagents-api.md +0 -304
- package/docs/plans/0049-remove-group-join-output-file-rpc.md +0 -180
- package/docs/plans/0051-update-adr-0001-hard-fork.md +0 -77
- package/docs/plans/0052-remove-scheduled-subagents.md +0 -133
- package/docs/plans/0053-extract-model-resolution-from-execute.md +0 -181
- package/docs/plans/0054-decompose-index-into-modules.md +0 -302
- package/docs/plans/0057-structured-debug-logging.md +0 -154
- package/docs/plans/0061-session-format-transcript.md +0 -284
- package/docs/plans/0066-replace-as-any-with-sdk-types.md +0 -215
- package/docs/plans/0069-create-subagent-runtime.md +0 -345
- package/docs/plans/0070-extract-event-handlers.md +0 -306
- package/docs/plans/0071-extract-session-config-assembler.md +0 -362
- package/docs/plans/0072-inject-agent-manager-collaborators.md +0 -329
- package/docs/plans/0076-inject-cwd-into-agent-manager.md +0 -102
- package/docs/plans/0077-inject-project-agents-dir.md +0 -115
- package/docs/plans/0080-consolidate-agent-config-lookup.md +0 -247
- package/docs/plans/0084-extract-git-worktree-manager.md +0 -142
- package/docs/plans/0087-evolve-subagent-runtime-methods.md +0 -240
- package/docs/plans/0098-extract-agent-record-state-machine.md +0 -435
- package/docs/plans/0099-replace-ctx-with-parent-snapshot.md +0 -488
- package/docs/plans/0100-replace-callback-threading-with-session-subscription.md +0 -454
- package/docs/plans/0102-consolidate-test-record-factory.md +0 -176
- package/docs/plans/0108-extract-agent-type-registry.md +0 -322
- package/docs/plans/0109-extract-settings-manager.md +0 -276
- package/docs/plans/0110-agent-activity-tracker.md +0 -297
- package/docs/plans/0111-split-agent-record-lifecycle.md +0 -582
- package/docs/plans/0112-replace-agent-manager-callbacks.md +0 -241
- package/docs/plans/0113-disambiguate-spawn-options.md +0 -155
- package/docs/plans/0114-narrow-agent-tool-menu-deps.md +0 -279
- package/docs/plans/0115-decompose-agent-tool.md +0 -337
- package/docs/plans/0116-type-housekeeping.md +0 -351
- package/docs/plans/0118-settings-manager-apply-methods.md +0 -271
- package/docs/plans/0123-remove-vi-fn-cast-smell.md +0 -179
- package/docs/plans/0131-consolidate-shared-test-fixtures.md +0 -207
- package/docs/plans/0132-inject-io-into-session-config.md +0 -219
- package/docs/plans/0133-inject-sdk-boundary-into-agent-runner.md +0 -373
- package/docs/plans/0134-reduce-as-any-casts.md +0 -366
- package/docs/plans/0135-extract-display-helpers.md +0 -182
- package/docs/plans/0136-decompose-agent-menu.md +0 -300
- package/docs/plans/0144-consolidate-observation-model.md +0 -263
- package/docs/plans/0145-decompose-execute-push-ctx-to-boundary.md +0 -290
- package/docs/plans/0146-narrow-ui-context.md +0 -319
- package/docs/plans/0147-inject-wrap-text-into-conversation-viewer.md +0 -166
- package/docs/plans/0148-split-agent-widget-rendering.md +0 -255
- package/docs/plans/0152-add-prompt-snippet.md +0 -91
- package/docs/plans/0164-reorganize-into-domain-directories.md +0 -409
- package/docs/plans/0165-decompose-resolved-spawn-config.md +0 -157
- package/docs/plans/0166-extract-parent-session-info.md +0 -231
- package/docs/plans/0167-narrow-runner-io.md +0 -150
- package/docs/plans/0168-extract-tool-filter-config.md +0 -173
- package/docs/plans/0169-extract-run-context-from-run-options.md +0 -194
- package/docs/plans/0170-reduce-build-content-lines-complexity.md +0 -225
- package/docs/plans/0171-reduce-render-result-complexity.md +0 -200
- package/docs/plans/0172-extract-turn-formatting.md +0 -206
- package/docs/plans/0180-reorder-append-prompt-for-kv-cache.md +0 -100
- package/docs/plans/0185-remove-persistent-agent-memory.md +0 -161
- package/docs/plans/0188-replace-any-casts-with-sdk-types.md +0 -162
- package/docs/plans/0192-define-session-context-interface.md +0 -107
- package/docs/plans/0193-runtime-owns-context-queries.md +0 -245
- package/docs/plans/0194-align-tool-interfaces-for-structural-typing.md +0 -139
- package/docs/plans/0195-convert-tool-factories-to-classes.md +0 -264
- package/docs/plans/0196-convert-runner-menu-to-classes.md +0 -268
- package/docs/plans/0205-decompose-render-widget-lines.md +0 -140
- package/docs/plans/0206-decompose-show-agent-detail.md +0 -211
- package/docs/plans/0207-decompose-agent-widget-update.md +0 -242
- package/docs/plans/0208-extract-shared-test-fixtures.md +0 -298
- package/docs/plans/0214-convert-remaining-closure-factories-to-classes.md +0 -261
- package/docs/plans/0215-decompose-build-parent-context.md +0 -166
- package/docs/plans/0216-decompose-start-agent.md +0 -255
- package/docs/plans/0217-extract-overwrite-guard.md +0 -176
- package/docs/plans/0218-push-sdk-boundary-in-settings.md +0 -172
- package/docs/plans/0219-reduce-test-duplication-top-3-clone-families.md +0 -162
- package/docs/plans/0227-evolve-agent-record-into-agent.md +0 -322
- package/docs/plans/0228-async-start-agent-dissolve-run-handle.md +0 -288
- package/docs/plans/0229-agent-born-complete.md +0 -564
- package/docs/plans/0230-extract-concurrency-queue.md +0 -265
- package/docs/plans/0231-push-exec-registry-to-runner.md +0 -245
- package/docs/plans/0232-agent-resume-internal-observer-lifecycle.md +0 -180
- package/docs/plans/0237-remove-disallowed-tools.md +0 -180
- package/docs/plans/0238-remove-extensions-filtering.md +0 -191
- package/docs/plans/0239-collapse-filter-active-tools.md +0 -217
- package/docs/plans/0242-rename-agent-tool-to-subagent.md +0 -165
- package/docs/plans/0256-extract-worktree-isolation.md +0 -256
- package/docs/plans/0257-extract-child-session-factory.md +0 -285
- package/docs/plans/0262-add-workspace-provider-seam.md +0 -264
- package/docs/plans/0264-remove-extension-lifecycle-control.md +0 -277
- package/docs/plans/0265-born-complete-subagent-session.md +0 -333
- package/docs/plans/0270-type-consumable-public-surface.md +0 -204
- package/docs/plans/0272-export-workspace-collaborator-types.md +0 -147
- package/docs/plans/0277-encapsulate-agent-session.md +0 -304
- package/docs/plans/0280-rename-agent-to-subagent.md +0 -197
- package/docs/plans/0360-custom-agents-default-append.md +0 -132
- package/docs/plans/0373-extract-subagent-state.md +0 -250
- package/docs/plans/0374-encapsulate-subagent-start-notification.md +0 -268
- package/docs/plans/0375-extract-run-listener-workspace-bracket.md +0 -300
- package/docs/plans/0376-extract-manager-observer.md +0 -232
- package/docs/plans/0377-split-widget-delegation-from-runtime.md +0 -254
- package/docs/plans/0378-consolidate-lifecycle-test-fixtures.md +0 -243
- package/docs/plans/0379-consolidate-ui-tools-test-fixtures.md +0 -335
- package/docs/plans/0380-extract-layered-settings-loader.md +0 -223
- package/docs/plans/0381-replace-concurrency-queue-with-limiter.md +0 -267
- package/docs/plans/0400-include-parent-prompt-in-replace-mode.md +0 -199
- package/docs/plans/0403-abort-subagents-on-interrupt.md +0 -180
- package/docs/plans/0412-unify-session-mock-builders.md +0 -146
- package/docs/plans/0420-fold-run-metrics-live-activity-onto-record.md +0 -228
- package/docs/plans/0421-migrate-activity-readers-to-record-getters.md +0 -309
- package/docs/plans/0422-delete-activity-tracker-ui-observer.md +0 -204
- package/docs/plans/0423-widget-self-drive-from-lifecycle.md +0 -264
- package/docs/plans/0424-drop-widget-dep-from-subagent-tool.md +0 -166
- package/docs/plans/0425-reconcile-subagent-events-contract.md +0 -144
- package/docs/plans/0426-consolidate-residual-test-clone-families.md +0 -235
- package/docs/plans/0427-reconsider-ui-direction.md +0 -212
- package/docs/plans/0441-remove-agent-definition-management-subtree.md +0 -180
- package/docs/plans/0442-dissolve-agents-conversation-viewer.md +0 -195
- package/docs/plans/0443-consolidate-remaining-test-clone-families.md +0 -268
- package/docs/plans/0444-shrink-widget-to-background-agents.md +0 -156
- package/docs/plans/0445-native-session-navigation.md +0 -365
- package/docs/plans/0446-spike-session-navigation-entry-criteria.md +0 -171
- package/docs/plans/0447-extract-subagents-settings-command.md +0 -220
- package/docs/plans/0448-block-disabled-agent-spawn.md +0 -207
- package/docs/plans/0462-navigation-renderer-tui-components.md +0 -338
- package/docs/plans/0463-file-snapshot-source-evicted-agents.md +0 -306
- package/docs/plans/0470-refresh-readme-command-surface.md +0 -159
- package/docs/retro/0048-implement-subagents-api.md +0 -44
- package/docs/retro/0049-remove-group-join-output-file-rpc.md +0 -49
- package/docs/retro/0051-update-adr-0001-hard-fork.md +0 -39
- package/docs/retro/0053-extract-model-resolution-from-execute.md +0 -40
- package/docs/retro/0054-decompose-index-into-modules.md +0 -53
- package/docs/retro/0057-structured-debug-logging.md +0 -77
- package/docs/retro/0061-session-format-transcript.md +0 -41
- package/docs/retro/0066-replace-as-any-with-sdk-types.md +0 -44
- package/docs/retro/0069-create-subagent-runtime.md +0 -43
- package/docs/retro/0070-extract-event-handlers.md +0 -49
- package/docs/retro/0071-extract-session-config-assembler.md +0 -60
- package/docs/retro/0072-inject-agent-manager-collaborators.md +0 -46
- package/docs/retro/0076-inject-cwd-into-agent-manager.md +0 -35
- package/docs/retro/0077-inject-project-agents-dir.md +0 -33
- package/docs/retro/0080-consolidate-agent-config-lookup.md +0 -35
- package/docs/retro/0084-extract-git-worktree-manager.md +0 -37
- package/docs/retro/0087-evolve-subagent-runtime-methods.md +0 -42
- package/docs/retro/0098-extract-agent-record-state-machine.md +0 -46
- package/docs/retro/0099-replace-ctx-with-parent-snapshot.md +0 -37
- package/docs/retro/0100-replace-callback-threading-with-session-subscription.md +0 -36
- package/docs/retro/0102-consolidate-test-record-factory.md +0 -30
- package/docs/retro/0108-extract-agent-type-registry.md +0 -41
- package/docs/retro/0109-extract-settings-manager.md +0 -55
- package/docs/retro/0110-agent-activity-tracker.md +0 -44
- package/docs/retro/0111-split-agent-record-lifecycle.md +0 -61
- package/docs/retro/0112-replace-agent-manager-callbacks.md +0 -35
- package/docs/retro/0113-disambiguate-spawn-options.md +0 -29
- package/docs/retro/0114-narrow-agent-tool-menu-deps.md +0 -38
- package/docs/retro/0115-decompose-agent-tool.md +0 -51
- package/docs/retro/0116-type-housekeeping.md +0 -42
- package/docs/retro/0118-settings-manager-apply-methods.md +0 -40
- package/docs/retro/0123-remove-vi-fn-cast-smell.md +0 -49
- package/docs/retro/0131-consolidate-shared-test-fixtures.md +0 -46
- package/docs/retro/0132-inject-io-into-session-config.md +0 -33
- package/docs/retro/0133-inject-sdk-boundary-into-agent-runner.md +0 -45
- package/docs/retro/0134-reduce-as-any-casts.md +0 -56
- package/docs/retro/0135-extract-display-helpers.md +0 -38
- package/docs/retro/0136-decompose-agent-menu.md +0 -43
- package/docs/retro/0144-consolidate-observation-model.md +0 -39
- package/docs/retro/0145-decompose-execute-push-ctx-to-boundary.md +0 -56
- package/docs/retro/0146-narrow-ui-context.md +0 -70
- package/docs/retro/0147-inject-wrap-text-into-conversation-viewer.md +0 -90
- package/docs/retro/0148-split-agent-widget-rendering.md +0 -39
- package/docs/retro/0152-add-prompt-snippet.md +0 -34
- package/docs/retro/0164-reorganize-into-domain-directories.md +0 -82
- package/docs/retro/0165-decompose-resolved-spawn-config.md +0 -78
- package/docs/retro/0166-extract-parent-session-info.md +0 -66
- package/docs/retro/0167-narrow-runner-io.md +0 -63
- package/docs/retro/0168-extract-tool-filter-config.md +0 -76
- package/docs/retro/0169-extract-run-context-from-run-options.md +0 -68
- package/docs/retro/0170-reduce-build-content-lines-complexity.md +0 -72
- package/docs/retro/0171-reduce-render-result-complexity.md +0 -77
- package/docs/retro/0172-extract-turn-formatting.md +0 -80
- package/docs/retro/0180-reorder-append-prompt-for-kv-cache.md +0 -62
- package/docs/retro/0185-remove-persistent-agent-memory.md +0 -73
- package/docs/retro/0188-replace-any-casts-with-sdk-types.md +0 -69
- package/docs/retro/0192-define-session-context-interface.md +0 -59
- package/docs/retro/0193-runtime-owns-context-queries.md +0 -75
- package/docs/retro/0194-align-tool-interfaces-for-structural-typing.md +0 -62
- package/docs/retro/0195-convert-tool-factories-to-classes.md +0 -84
- package/docs/retro/0196-convert-runner-menu-to-classes.md +0 -73
- package/docs/retro/0205-decompose-render-widget-lines.md +0 -64
- package/docs/retro/0206-decompose-show-agent-detail.md +0 -67
- package/docs/retro/0207-decompose-agent-widget-update.md +0 -92
- package/docs/retro/0208-extract-shared-test-fixtures.md +0 -102
- package/docs/retro/0214-convert-remaining-closure-factories-to-classes.md +0 -66
- package/docs/retro/0215-decompose-build-parent-context.md +0 -61
- package/docs/retro/0216-decompose-start-agent.md +0 -80
- package/docs/retro/0217-extract-overwrite-guard.md +0 -63
- package/docs/retro/0218-push-sdk-boundary-in-settings.md +0 -72
- package/docs/retro/0219-reduce-test-duplication-top-3-clone-families.md +0 -64
- package/docs/retro/0227-evolve-agent-record-into-agent.md +0 -80
- package/docs/retro/0228-async-start-agent-dissolve-run-handle.md +0 -80
- package/docs/retro/0229-agent-born-complete.md +0 -89
- package/docs/retro/0230-extract-concurrency-queue.md +0 -38
- package/docs/retro/0231-push-exec-registry-to-runner.md +0 -71
- package/docs/retro/0232-agent-resume-internal-observer-lifecycle.md +0 -109
- package/docs/retro/0237-remove-disallowed-tools.md +0 -78
- package/docs/retro/0238-remove-extensions-filtering.md +0 -72
- package/docs/retro/0239-collapse-filter-active-tools.md +0 -70
- package/docs/retro/0242-rename-agent-tool-to-subagent.md +0 -82
- package/docs/retro/0256-extract-worktree-isolation.md +0 -89
- package/docs/retro/0257-extract-child-session-factory.md +0 -33
- package/docs/retro/0262-add-workspace-provider-seam.md +0 -89
- package/docs/retro/0264-remove-extension-lifecycle-control.md +0 -91
- package/docs/retro/0265-born-complete-subagent-session.md +0 -97
- package/docs/retro/0270-type-consumable-public-surface.md +0 -108
- package/docs/retro/0272-export-workspace-collaborator-types.md +0 -38
- package/docs/retro/0277-encapsulate-agent-session.md +0 -80
- package/docs/retro/0280-rename-agent-to-subagent.md +0 -96
- package/docs/retro/0360-custom-agents-default-append.md +0 -81
- package/docs/retro/0373-extract-subagent-state.md +0 -94
- package/docs/retro/0374-encapsulate-subagent-start-notification.md +0 -171
- package/docs/retro/0375-extract-run-listener-workspace-bracket.md +0 -104
- package/docs/retro/0376-extract-manager-observer.md +0 -81
- package/docs/retro/0377-split-widget-delegation-from-runtime.md +0 -105
- package/docs/retro/0378-consolidate-lifecycle-test-fixtures.md +0 -100
- package/docs/retro/0379-consolidate-ui-tools-test-fixtures.md +0 -52
- package/docs/retro/0380-extract-layered-settings-loader.md +0 -97
- package/docs/retro/0381-replace-concurrency-queue-with-limiter.md +0 -95
- package/docs/retro/0400-include-parent-prompt-in-replace-mode.md +0 -84
- package/docs/retro/0403-abort-subagents-on-interrupt.md +0 -90
- package/docs/retro/0412-unify-session-mock-builders.md +0 -80
- package/docs/retro/0420-fold-run-metrics-live-activity-onto-record.md +0 -90
- package/docs/retro/0421-migrate-activity-readers-to-record-getters.md +0 -105
- package/docs/retro/0422-delete-activity-tracker-ui-observer.md +0 -97
- package/docs/retro/0423-widget-self-drive-from-lifecycle.md +0 -112
- package/docs/retro/0424-drop-widget-dep-from-subagent-tool.md +0 -89
- package/docs/retro/0425-reconcile-subagent-events-contract.md +0 -101
- package/docs/retro/0426-consolidate-residual-test-clone-families.md +0 -100
- package/docs/retro/0427-reconsider-ui-direction.md +0 -102
- package/docs/retro/0441-remove-agent-definition-management-subtree.md +0 -97
- package/docs/retro/0442-dissolve-agents-conversation-viewer.md +0 -108
- package/docs/retro/0443-consolidate-remaining-test-clone-families.md +0 -28
- package/docs/retro/0444-shrink-widget-to-background-agents.md +0 -81
- package/docs/retro/0445-native-session-navigation.md +0 -122
- package/docs/retro/0446-spike-session-navigation-entry-criteria.md +0 -99
- package/docs/retro/0447-extract-subagents-settings-command.md +0 -91
- package/docs/retro/0448-block-disabled-agent-spawn.md +0 -75
- package/docs/retro/0462-navigation-renderer-tui-components.md +0 -105
- package/docs/retro/0463-file-snapshot-source-evicted-agents.md +0 -103
- package/docs/retro/0470-refresh-readme-command-surface.md +0 -40
- package/src/observation/notification-state.ts +0 -27
- package/vitest.config.ts +0 -14
|
@@ -1,306 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
issue: 463
|
|
3
|
-
issue_title: "pi-subagents: add file-snapshot source to /subagent-sessions for evicted agents"
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# File-snapshot source for evicted agents in `/subagent-sessions`
|
|
7
|
-
|
|
8
|
-
## Release Recommendation
|
|
9
|
-
|
|
10
|
-
**Release:** ship independently
|
|
11
|
-
|
|
12
|
-
Architecture roadmap Phase 19 Step 4b ([#463]) carries `Release: independent`.
|
|
13
|
-
This is a new capability the bespoke viewer never had — it gates nothing and is not a Step 5 prerequisite — so it ships on its own once landed.
|
|
14
|
-
|
|
15
|
-
## Problem Statement
|
|
16
|
-
|
|
17
|
-
The [#445] native-session-navigation slice sources transcripts live, from `manager.listAgents()` only.
|
|
18
|
-
The manager's cleanup sweep (`SubagentManager.cleanup()`, every 60 s) disposes a completed/stopped/errored record 10 minutes after it finishes — `disposeSession()` frees the heavy in-memory session (its message history included) and the record is deleted from the map.
|
|
19
|
-
The lightweight session JSONL persists on disk at `Subagent.outputFile`, but with no live record the agent is unreachable from `/subagent-sessions`.
|
|
20
|
-
|
|
21
|
-
This step makes a fully-evicted agent navigable: it adds the file-snapshot `TranscriptSource` branch the #445 seam was designed for, and broadens the picker's candidate set to include evicted agents.
|
|
22
|
-
|
|
23
|
-
## Goals
|
|
24
|
-
|
|
25
|
-
- Implement `fileSnapshotSource(outputFile, readFile)` in `src/ui/session-navigation.ts`: `parseSessionEntries(readFile(outputFile))` → drop the `SessionHeader` → `buildSessionContext(...).messages` → a static (no-subscribe, no-streaming) `TranscriptSource`.
|
|
26
|
-
- Broaden the `/subagent-sessions` candidate set to include evicted agents, deduped against live records.
|
|
27
|
-
- Render an evicted agent's transcript read-only from its persisted file when picked; the renderer (`buildTranscriptComponents`) and overlay (`TranscriptOverlay`) are untouched.
|
|
28
|
-
- Inject `readFile` as a parameter so the pure module performs no `fs` calls.
|
|
29
|
-
|
|
30
|
-
This change is **not** breaking: it is additive (a new source branch and a broadened candidate set); no existing behavior, output shape, or default changes for a tracked agent.
|
|
31
|
-
|
|
32
|
-
## Non-Goals
|
|
33
|
-
|
|
34
|
-
- No directory scan of the tasks directory and no surfacing of prior-process orphan session files (see Design Overview → Candidate-set decision).
|
|
35
|
-
- No change to the cleanup sweep's 10-minute eviction policy or to `disposeSession()`.
|
|
36
|
-
- No renderer or overlay changes — `session-navigator.ts`'s `buildTranscriptComponents` / `TranscriptOverlay` / `addMessageComponents` are unchanged (Step 4a, [#462], already brought them to parity).
|
|
37
|
-
- No `switchSession` / `loadEntriesFromFile` (rejected by the Step 1 spike, [#446]).
|
|
38
|
-
- No live re-read or file-watching of the snapshot — a file source is a static snapshot by design.
|
|
39
|
-
|
|
40
|
-
## Background
|
|
41
|
-
|
|
42
|
-
Relevant modules:
|
|
43
|
-
|
|
44
|
-
- `src/ui/session-navigation.ts` — the pure, unit-testable core: `NavigableSubagent` (narrow record view), `NavigationEntry`, `TranscriptSource`, `listNavigableAgents(agents, registry)`, `liveSource(record)`, `buildLabel(record, registry)`.
|
|
45
|
-
It imports SDK *types* only (`ToolDefinition`) and the lifecycle type `SubagentStatus`.
|
|
46
|
-
- `src/ui/session-navigator.ts` — the SDK/TUI consumer half: `SessionNavigatorHandler` (picker + source selection) and `TranscriptOverlay` (read-only scrollable renderer).
|
|
47
|
-
The handler already calls `liveSource(entry.record)`; the source is the only seam that varies.
|
|
48
|
-
- `src/lifecycle/subagent-manager.ts` — `listAgents()` (sorted live records), `cleanup()` (the 10-minute sweep), `clearCompleted()` (new-session wipe, called from `handlers/lifecycle.ts` on `session_start` / `session_before_switch`), `removeRecord()` (`disposeSession()` + `agents.delete`).
|
|
49
|
-
- `src/lifecycle/subagent.ts` — `Subagent` exposes `id`, `type`, `description` (readonly), and getters `status`, `startedAt`, `completedAt`, `toolUses`, `outputFile`.
|
|
50
|
-
- `src/index.ts` — registers `subagent-sessions`, calling `sessionNavigator.handle({ ui, agents: manager.listAgents(), registry, cwd })`.
|
|
51
|
-
|
|
52
|
-
SDK functions (`@earendil-works/pi-coding-agent`): `parseSessionEntries(content): FileEntry[]`, `buildSessionContext(entries: SessionEntry[]): { messages: AgentMessage[]; … }`, and the types `SessionEntry` / `SessionHeader`.
|
|
53
|
-
`FileEntry = SessionHeader | SessionEntry`; only the header has `type: "session"`, so `entries.filter((e): e is SessionEntry => e.type !== "session")` is a sound type guard.
|
|
54
|
-
`SessionMessage` (the navigator's message type) is `SessionContext["messages"][number]` = `AgentMessage`, so `buildSessionContext(...).messages` matches `TranscriptSource.getMessages()` with no cast.
|
|
55
|
-
|
|
56
|
-
Constraints from AGENTS.md / skills that apply:
|
|
57
|
-
|
|
58
|
-
- `code-design` SDK-boundary guideline: pure helpers should avoid SDK *runtime* imports.
|
|
59
|
-
`fileSnapshotSource` calls the SDK runtime functions `parseSessionEntries` / `buildSessionContext` directly (see Design Overview → SDK-runtime decision); there is no `no-restricted-imports` lint rule, and the module already depends on SDK types.
|
|
60
|
-
- `testing`: components used by `session-navigator.ts` need `initTheme(undefined, false)` in `beforeAll` (already present in `session-navigator.test.ts`).
|
|
61
|
-
- `fallow dead-code`: a new export with no caller fails the gate.
|
|
62
|
-
Producers (`fileSnapshotSource`, `listEvicted`) gain their caller in the integration step within the same push; the pushed tip is dead-code-clean (see Risks).
|
|
63
|
-
|
|
64
|
-
## Design Overview
|
|
65
|
-
|
|
66
|
-
### Candidate-set decision — manager-retained descriptors (not a directory scan)
|
|
67
|
-
|
|
68
|
-
The issue proposes enumerating persisted JSONL files in the tasks directory.
|
|
69
|
-
The persisted child session carries **no** subagent `type` or `description` — those live only on the in-memory `Subagent` record; the file holds only the conversation plus a header (`id`, `timestamp`, `cwd`, `parentSession`).
|
|
70
|
-
A directory scan therefore yields degraded labels (agent type → generic placeholder; curated `description` → a raw first-prompt snippet or filename) and must parse every file on every picker open.
|
|
71
|
-
|
|
72
|
-
Decision (confirmed with the operator): the manager retains a lightweight **descriptor** at eviction time instead.
|
|
73
|
-
When `cleanup()` disposes a record that has a persisted file, it first copies the record's label fields plus `outputFile` into a separate `evicted` map (no messages — the heavy state is still freed, so memory stays bounded by the number of subagents spawned, not their transcripts).
|
|
74
|
-
The picker's candidate set is `live ∪ evicted`, deduped by id, with identical rich labels for both.
|
|
75
|
-
|
|
76
|
-
Coverage: this surfaces agents evicted **in the current root session** — which are the cleanup sweep's only targets.
|
|
77
|
-
A fresh `SubagentManager` is created per session and `clearCompleted()` runs on `session_start`, so prior-process subagents are never reloaded into the manager; the only files a directory scan would *additionally* surface are old-session orphans, which are exactly the ones with degraded labels.
|
|
78
|
-
Picking an evicted agent still reads its file to render (the messages are gone from memory regardless) — so `fileSnapshotSource` is required either way; the descriptor only feeds the *label*.
|
|
79
|
-
|
|
80
|
-
### SDK-runtime decision — call `parseSessionEntries` / `buildSessionContext` directly
|
|
81
|
-
|
|
82
|
-
`fileSnapshotSource` imports and calls these SDK runtime functions directly rather than injecting them.
|
|
83
|
-
Rationale: they are deterministic parsers of Pi's own session format; the injected `readFile` already provides the unit-test seam (a fake `readFile` returning fixture JSONL fully exercises parse → drop-header → build → messages), so injecting the parsers adds wiring without testability gain.
|
|
84
|
-
There is no `no-restricted-imports` rule, and `session-navigation.ts` already imports SDK types.
|
|
85
|
-
|
|
86
|
-
### Data shapes
|
|
87
|
-
|
|
88
|
-
`EvictedSubagent` — the descriptor, owned by lifecycle (the manager constructs it), imported as a type by the UI (the UI already imports `SubagentStatus` from lifecycle):
|
|
89
|
-
|
|
90
|
-
```typescript
|
|
91
|
-
// src/lifecycle/subagent-manager.ts
|
|
92
|
-
export interface EvictedSubagent {
|
|
93
|
-
readonly id: string;
|
|
94
|
-
readonly type: SubagentType;
|
|
95
|
-
readonly description: string;
|
|
96
|
-
readonly status: SubagentStatus;
|
|
97
|
-
readonly startedAt: number;
|
|
98
|
-
readonly completedAt: number | undefined;
|
|
99
|
-
readonly toolUses: number;
|
|
100
|
-
readonly outputFile: string;
|
|
101
|
-
}
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
`NavigationEntry` changes from `{ record, label }` to a discriminated union so the handler can pick the source by kind:
|
|
105
|
-
|
|
106
|
-
```typescript
|
|
107
|
-
// src/ui/session-navigation.ts
|
|
108
|
-
export type NavigationEntry =
|
|
109
|
-
| { readonly kind: "live"; readonly label: string; readonly record: NavigableSubagent }
|
|
110
|
-
| { readonly kind: "evicted"; readonly label: string; readonly outputFile: string };
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
`buildLabel` is narrowed to a per-call interface (ISP) that both a `NavigableSubagent` and an `EvictedSubagent` satisfy, and gains an `evicted` marker:
|
|
114
|
-
|
|
115
|
-
```typescript
|
|
116
|
-
interface LabelFields {
|
|
117
|
-
readonly type: SubagentType;
|
|
118
|
-
readonly description: string;
|
|
119
|
-
readonly status: SubagentStatus;
|
|
120
|
-
readonly startedAt: number;
|
|
121
|
-
readonly completedAt: number | undefined;
|
|
122
|
-
readonly toolUses: number;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
function buildLabel(fields: LabelFields, registry: AgentConfigLookup, evicted = false): string {
|
|
126
|
-
const name = getDisplayName(fields.type, registry);
|
|
127
|
-
const duration = formatDuration(fields.startedAt, fields.completedAt);
|
|
128
|
-
const marker = evicted ? " · evicted (snapshot)" : "";
|
|
129
|
-
return `${name} (${fields.description}) · ${fields.toolUses} tools · ${fields.status} · ${duration}${marker}`;
|
|
130
|
-
}
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
### `fileSnapshotSource`
|
|
134
|
-
|
|
135
|
-
```typescript
|
|
136
|
-
export function fileSnapshotSource(
|
|
137
|
-
outputFile: string,
|
|
138
|
-
readFile: (path: string) => string,
|
|
139
|
-
): TranscriptSource {
|
|
140
|
-
const entries = parseSessionEntries(readFile(outputFile));
|
|
141
|
-
const sessionEntries = entries.filter(
|
|
142
|
-
(entry): entry is SessionEntry => entry.type !== "session",
|
|
143
|
-
);
|
|
144
|
-
const { messages } = buildSessionContext(sessionEntries);
|
|
145
|
-
return {
|
|
146
|
-
getMessages: () => messages,
|
|
147
|
-
subscribe: () => undefined, // static snapshot — no live updates
|
|
148
|
-
streaming: () => undefined, // never streaming
|
|
149
|
-
getToolDefinition: () => undefined, // no live tool registry off disk
|
|
150
|
-
};
|
|
151
|
-
}
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
### `listNavigableAgents` (broadened) and the handler call site
|
|
155
|
-
|
|
156
|
-
```typescript
|
|
157
|
-
export function listNavigableAgents(
|
|
158
|
-
agents: readonly NavigableSubagent[],
|
|
159
|
-
evicted: readonly EvictedSubagent[],
|
|
160
|
-
registry: AgentConfigLookup,
|
|
161
|
-
): NavigationEntry[] {
|
|
162
|
-
const live = agents
|
|
163
|
-
.filter((record) => record.isSessionReady())
|
|
164
|
-
.map((record): NavigationEntry => ({ kind: "live", record, label: buildLabel(record, registry) }));
|
|
165
|
-
const liveIds = new Set(agents.map((record) => record.id));
|
|
166
|
-
const evictedEntries = evicted
|
|
167
|
-
.filter((d) => !liveIds.has(d.id))
|
|
168
|
-
.map((d): NavigationEntry => ({ kind: "evicted", outputFile: d.outputFile, label: buildLabel(d, registry, true) }));
|
|
169
|
-
return [...live, ...evictedEntries];
|
|
170
|
-
}
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
Dedup is keyed by `id` (both shapes carry it) rather than `outputFile`: a record leaves `listAgents()` at the same instant its descriptor is captured, so the sets cannot overlap; the filter is defensive.
|
|
174
|
-
Order is live-then-evicted, which is already recency-ordered (evicted agents completed > 10 min ago).
|
|
175
|
-
|
|
176
|
-
Handler consumer sketch (Tell-Don't-Ask: the entry tells the handler its kind; the handler asks the right source constructor — no reach-through into the record's internals):
|
|
177
|
-
|
|
178
|
-
```typescript
|
|
179
|
-
const entry = entries.find((candidate) => candidate.label === choice);
|
|
180
|
-
if (!entry) return;
|
|
181
|
-
let source: TranscriptSource;
|
|
182
|
-
try {
|
|
183
|
-
source = entry.kind === "live" ? liveSource(entry.record) : fileSnapshotSource(entry.outputFile, readFile);
|
|
184
|
-
} catch {
|
|
185
|
-
ui.notify("Could not read the session transcript file.", "error");
|
|
186
|
-
return;
|
|
187
|
-
}
|
|
188
|
-
// …unchanged: getMarkdownTheme(), ui.custom(new TranscriptOverlay({ source, … }))
|
|
189
|
-
```
|
|
190
|
-
|
|
191
|
-
### Manager retention sketch
|
|
192
|
-
|
|
193
|
-
```typescript
|
|
194
|
-
private readonly evicted = new Map<string, EvictedSubagent>();
|
|
195
|
-
|
|
196
|
-
private cleanup() {
|
|
197
|
-
const cutoff = Date.now() - 10 * 60_000;
|
|
198
|
-
for (const [id, record] of this.agents) {
|
|
199
|
-
if (record.status === "running" || record.status === "queued") continue;
|
|
200
|
-
if ((record.completedAt ?? 0) >= cutoff) continue;
|
|
201
|
-
if (record.outputFile) this.evicted.set(id, toEvictedSubagent(record));
|
|
202
|
-
this.removeRecord(id, record);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
listEvicted(): EvictedSubagent[] {
|
|
207
|
-
return [...this.evicted.values()].sort((a, b) => b.startedAt - a.startedAt);
|
|
208
|
-
}
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
`clearCompleted()` and `dispose()` also `this.evicted.clear()` — descriptors belong to the session that evicted them, so a new session starts empty.
|
|
212
|
-
`toEvictedSubagent(record)` copies the eight fields; it is a private helper (or a free function in the module) and reads `record.outputFile` only after the `if (record.outputFile)` guard, so the `outputFile` field is always a defined `string`.
|
|
213
|
-
|
|
214
|
-
### Edge cases
|
|
215
|
-
|
|
216
|
-
- **`outputFile` undefined at eviction** (headless / never-persisted session) → no descriptor captured; nothing to navigate.
|
|
217
|
-
- **File deleted/unreadable after eviction** → `fileSnapshotSource`'s eager read throws; the handler's `try/catch` notifies and returns without opening the overlay.
|
|
218
|
-
- **Empty or header-only JSONL** → `buildSessionContext` returns `messages: []`; the overlay renders an empty transcript (no crash).
|
|
219
|
-
- **No navigable sessions at all** (no live, no evicted) → existing `ui.notify("No subagent sessions to view.", "info")` path, unchanged.
|
|
220
|
-
|
|
221
|
-
## Module-Level Changes
|
|
222
|
-
|
|
223
|
-
- `src/lifecycle/subagent-manager.ts` — add the `EvictedSubagent` interface (exported), the `evicted` map field, descriptor capture in `cleanup()`, `evicted.clear()` in `clearCompleted()` and `dispose()`, the `listEvicted()` method, and the `toEvictedSubagent` helper.
|
|
224
|
-
Import `SubagentType` (already in `#src/types`) and `SubagentStatus` (`#src/lifecycle/subagent-state`) for the interface.
|
|
225
|
-
- `src/ui/session-navigation.ts` — add `fileSnapshotSource`; import `EvictedSubagent` (type), and the SDK runtime `parseSessionEntries` / `buildSessionContext` plus the `SessionEntry` type; change `NavigationEntry` to the discriminated union; broaden `listNavigableAgents` to `(agents, evicted, registry)`; narrow `buildLabel` to `LabelFields` + `evicted` flag.
|
|
226
|
-
- `src/ui/session-navigator.ts` — `SessionNavigatorParams` gains `evicted: readonly EvictedSubagent[]` and `readFile: (path: string) => string`; the handler selects the source by `entry.kind` inside a `try/catch`.
|
|
227
|
-
No change to `TranscriptOverlay`, `buildTranscriptComponents`, `addMessageComponents`, or `addUserComponents`.
|
|
228
|
-
- `src/index.ts` — add `import { readFileSync } from "node:fs"`; the `subagent-sessions` handler passes `evicted: manager.listEvicted()` and `readFile: (path) => readFileSync(path, "utf8")`.
|
|
229
|
-
- `docs/architecture/architecture.md` — mark Step 4b ([#463]) **Landed** with a note recording the descriptor-vs-scan decision and that ADR-0004 Addendum 2's dual-source design is now realized; flip the `S4b` node label in the step-dependency Mermaid diagram to ✅.
|
|
230
|
-
- `docs/decisions/0004-reconsider-ui-direction.md` — optional one-line note under Addendum 2 that Step 4b chose manager-retained descriptors over a directory scan (the addendum's "evicted/untracked" wording said file snapshot for *rendering*, which still holds; only the *candidate-set* mechanism is being pinned down).
|
|
231
|
-
|
|
232
|
-
No SKILL update: the package SKILL's UI section references no session-navigation internals and the file/domain counts are unchanged (no new module — `fileSnapshotSource` joins the existing `session-navigation.ts`).
|
|
233
|
-
The architecture LOC/complexity tables are explicit end-of-phase snapshots, not per-file inventories, so a ~30-LOC addition does not update them.
|
|
234
|
-
|
|
235
|
-
## Test Impact Analysis
|
|
236
|
-
|
|
237
|
-
1. **New unit tests enabled by this change:**
|
|
238
|
-
- `fileSnapshotSource` (pure, in `session-navigation.test.ts`): fixture JSONL via a fake `readFile` → assert `getMessages()` equals the parsed messages, the `SessionHeader` is dropped, and `subscribe()` / `streaming()` / `getToolDefinition()` return `undefined`.
|
|
239
|
-
- `listNavigableAgents` with evicted descriptors: evicted entries appear with the `· evicted (snapshot)` marker; an evicted descriptor whose id matches a live record is deduped out.
|
|
240
|
-
- `SubagentManager.listEvicted()` (in `subagent-manager.test.ts`): after advancing fake timers past the cutoff, a completed agent with an `outputFile` produces a descriptor; one without an `outputFile` does not; `clearCompleted()` empties the set.
|
|
241
|
-
- `SessionNavigatorHandler` evicted path (in `session-navigator.test.ts`): picking an evicted label opens an overlay sourced from `readFile`; a throwing `readFile` notifies and skips the overlay.
|
|
242
|
-
2. **Tests that become redundant:** none — the live-source tests still exercise the tracked-agent branch.
|
|
243
|
-
3. **Tests that must stay as-is:** the existing `liveSource`, `listNavigableAgents` (live-only), `TranscriptOverlay`, and live-source handler tests — they pin the tracked-agent behavior this change must not regress.
|
|
244
|
-
|
|
245
|
-
## Invariants at risk
|
|
246
|
-
|
|
247
|
-
Step 4 (#445) and Step 4a (#462) established invariants on the navigation surface; this step must preserve them:
|
|
248
|
-
|
|
249
|
-
- **Handler is a reactive consumer with no inbound call into the core** (Invariant #423/#445).
|
|
250
|
-
Pinned by the existing `session-navigator.test.ts` assertion `expect(record.getToolDefinition).not.toHaveBeenCalled()` in the live-source handler test.
|
|
251
|
-
The evicted path reads only `entry.outputFile` (a string) + the injected `readFile`; it makes no inbound call into a record.
|
|
252
|
-
- **Read-only, non-interactive overlay** (#445/#446).
|
|
253
|
-
The file source returns `subscribe: () => undefined` and `streaming: () => undefined`; the overlay's existing read-only behavior is unchanged.
|
|
254
|
-
Pinned by the existing overlay tests.
|
|
255
|
-
- **Renderer parity via Pi per-entry components** (#462).
|
|
256
|
-
The renderer is untouched; both sources still yield `SessionMessage[]` into the same `buildTranscriptComponents`.
|
|
257
|
-
Pinned by the existing `TranscriptOverlay` render tests.
|
|
258
|
-
|
|
259
|
-
No new test is needed for these — existing tests already pin them; the integration step keeps them green.
|
|
260
|
-
|
|
261
|
-
## TDD Order
|
|
262
|
-
|
|
263
|
-
1. **`fileSnapshotSource` (pure, additive).**
|
|
264
|
-
Surface: `test/ui/session-navigation.test.ts`.
|
|
265
|
-
Red: a `describe("fileSnapshotSource")` with a fake `readFile` returning fixture JSONL (a `session` header line + a couple of `message` entries) — assert messages parsed, header dropped, and the three static-source methods return `undefined`.
|
|
266
|
-
Green: implement `fileSnapshotSource` and the SDK runtime imports.
|
|
267
|
-
Commit: `test(pi-subagents): cover file-snapshot transcript source` then `feat(pi-subagents): add file-snapshot transcript source` (or a single `feat` commit with test + impl).
|
|
268
|
-
2. **Manager evicted-descriptor retention (additive).**
|
|
269
|
-
Surface: `test/lifecycle/subagent-manager.test.ts`.
|
|
270
|
-
Red: with fake timers, advance past the 10-minute cutoff and assert `listEvicted()` returns a descriptor for a completed agent with an `outputFile`, none for one without, and an empty set after `clearCompleted()`.
|
|
271
|
-
Green: add `EvictedSubagent`, the `evicted` map, capture in `cleanup()`, `clear()` in `clearCompleted()`/`dispose()`, `listEvicted()`, and `toEvictedSubagent`.
|
|
272
|
-
Run `pnpm run check` after this commit (new exported interface).
|
|
273
|
-
Commit: `feat(pi-subagents): retain evicted-agent descriptors in the manager`.
|
|
274
|
-
3. **Integrate: broaden the candidate set and dual-source the handler (breaking the `NavigationEntry` shape).**
|
|
275
|
-
This step changes `NavigationEntry` and `listNavigableAgents`'s signature, so the handler, `index.ts` call site, and both UI test files break and are updated together.
|
|
276
|
-
Surfaces: `src/ui/session-navigation.ts`, `src/ui/session-navigator.ts`, `src/index.ts`, `test/ui/session-navigation.test.ts`, `test/ui/session-navigator.test.ts`.
|
|
277
|
-
Red: update `listNavigableAgents` tests to the union shape and add the evicted-entry (marker + dedup) cases; add the handler evicted-path tests (file source + throwing-`readFile` notify).
|
|
278
|
-
Green: change `NavigationEntry` to the union, broaden `listNavigableAgents`, narrow `buildLabel`, add `evicted` + `readFile` to `SessionNavigatorParams`, switch the handler source by kind inside `try/catch`, and wire `manager.listEvicted()` + `readFileSync` in `index.ts`.
|
|
279
|
-
Run `pnpm run check` (shared interface change with a single non-test call site).
|
|
280
|
-
Commit: `feat(pi-subagents): source evicted-agent transcripts from disk in /subagent-sessions`.
|
|
281
|
-
4. **Docs.**
|
|
282
|
-
Update `docs/architecture/architecture.md` Step 4b to Landed (descriptor decision + dual-source realized) and the `S4b` diagram node to ✅; add the optional Addendum 2 note in `docs/decisions/0004-reconsider-ui-direction.md`.
|
|
283
|
-
Commit: `docs(pi-subagents): mark Phase 19 Step 4b landed (#463)`.
|
|
284
|
-
|
|
285
|
-
## Risks and Mitigations
|
|
286
|
-
|
|
287
|
-
- **Transient dead code between steps 1–2 and 3.**
|
|
288
|
-
`fileSnapshotSource` and `listEvicted()` have no caller until step 3.
|
|
289
|
-
CI / pre-completion `fallow dead-code` runs on the pushed tip, which is clean once step 3 lands; all steps ship in one push.
|
|
290
|
-
Mitigation: do not run `/ship-issue` before step 3 is committed.
|
|
291
|
-
- **`buildSessionContext` / `parseSessionEntries` behavior on malformed JSONL.**
|
|
292
|
-
Mitigation: the handler `try/catch` covers a throwing read/parse; a TDD fixture exercises a valid header-plus-messages file, and an empty-messages case confirms a graceful empty render.
|
|
293
|
-
- **Memory: the `evicted` map grows over a long session.**
|
|
294
|
-
Each descriptor is eight scalar fields (no messages), bounded by subagents spawned; the heavy session objects are still disposed.
|
|
295
|
-
`clearCompleted()` resets it per session.
|
|
296
|
-
- **Divergence from the issue's "enumerate persisted JSONL files" wording.**
|
|
297
|
-
Documented and operator-confirmed (descriptors over scan, for rich labels and bounded IO); the architecture/ADR notes record the rationale so the file-scan option is not silently lost.
|
|
298
|
-
|
|
299
|
-
## Open Questions
|
|
300
|
-
|
|
301
|
-
None — the candidate-set strategy and the evicted-entry marker were resolved with the operator during planning.
|
|
302
|
-
|
|
303
|
-
[#445]: https://github.com/gotgenes/pi-packages/issues/445
|
|
304
|
-
[#446]: https://github.com/gotgenes/pi-packages/issues/446
|
|
305
|
-
[#462]: https://github.com/gotgenes/pi-packages/issues/462
|
|
306
|
-
[#463]: https://github.com/gotgenes/pi-packages/issues/463
|
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
issue: 470
|
|
3
|
-
issue_title: "pi-subagents: README still documents the removed /agents command and omits /subagents:settings and /subagents:sessions"
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Refresh pi-subagents README to the post-Phase-19 command surface
|
|
7
|
-
|
|
8
|
-
## Release Recommendation
|
|
9
|
-
|
|
10
|
-
**Release:** ship independently
|
|
11
|
-
|
|
12
|
-
This is a standalone documentation fix.
|
|
13
|
-
It is not a member of any architecture-roadmap step or `Release: batch` — the `dissolve-agents` batch (Steps 5–6, [#442]/[#441]) already shipped as `pi-subagents-v18.0.0`, and this issue corrects README staleness that batch left behind.
|
|
14
|
-
There is no code change and nothing to batch with, so it ships on its own.
|
|
15
|
-
|
|
16
|
-
## Problem Statement
|
|
17
|
-
|
|
18
|
-
The Phase 19 terminal cut ([#442], [#441]) removed the `/agents` command, the bespoke conversation viewer, and the agent creation wizard / config editor, replacing them with `/subagents:settings`, `/subagents:sessions`, and the always-on background widget.
|
|
19
|
-
`packages/pi-subagents/README.md` was never updated and still documents the deleted surface as if it were live.
|
|
20
|
-
This shipped to npm with `pi-subagents-v18.0.0`, so the published package's README actively misleads users toward commands and menu actions that no longer exist.
|
|
21
|
-
|
|
22
|
-
## Goals
|
|
23
|
-
|
|
24
|
-
- Replace every reference to the removed `/agents` interactive menu with the current focused commands `/subagents:settings` and `/subagents:sessions`.
|
|
25
|
-
- Remove the **Conversation viewer** feature bullet — the live-scrolling overlay was deleted; transcript viewing is now served by `/subagents:sessions`.
|
|
26
|
-
- Remove the **eject** customization story from the Default Agent Types section — the eject/disable/enable/edit/delete menu is gone; customization is now "override by creating `.pi/agents/<name>.md`" and disabling stays `enabled: false` frontmatter (per ADR-0004 Decision C).
|
|
27
|
-
- Rewrite the **Commands** section to document the two real commands instead of the deleted `/agents` menu tree.
|
|
28
|
-
- Correct the **Persistent Settings** section's `/agents` → Settings references to `/subagents:settings`.
|
|
29
|
-
- Correct the **Events** table's `subagents:settings_changed` description (currently "`/agents` → Settings mutation").
|
|
30
|
-
- Add a feature bullet (or amend an existing one) so the read-only session navigation surface (`/subagents:sessions`) is discoverable in the Features list, since the deleted Conversation viewer bullet was its only nearby mention.
|
|
31
|
-
|
|
32
|
-
## Non-Goals
|
|
33
|
-
|
|
34
|
-
- No source, test, or behavior changes — this is a pure documentation edit.
|
|
35
|
-
`src/`, `test/`, the architecture doc, and the ADRs are already accurate (architecture.md lines 405/559 already describe the Phase 19 outcome) and are not touched.
|
|
36
|
-
- No rewrite of accurate sections (Quick Start, UI widget render examples, Default Agent Types table, Custom Agents, Tools, Graceful Max Turns, Concurrency, Worktree Isolation, migration notes, For Extension Authors).
|
|
37
|
-
- No new screenshots or media.
|
|
38
|
-
The widget render examples in the **UI** section already describe the background-only widget accurately and stay as-is.
|
|
39
|
-
- No change to the `subagents.json` settings-file layering description (global `~/.pi/agent/subagents.json` + project `.pi/subagents.json`) — that mechanism is unchanged; only the command name that writes the project file changed.
|
|
40
|
-
|
|
41
|
-
## Background
|
|
42
|
-
|
|
43
|
-
Relevant current surface (verified against `src/index.ts`):
|
|
44
|
-
|
|
45
|
-
- `pi.registerCommand("subagents:settings", …)` → `SubagentsSettingsHandler.handle({ ui })` — interactive list to set max concurrency, default max turns, and grace turns at runtime (`src/ui/subagents-settings.ts`).
|
|
46
|
-
This is the re-homed Settings job from the old `/agents` menu; it still persists to the same `subagents.json` files.
|
|
47
|
-
- `pi.registerCommand("subagents:sessions", …)` → `SessionNavigatorHandler.handle({ … agents, evicted, registry, cwd, readFile })` — read-only transcript navigation over `manager.listAgents()` and `manager.listEvicted()`, so any subagent (live or evicted) is navigable (`src/ui/session-navigation.ts`, `session-navigator.ts`).
|
|
48
|
-
|
|
49
|
-
ADR-0004 Decision C (`docs/decisions/0004-reconsider-ui-direction.md`) is the authority for the customization story the README must now tell:
|
|
50
|
-
|
|
51
|
-
- Create-new-agent wizard → removed.
|
|
52
|
-
An operator generates a new agent `.md` by asking a Pi session directly or by writing the file in an editor.
|
|
53
|
-
- Agent-types list + config editor → removed.
|
|
54
|
-
Viewing/editing definitions is done by opening the `.md` files in an editor/IDE.
|
|
55
|
-
- The eject convenience no longer exists; overriding a default agent is done by creating `.pi/agents/<name>.md` with the same name, and disabling stays `enabled: false` frontmatter.
|
|
56
|
-
|
|
57
|
-
AGENTS.md constraint that applies: this is a single-package README change, so it lives in `packages/pi-subagents/docs/plans/`.
|
|
58
|
-
The README documents commands, not module filenames, which is exactly why the Phase 19 module-name doc-staleness check missed it (the issue's own root-cause note, Refs [#442]/[#441]).
|
|
59
|
-
|
|
60
|
-
## Design Overview
|
|
61
|
-
|
|
62
|
-
Pure prose edit, no decision model or data shapes.
|
|
63
|
-
The README is restructured section-by-section to describe the live surface.
|
|
64
|
-
The one structural choice is how to present the two commands in the **Commands** section: keep the existing two-column command table (`| Command | Description |`) with two rows, then a short subsection per command describing what it opens, instead of the old single-command-plus-menu-tree layout.
|
|
65
|
-
|
|
66
|
-
### Commands section — target shape (illustrative)
|
|
67
|
-
|
|
68
|
-
```markdown
|
|
69
|
-
## Commands
|
|
70
|
-
|
|
71
|
-
| Command | Description |
|
|
72
|
-
| --------------------- | ------------------------------------------------------ |
|
|
73
|
-
| `/subagents:settings` | Configure subagent settings (concurrency, turn limits) |
|
|
74
|
-
| `/subagents:sessions` | View a subagent's session transcript (read-only) |
|
|
75
|
-
|
|
76
|
-
### `/subagents:settings`
|
|
77
|
-
|
|
78
|
-
Interactive list to tune runtime settings — max concurrency, default max turns, and grace turns.
|
|
79
|
-
Changes persist across pi restarts (see Persistent Settings).
|
|
80
|
-
|
|
81
|
-
### `/subagents:sessions`
|
|
82
|
-
|
|
83
|
-
Pick any subagent — running or already evicted — and read its full session transcript in pi's native per-entry viewer.
|
|
84
|
-
Read-only: no steering, no session takeover (steering lives in the `steer_subagent` tool).
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
The command descriptions in the table are copied verbatim from the `registerCommand` calls in `src/index.ts` so the README matches what `/help` shows.
|
|
88
|
-
|
|
89
|
-
### Customization story — Default Agent Types section
|
|
90
|
-
|
|
91
|
-
Replace the eject sentence (line 119) with two retained mechanisms only:
|
|
92
|
-
|
|
93
|
-
```markdown
|
|
94
|
-
Default agents can be **overridden** by creating a `.md` file with the same name (e.g. `.pi/agents/general-purpose.md`), or **disabled** per-project with `enabled: false` frontmatter.
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
(The "ejected … to export them as `.md` files" clause is dropped entirely — there is no eject UI to produce the export.)
|
|
98
|
-
|
|
99
|
-
## Module-Level Changes
|
|
100
|
-
|
|
101
|
-
Single file: `packages/pi-subagents/README.md`.
|
|
102
|
-
Line numbers are against the current README (from the issue's "Affected lines"); each is a prose edit, not a code change.
|
|
103
|
-
|
|
104
|
-
1. **Line 21 — Features list, Conversation viewer bullet.**
|
|
105
|
-
Remove the `**Conversation viewer**` bullet (it describes selecting an agent in `/agents` to open a live overlay).
|
|
106
|
-
In its place, add a bullet for read-only session navigation, e.g. `**Session transcripts** — open any subagent's full session transcript (running or evicted) in a read-only viewer via /subagents:sessions`.
|
|
107
|
-
2. **Line 119 — Default Agent Types, eject sentence.**
|
|
108
|
-
Reword to drop eject; keep override + disable (see Design Overview snippet).
|
|
109
|
-
3. **Lines ~226–303 — Commands + Persistent Settings sections.**
|
|
110
|
-
- Rewrite the **Commands** section (table row `/agents` and the entire interactive-menu code block + bullet list, lines ~228–252) to document `/subagents:settings` and `/subagents:sessions` per the target shape above.
|
|
111
|
-
- In **Persistent Settings**, replace each `/agents` → Settings reference (lines 277, 281, 283, 301) with `/subagents:settings`, and "the `/agents` menu never writes here" (line 281) with "the `/subagents:settings` command never writes here", and "without ever touching the menu" (line 300) with "without ever touching the command".
|
|
112
|
-
- Line 303 failure-behavior sentence: change "the `/agents` toast" to "the `/subagents:settings` toast".
|
|
113
|
-
4. **Line 318 — Events table, `subagents:settings_changed` row.**
|
|
114
|
-
Change the "When" cell from "`/agents` → Settings mutation was applied" to "`/subagents:settings` mutation was applied".
|
|
115
|
-
|
|
116
|
-
Doc-staleness cross-checks performed (per plan-issue checklist):
|
|
117
|
-
|
|
118
|
-
- Grepped the README for every removed term (`/agents`, `eject`/`Eject`, `conversation viewer`, `wizard`, `Create new agent`, `interactive menu`, `menu`) — all occurrences are enumerated in the steps above (lines 21, 119, 228, 230, 235, 241–248, 277, 281, 283, 300, 301, 303, 318).
|
|
119
|
-
- Confirmed no other `packages/pi-subagents/docs/` file or `.pi/skills/package-pi-subagents/SKILL.md` references `/agents` as a live command needing update (architecture.md already describes the Phase 19 removal as past tense; the SKILL.md describes `/subagents:settings`/`/subagents:sessions` already).
|
|
120
|
-
- The **UI** widget-render examples and the **Default Agent Types** table are accurate post-Phase-19 and are left unchanged — verify they are not contradicted by the edits.
|
|
121
|
-
|
|
122
|
-
## Test Impact Analysis
|
|
123
|
-
|
|
124
|
-
Not applicable — documentation-only change with no test surface.
|
|
125
|
-
There is no executable behavior to cover; correctness is verified by `pnpm run lint` (rumdl markdown rules) and visual review against `src/index.ts`'s registered commands.
|
|
126
|
-
|
|
127
|
-
## Invariants at risk
|
|
128
|
-
|
|
129
|
-
None.
|
|
130
|
-
This change touches no code surface a prior phase step refactored; the Phase 19 spine invariants ([#422]–[#425]) are pinned by existing source/observer/event-contract suites and are untouched by a README edit.
|
|
131
|
-
|
|
132
|
-
## Build Order
|
|
133
|
-
|
|
134
|
-
This is a `/build-plan` (docs-only) change, executed as a single reviewable commit — no red→green cycles.
|
|
135
|
-
|
|
136
|
-
1. Edit `packages/pi-subagents/README.md` per all four Module-Level Changes steps in one pass.
|
|
137
|
-
Apply one-sentence-per-line formatting (markdown-conventions skill) to all rewritten prose.
|
|
138
|
-
Suggested commit message: `docs(pi-subagents): refresh README for /subagents:settings and /subagents:sessions (#470)`.
|
|
139
|
-
2. Verify: run `pnpm --filter @gotgenes/pi-subagents run lint` (or `pnpm run lint` at root) to confirm rumdl passes, and re-grep the README for `/agents`, `eject`, `wizard`, `conversation viewer` to confirm zero stale references remain.
|
|
140
|
-
Cross-check the two command descriptions against `src/index.ts` `registerCommand` strings.
|
|
141
|
-
|
|
142
|
-
## Risks and Mitigations
|
|
143
|
-
|
|
144
|
-
- **Risk: missing a stale reference.**
|
|
145
|
-
Mitigation: the grep in Module-Level Changes enumerated every occurrence; the build step re-greps after editing to confirm none survive.
|
|
146
|
-
- **Risk: command description drift from the actual `/help` text.**
|
|
147
|
-
Mitigation: copy the table descriptions verbatim from the `registerCommand` calls in `src/index.ts`.
|
|
148
|
-
- **Risk: markdown lint failure on the rewritten Commands table or code fences.**
|
|
149
|
-
Mitigation: follow markdown-conventions (compact table style, fenced-block languages, sequential numbering) and run `pnpm run lint` before committing.
|
|
150
|
-
|
|
151
|
-
## Open Questions
|
|
152
|
-
|
|
153
|
-
None.
|
|
154
|
-
The issue is the operator's own, the surface is verified against source, and no follow-up work is named.
|
|
155
|
-
|
|
156
|
-
[#441]: https://github.com/gotgenes/pi-packages/issues/441
|
|
157
|
-
[#442]: https://github.com/gotgenes/pi-packages/issues/442
|
|
158
|
-
[#422]: https://github.com/gotgenes/pi-packages/issues/422
|
|
159
|
-
[#425]: https://github.com/gotgenes/pi-packages/issues/425
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
issue: 48
|
|
3
|
-
issue_title: "feat: implement and publish SubagentsService at extension init"
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Retro: #48 — implement and publish SubagentsService at extension init
|
|
7
|
-
|
|
8
|
-
## Final Retrospective (2026-05-17T15:30:00Z)
|
|
9
|
-
|
|
10
|
-
### Session summary
|
|
11
|
-
|
|
12
|
-
Planned and implemented a typed `SubagentsService` interface with `Symbol.for()` accessor functions, an adapter wrapping `AgentManager` with model resolution and record serialization, and wired it into the extension init.
|
|
13
|
-
Released as `@gotgenes/pi-subagents@4.0.0` (breaking: old untyped global removed).
|
|
14
|
-
The plan was revised mid-session to align naming with `pi-permission-system`'s established conventions after the user flagged the discrepancy.
|
|
15
|
-
|
|
16
|
-
### Observations
|
|
17
|
-
|
|
18
|
-
#### What went well
|
|
19
|
-
|
|
20
|
-
- TDD execution was clean: 8 steps, zero rework, all 33 new tests green on first pass.
|
|
21
|
-
- The adapter design was well-scoped — `index.ts` wiring was +16/−12 lines, and the narrow `AgentManagerLike` interface made test mocks trivial.
|
|
22
|
-
- The allowlist serialization pattern (`toSubagentRecord`) prevents future leaks of non-serializable fields by default.
|
|
23
|
-
|
|
24
|
-
#### What caused friction (agent side)
|
|
25
|
-
|
|
26
|
-
- `missing-context` — The initial plan adopted the issue body's naming verbatim (`SubagentsAPI`, `api.ts`, `pi:service:subagents`, `(globalThis as any)`) without checking `pi-permission-system` for the established convention (`SubagentsService`, `service.ts`, `@gotgenes/<pkg>:service`, `Record<symbol, unknown>`).
|
|
27
|
-
The user had to explicitly ask "Does this structure follow the pattern set forth by pi-permission-system?"
|
|
28
|
-
Impact: full plan rewrite (replaced entire file), issue title update, issue body update — ~15 minutes of rework across 3 user turns.
|
|
29
|
-
This was **user-caught**.
|
|
30
|
-
|
|
31
|
-
- `missing-context` — Same pattern as the #49 retro: following the issue spec literally without checking the codebase.
|
|
32
|
-
The architecture doc also used the stale naming, reinforcing the wrong choice.
|
|
33
|
-
Root cause: the "Gather context" step in `/plan-issue` didn't include a cross-package convention check.
|
|
34
|
-
|
|
35
|
-
#### What caused friction (user side)
|
|
36
|
-
|
|
37
|
-
- The user had to perform mechanical oversight ("Does this follow the pi-permission-system pattern?") that the planner should have caught independently.
|
|
38
|
-
If the `/plan-issue` prompt included a step to grep sibling packages for established API patterns, this would have been a design decision surfaced during planning rather than a correction after the fact.
|
|
39
|
-
|
|
40
|
-
### Changes made
|
|
41
|
-
|
|
42
|
-
1. Created `packages/pi-subagents/docs/retro/0048-implement-subagents-api.md` (this file).
|
|
43
|
-
2. Updated `.pi/skills/package-pi-subagents/SKILL.md` — changed `SubagentsAPI` → `SubagentsService` in Implementation Priorities; added `service.ts` and `service-adapter.ts` to module dependency graph and descriptions.
|
|
44
|
-
3. Updated `.pi/prompts/plan-issue.md` — added step 7 to Gather context: check sibling packages for established API patterns before adopting issue body naming.
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
issue: 49
|
|
3
|
-
issue_title: "feat: remove group-join, output-file, and ad-hoc RPC"
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Retro: #49 — remove group-join, output-file, and ad-hoc RPC
|
|
7
|
-
|
|
8
|
-
## Final Retrospective (2026-05-17T15:15:00Z)
|
|
9
|
-
|
|
10
|
-
### Session summary
|
|
11
|
-
|
|
12
|
-
Planned and implemented the removal of group-join and ad-hoc RPC from `pi-subagents`, releasing v3.0.0.
|
|
13
|
-
The original scope included `output-file.ts` removal, but the user intervened to retain it for post-hoc debugging value.
|
|
14
|
-
A new issue (#61) was filed to port the output-file format to Pi's official JSONL session schema.
|
|
15
|
-
|
|
16
|
-
### Observations
|
|
17
|
-
|
|
18
|
-
#### What went well
|
|
19
|
-
|
|
20
|
-
- User intervention produced a materially better outcome — retaining debugging transcripts and identifying a format conformance gap that became #61.
|
|
21
|
-
- TDD execution was clean: 6 steps, zero rework, all tests green on first pass after each step.
|
|
22
|
-
- The `feat!:` → release-please → v3.0.0 pipeline worked smoothly end-to-end.
|
|
23
|
-
|
|
24
|
-
#### What caused friction (agent side)
|
|
25
|
-
|
|
26
|
-
- `missing-context` — Included `output-file.ts` removal in the initial plan without questioning its debugging value, despite AGENTS.md's rule "Ask before removing functionality or changing defaults."
|
|
27
|
-
The issue body explicitly listed it for removal so I followed the spec literally.
|
|
28
|
-
Impact: required plan revision (amend commit), scope-narrowing comment on issue, and filing #61 — roughly 10 minutes of rework, but produced a better design.
|
|
29
|
-
|
|
30
|
-
- `missing-context` — When asked whether output-file adheres to Pi's session format, searched the web (`web_search` for "Claude Code session JSONL format") instead of checking the local `~/development/pi/pi` monorepo.
|
|
31
|
-
The user had to explicitly say "~/development/pi/pi has the code for Pi's JSONL format."
|
|
32
|
-
Impact: one extra round-trip and less authoritative initial answer (Claude Code's format vs Pi's `SessionManager`).
|
|
33
|
-
Self-identified after user redirect.
|
|
34
|
-
|
|
35
|
-
- `instruction-violation` (self-identified) — Shell-escaped the `gh issue comment` body incorrectly; backtick-wrapped `src/output-file.ts` was interpreted by bash.
|
|
36
|
-
Caught immediately via `gh issue view` and fixed with `--edit-last`.
|
|
37
|
-
Impact: trivial — one extra command.
|
|
38
|
-
|
|
39
|
-
#### What caused friction (user side)
|
|
40
|
-
|
|
41
|
-
- The issue body listed output-file for removal without noting its debugging value.
|
|
42
|
-
The user's "How confident are we in getting rid of the logging system?"
|
|
43
|
-
intervention was the correction.
|
|
44
|
-
If the issue had marked output-file removal as "tentative pending debugging value assessment," the plan would have surfaced it as a design decision from the start.
|
|
45
|
-
Minor — the discussion was quick and productive.
|
|
46
|
-
|
|
47
|
-
### Changes made
|
|
48
|
-
|
|
49
|
-
1. Created `packages/pi-subagents/docs/retro/0049-remove-group-join-output-file-rpc.md` (this file).
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
issue: 51
|
|
3
|
-
issue_title: "docs: update ADR 0001 to reflect hard-fork decision"
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Retro: #51 — docs: update ADR-0001 to reflect hard-fork decision
|
|
7
|
-
|
|
8
|
-
## Final Retrospective (2026-05-16)
|
|
9
|
-
|
|
10
|
-
### Session summary
|
|
11
|
-
|
|
12
|
-
Updated [ADR-0001] to reflect the hard-fork decision documented in `docs/architecture/architecture.md`.
|
|
13
|
-
The change was planned, implemented, shipped, and released as `pi-subagents-v1.0.2` in a single clean pass with no rework.
|
|
14
|
-
|
|
15
|
-
### Observations
|
|
16
|
-
|
|
17
|
-
#### What went well
|
|
18
|
-
|
|
19
|
-
- The entire plan→build→ship pipeline completed with zero corrections, zero CI failures, and zero user interventions.
|
|
20
|
-
- Parallel context gathering (issue body, `AGENTS.md`, ADR file, architecture doc, two skill files) in one tool call made the planning phase efficient.
|
|
21
|
-
- The 4-edit approach (`Edit` with a single call containing four `edits[]` entries) was well-matched to the task — each edit was small, unique, and non-overlapping.
|
|
22
|
-
|
|
23
|
-
#### What caused friction (agent side)
|
|
24
|
-
|
|
25
|
-
- No friction observed.
|
|
26
|
-
The task was unambiguous and the tooling well-suited.
|
|
27
|
-
|
|
28
|
-
#### What caused friction (user side)
|
|
29
|
-
|
|
30
|
-
- No friction observed.
|
|
31
|
-
The session required no user input beyond invoking the three slash commands.
|
|
32
|
-
|
|
33
|
-
### Follow-ups identified
|
|
34
|
-
|
|
35
|
-
- The `package-pi-subagents` skill (`.pi/skills/package-pi-subagents/SKILL.md`) still frames the fork as "a friendly fork… carrying a small number of patches" with priorities like "stays as close to upstream as possible."
|
|
36
|
-
This framing is now stale given the hard-fork commitment.
|
|
37
|
-
A separate issue should update the skill to reflect the architecture document's posture.
|
|
38
|
-
|
|
39
|
-
[ADR-0001]: ../decisions/0001-deferred-patches.md
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
issue: 53
|
|
3
|
-
issue_title: "refactor: extract model resolution from Agent.execute"
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Retro: #53 — extract model resolution from Agent.execute
|
|
7
|
-
|
|
8
|
-
## Final Retrospective (2026-05-17T21:00:00Z)
|
|
9
|
-
|
|
10
|
-
### Session summary
|
|
11
|
-
|
|
12
|
-
Planned and executed the extraction of inline model-resolution logic from `Agent.execute` in `index.ts` into a new `resolveInvocationModel()` function in `model-resolver.ts`.
|
|
13
|
-
Released as `pi-subagents-v4.1.0` with +10 new unit tests and no behavior change.
|
|
14
|
-
Also fixed a pre-existing `rumdl` glob-quoting bug in `package.json` discovered during the lint step.
|
|
15
|
-
|
|
16
|
-
### Observations
|
|
17
|
-
|
|
18
|
-
#### What went well
|
|
19
|
-
|
|
20
|
-
- Pre-existing lint bug surfaced and fixed: the `rumdl check '*.md' 'docs/**/*.md'` command in `package.json` used single-quoted globs that prevented shell expansion.
|
|
21
|
-
Verified as pre-existing (reproduced on prior commit via `git stash`), cleanly isolated into its own `fix:` commit.
|
|
22
|
-
This was a genuine find — the lint had been silently broken.
|
|
23
|
-
|
|
24
|
-
#### What caused friction (agent side)
|
|
25
|
-
|
|
26
|
-
- `missing-context` — In step 6 (refactoring `index.ts`), replaced the `resolveModel` import with `resolveInvocationModel` without first checking whether `resolveModel` was still used elsewhere in the file.
|
|
27
|
-
Two other call sites (`createSubagentsService` at line 386 and `getModelLabel` at line 1043) still needed it.
|
|
28
|
-
The plan explicitly listed `getModelLabel` as a non-goal that continues using `resolveModel`, so the information was available.
|
|
29
|
-
Caught immediately via `grep` after the edit and fixed in the same commit.
|
|
30
|
-
Impact: one extra edit + grep cycle, no rework.
|
|
31
|
-
|
|
32
|
-
- `missing-context` — The plan's type definitions specified `model: unknown` for `ModelResolutionResult`, but downstream code in `index.ts` accesses `.id` and `.name` on the model and passes it where `Model<any>` is expected.
|
|
33
|
-
The plan's risk section flagged this ("reducing but not eliminating the `any`"), yet the implementation went with `unknown` first, requiring a correction after `pnpm run check` failed with 4 type errors.
|
|
34
|
-
Changed to `model: any` to match the existing `resolveModel` return type.
|
|
35
|
-
Impact: one extra edit cycle within the same commit, no rework.
|
|
36
|
-
|
|
37
|
-
#### What caused friction (user side)
|
|
38
|
-
|
|
39
|
-
- None observed.
|
|
40
|
-
The issue was well-scoped with clear acceptance criteria, making planning and execution straightforward.
|