Files
my-pi/pi-permission-system/docs/retro/0529-extract-subagent-detection.md

90 lines
8.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
issue: 529
issue_title: "pi-permission-system: extract a SubagentDetection collaborator; seed src/authority/"
---
# Retro: #529 — pi-permission-system: extract a SubagentDetection collaborator; seed src/authority/
## Stage: Planning (2026-07-06T00:00:00Z)
### Session summary
Produced `docs/plans/0529-extract-subagent-detection.md`: a 7-step TDD plan that moves `subagent-context.ts` into `src/authority/`, adds a `SubagentDetection` class (constructed once in `index.ts`), and rewires four consumers onto two ISP seams (`SubagentDetector`, `RegisteredChildDetector`).
Release recommendation: ship independently (roadmap `Release: independent`; all-`refactor:` commits auto-batch into the next release).
### Observations
- Two design forks were surfaced via `ask_user` and resolved by the operator: **(1) Complete scope** — `SubagentDetection` also owns `isRegisteredChild(ctx)` and `service-lifecycle.ts` is rewired onto it (a fourth consumer beyond the issue's three), so all subagent-detection predicates get one owner; **(2) Delegate** — the pure functions `isSubagentExecutionContext` / `isRegisteredSubagentChild` stay exported and the class delegates, preserving the 372-LOC `subagent-context.test.ts` intact (it moves to `test/authority/` with only an import-path change).
- `PermissionForwarder` keeps its `registry` dep — it uses the registry directly for `resolvePermissionForwardingTargetSessionId` (registry-as-data), separate from detection.
Only `subagentSessionsDir` and `platform` drop from `PermissionForwarderDeps`.
- The rewire obsoletes the last `vi.mock` module mock in `test/forwarding-manager.test.ts` (the reason that file was left off the #528 forwarding harness); it gets a one-field fake detector instead but stays off the harness per that plan's Non-Goals.
- Per-ask re-evaluation inside `PermissionForwarder` (two `isSubagent` calls per forwarded ask) is deliberately **not** collapsed — the once-per-session selection is Phase 9's Authorizer job.
- Docs inventory: `architecture.md` needs the Step 5 ✅ (heading + Mermaid `S5` + metrics row), a `Landed:` bullet documenting the scope widening, the line-424 path fix, and an `authority/` subtree in the module-layout tree; SKILL.md, README, and `docs/subagent-integration.md` were checked and need no changes (they reference the still-exported function / module leaf name only).
- No follow-up issues filed — Step 6 (#530) already exists and consumes this step's output.
## Stage: Implementation — TDD (2026-07-06T21:54:00Z)
### Session summary
Executed all 7 TDD steps as planned: mechanical `git mv` of `subagent-context` into `src/authority/`, added the `SubagentDetection` class (two ISP seams), rewired the four consumers (`PromptingGateway`, `ForwardingManager`, `PermissionForwarder`, `PermissionServiceLifecycle`) onto the collaborator, and marked Phase 8 Step 5 complete in `architecture.md`.
Test count went from 2293 → 2300 (+7 from the new `subagent-detection.test.ts`); `check`, root `lint`, and `fallow dead-code` all green.
Pre-completion reviewer returned PASS.
### Observations
- One deviation from the plan's Module-Level Changes: `test/permission-forwarder.test.ts` needed **no** edits — its only non-UI case is the deny path (`isSubagent` must be `false`), which the default `isSubagent → false` fixture in `makeForwarderDeps` satisfies; no test exercised the forwarded (`isSubagent` true) path directly (that round-trip lives in `composition-root.test.ts`).
Only `test/helpers/forwarding-fixtures.ts` changed for the forwarder rewire.
- `pi-autoformat` reordered imports on several `src/` files after the step-1 move, so a few `Edit` `oldText` blocks had to be re-anchored against the reflowed import order (re-read before editing).
- The per-consumer rewires (steps 36) were cleanly independent thanks to the delegate approach: the pure functions stayed live, so each consumer flipped to the seam one commit at a time with `index.ts` coexisting old + new wiring.
- `subagentRegistry` remains in `index.ts` for `subscribeSubagentLifecycle` and `PermissionForwarderDeps.registry` (target resolution) — confirmed still used, not dead after the service-lifecycle rewire.
- Reviewer noted the unrelated in-range commit `72c15808` (pluggable escalation seam note) from a prior branch also touches `architecture.md`; it is valid and separate, not part of #529's TDD order.
- Pre-completion reviewer: PASS — ready for `/ship-issue`.
## Stage: Final Retrospective (2026-07-07T14:01:59Z)
### Session summary
Shipped issue #529 across planning, TDD, and ship stages in one trunk session: a Phase 8 Step 5 refactor extracting a `SubagentDetection` collaborator and seeding `src/authority/`, landing 7 `refactor:`/`docs:` commits with no behavior change.
All three stages ran cleanly — two design forks resolved via `ask_user` at planning time drove the plan and never got second-guessed; the 7 TDD steps went red→green→commit with incremental verification; ship pushed green (CI success), closed the issue, and correctly cut no release (all-hidden changelog types auto-batch).
### Observations
#### What went well
- **Front-loading design forks paid off.**
Both genuine forks (scope: also own `isRegisteredChild` + rewire `service-lifecycle.ts`; delegate vs. absorb the pure functions) were surfaced via one `ask_user` call at planning time, before any plan prose was written.
The operator's answers became the plan's Goals and drove all 6 code steps with zero implementation-time rework or second-guessing.
- **Delegate + coexist made per-consumer commits cleanly independent.**
Because the pure functions stayed exported and `SubagentDetection` delegates to them, each of the four consumers flipped to the seam in its own commit (steps 36), with `index.ts` holding old + new wiring side by side between steps.
No lift-and-shift transitional wrapper was needed, and the type checker stayed green at every commit — a clean execution of the tidy-first / incremental-migration pattern for a refactor.
- **Verification ran incrementally, not just at the end.**
Each TDD step ran its affected test file plus `pnpm run check` before committing; the full suite + root `lint` + `fallow dead-code` ran once at the end.
No end-of-session surprise — the feedback loop caught nothing late because it ran early.
#### What caused friction (agent side)
- `other` (tooling interaction) — `pi-autoformat` re-sorted each file's whole import block when step 1 added a new import path, so `Edit` `oldText` blocks that spanned import lines in later steps (3, 4, 6) failed to match against the stale order I carried from planning-time reads.
Impact: 3 failed `Edit` batches, each recovered with one re-read + retry (~2 extra tool calls apiece); no rework or bad commits.
The existing `AGENTS.md` guidance covers re-reading a region "you just edited," but here the volatile import block was re-sorted by autoformat in an *earlier* step, so the "just edited" trigger did not fire in my head.
#### What caused friction (user side)
- None.
The operator's only involvement was answering the two planning-time `ask_user` forks (exactly the right strategic input) and running the three slash commands; no corrections or redirects were needed.
- Minor, not friction: a prior-branch commit (`72c15808`, the pluggable-escalation-seam architecture note) landed on `main` and rode along in the #529 push, requiring a one-line "is this mine?"
check during the ship close-comment range scan — resolved without rework.
### Diagnostic details
- **Feedback-loop gap analysis** — no gap: `pnpm run check` + the affected test file ran after every TDD step; full suite + `lint` + `fallow` ran at the end.
This is the intended incremental pattern, not end-loaded verification.
- **Escalation-delay tracking** — no rabbit-holes; the 3 `Edit`-match failures each resolved in a single re-read + retry (2 tool calls), well under the 5-call flag threshold.
- **Model-performance correlation** — one subagent dispatch (`pre-completion-reviewer`, 41 tool uses / ~189 s) on judgment-heavy review work, an appropriate match; it ran on its configured model with no override.
- **Unused-tool detection** — none: planning exploration used `colgrep`/`grep`/`Read` appropriately, and no friction point would have benefited from an un-dispatched subagent or tool.
### Changes made
1. Added this Final Retrospective stage entry to `packages/pi-permission-system/docs/retro/0529-extract-subagent-detection.md`.
2. Considered a one-sentence `AGENTS.md` addition (autoformat re-sorts a file's whole import block when any import changes, so a later-step `Edit` spanning imports can fail against a stale read); operator chose to **skip** it — the existing "re-read a region you just edited" guidance is close enough, and the friction cost only one re-read + retry per occurrence.
No `AGENTS.md` change landed.