Files
my-pi/pi-permission-system/docs/retro/0338-collapse-index-closure-bags.md
T

99 lines
9.3 KiB
Markdown
Raw 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: 338
issue_title: "Collapse the index.ts closure bags into object references"
---
# Retro: #338 — Collapse the `index.ts` closure bags into object references
## Stage: Planning (2026-06-06T00:00:00Z)
### Session summary
Produced the implementation plan for Phase 4 Step 5 (Track B): collapsing the `index.ts` adapter closures into direct collaborator references now that Steps 24 made config a store, the logger an injectable object, and `PermissionManager` / `SessionRules` single shared instances.
The plan reshapes the deps interfaces on `ConfigStore`, `PermissionForwarder`, `PermissionPrompter`, the RPC handlers, the command controller, and `PermissionSession`, unifying all logging on the single `SessionLogger` object via new narrow `ReviewLogger` / `DebugReviewLogger` seams.
Seven commit cycles (six `refactor:` consumer migrations + one `docs:` metric update), each folding the consumer interface change, its test updates, and the matching `index.ts` wiring into one commit.
### Observations
- Two design forks were surfaced via `ask_user`.
Decision 1: the logger's `getConfig` and `notify` forward-reference closures stay as idiomatic forward-reference closures (the pi-subagents pattern) — no setter methods, objects instantiated complete.
Decision 2: include `ConfigStore` and `PermissionForwarder` in the deps-shrinking scope (the issue's step-2 list omitted them, but their closures must collapse to hit the target).
- The roadmap's "≤ 8" target for `index.ts` is not reachable under the no-setter direction: the two logger cycle closures are a permanent idiomatic floor.
Realistic budget after this step is 11 (6 `pi.on` + 2 `toolRegistry` + 2 logger cycle + 1 transitional `canRequestPermissionConfirmation`), dropping to 10 after Step 6 ([#339]).
The plan updates the architecture metric to 20 → 11 with a budget breakdown rather than leaving the optimistic ≤ 8.
- `canRequestPermissionConfirmation` is deliberately left as a closure: collapsing it would require injecting `subagentRegistry` into `PermissionSession` only to extract it again in Step 6's `PromptingGateway`.
Avoided that churn.
- Forwarder cleanup is a genuine win beyond closure removal: merging the duplicated top-level `writeReviewLog` with the io `logger` into one `logger` retires the [#316] duplication.
- Verified no import cycle (`yolo-mode` imports only `extension-config` + `types`; `config-store` does not import the forwarder) and that `ConfigStoreLogger` / `ForwardedPermissionLogger` are referenced only in historical plan/retro docs, not in `.pi/skills/`.
- Largest single cycle is the forwarder + io-logger rename (cycle 4): only 4 internal `io.ts` call sites, but ~28 `writeReviewLog` references in `permission-prompter.test.ts` make cycle 2 the heaviest test-churn step.
[#316]: https://github.com/gotgenes/pi-packages/issues/316
[#339]: https://github.com/gotgenes/pi-packages/issues/339
## Stage: Implementation — TDD (2026-06-06T21:54:00Z)
### Session summary
Executed all seven TDD cycles: six `refactor:` consumer migrations (cycles 16) plus one `docs:` metric update (cycle 7).
The suite remained at 86 test files / 1815 tests throughout (0 delta); all 1815 pass green.
All planned interface changes landed — `ConfigStoreLogger` and `ForwardedPermissionLogger` deleted; `ReviewLogger` / `DebugReviewLogger` introduced; `index.ts` closure count confirmed at 11.
### Observations
- Cycle 1 (`ConfigStore`): the batch edit for `config-store.ts` failed on the first attempt because the batch validator matched `oldText` against the original file (before any in-batch edits applied) but one `oldText` contained a context line that had already been mutated by an earlier entry in the same batch.
Re-read the exact line text via `Read` at offset, then split the batch to avoid the overlapping-context issue.
- Cycle 4 (`PermissionForwarder` + io logger): `io.ts` had 8 `ForwardedPermissionLogger` occurrences spread across function parameter signatures after the first Edit batch ran.
Used `sed -i ''` for the bulk rename rather than 8 individual `Edit` entries — faster and less error-prone for a mechanical global replace with no ambiguity.
- Cycle 5 (`config-modal`): `Ruleset` was used in test controller objects but not yet imported.
Added the import alongside the other changes in the same commit — caught by `pnpm run check` before commit.
- `test/composition-root.test.ts` listed in the plan but not modified: the existing "gate session-approval visible to RPC check" test already covers the injected-object behavior through the real factory; no new assertion was needed.
Noted as a minor deviation.
- One stray unused import (`Rule` in `permission-event-rpc.ts`) surfaced at lint time after cycle 3; fixed as a `style:` commit since it was separated from the originating commit by later commits.
- Pre-completion reviewer: **PASS** — all deterministic checks green, no structural concerns, architecture.md Mermaid diagrams valid.
## Stage: Final Retrospective (2026-06-06T22:30:00Z)
### Session summary
A single long session carried #338 from planning through shipping: explored the composition root, surfaced two design forks via `ask_user`, wrote the plan, executed seven TDD cycles (six `refactor:` + one `docs:`), passed pre-completion review, and shipped (CI green, issue closed, no release-please PR since `refactor:`/`style:`/`docs:` commits do not trigger a release).
The suite held at 86 files / 1815 tests with zero delta; `index.ts` closures dropped 20 → 11 exactly as the plan's budget table predicted.
Execution was clean — friction was confined to two minor tool-mechanics blips, no rework to committed code.
### Observations
#### What went well
- The two-question `ask_user` at planning surfaced genuine design forks (no-setter / forward-reference-closure direction; include `ConfigStore` + `PermissionForwarder` in scope) that shaped the whole plan and steered away from the wrong path of setter injection.
Novel: both answers materially changed the design rather than rubber-stamping it.
- The mid-session conceptual Q&A (Observer pattern vs. event-bus pub-sub for the logger `notify` cycle) turned a clarifying question into a committed Phase 5 roadmap note in `architecture.md` (commit `723310c0`).
Novel: a Q&A interlude becoming a durable architecture artifact rather than ephemeral chat.
- Lift-and-shift folding (consumer interface change + its test updates + the matching `index.ts` wiring, all in one commit per consumer) ran across all six refactor cycles with no type-checker deadlock and a clean Red→Green at each step.
Validated the planning rule about folding interface + tests + single call-site into one commit.
- Planning accuracy: the plan honestly revised the roadmap's optimistic "≤ 8" closure target to a realistic 11 with a budget breakdown, and the final count landed at exactly 11 (pre-completion confirmed).
#### What caused friction (agent side)
- `missing-context` — Cycle 1 (`config-store.ts`): constructed the `Edit` batch `oldText` from memory rather than reading the exact lines first; the batch was rejected ("Could not find edits[3]" — indentation/content mismatch) and atomically discarded.
Impact: ~3 extra tool calls (grep + two reads + re-apply); no rework to committed code.
- `other` (feedback-loop gap) — `pnpm run lint` ran only at the end of implementation, so the unused `Rule` import left behind when cycle 3 dropped `getSessionRules(): Rule[]` surfaced post-implementation and needed a separate `style:` commit (`939af088`).
Impact: one extra commit; `pnpm run check` (tsc) ran incrementally and passed, but does not flag unused type imports — biome does.
#### What caused friction (user side)
- None.
User involvement was strategic at every decision boundary (two planning forks, the proactive untangling Q&A, the ship-time batching decision) — no mechanical oversight, no corrections requiring rework.
- Minor opportunity, not friction: the logger-cycle answer ("same as we did for pi-subagents") required investigating `pi-subagents/src/index.ts` to recover the exact pattern; a file pointer would have saved one exploration step, but the reference was reasonable and unambiguous in hindsight.
### Diagnostic details
- **Model-performance correlation** — one subagent dispatch (`pre-completion-reviewer`) ran on `anthropic/claude-sonnet-4-6`; appropriate for judgment-heavy review (acceptance criteria, code design, docs staleness).
No mismatch.
- **Escalation-delay tracking** — no `rabbit-hole` friction; the Cycle 1 batch-edit rejection resolved in ~2 tool calls, well under the 5-call escalation threshold.
- **Unused-tool detection** — none notable; planning exploration used `grep`/`Read` efficiently and no `missing-context` point would have been better served by an Explore subagent or `colgrep`.
- **Feedback-loop gap analysis** — `pnpm run test` (per-file) and `pnpm run check` ran incrementally after each cycle (good); `pnpm run lint` ran only at the end, which is the sole gap and the direct cause of the late unused-import catch.
### Changes made
1. Appended this Final Retrospective stage entry to `packages/pi-permission-system/docs/retro/0338-collapse-index-closure-bags.md`.
No `AGENTS.md` or prompt changes — the two friction points (Edit-batch-from-memory; late lint) are already covered by existing guidance or too marginal to warrant a rule (user confirmed: land retro only).