6.9 KiB
issue, issue_title
| issue | issue_title |
|---|---|
| 318 | Introduce an McpTargetList value object in mcp-targets.ts |
Retro: #318 — Introduce an McpTargetList value object in mcp-targets.ts
Stage: Planning (2026-06-02T00:00:00Z)
Session summary
Produced the implementation plan for replacing the pushTarget closure in src/mcp-targets.ts with an McpTargetList value object that owns the ordered-uniqueness invariant.
This is Track C / Step 5 of the architecture roadmap (Finding 4).
The change is behavior-preserving — the existing test/mcp-targets.test.ts is the regression guard and candidate ordering is unchanged.
Observations
- The design is unambiguous per the issue; only one decision needed surfacing: whether
McpTargetListis exported with direct unit tests or kept module-private. Confirmed with the user viaask_user— chose export + direct unit tests, mirroring the existingparseQualifiedMcpToolName(exported + tested) precedent in the same module. This adds a new red→green cycle (Step 1) documenting the invariant in isolation. - Both Non-Goals from the issue were preserved in the plan: no MCP-naming command methods on the list (keeps ordering+uniqueness separate from the
${server}_${tool}spelling), and noMcpInvocation/deriveTargets()class (a one-shot transform in a class costume). - Sole production consumer is
src/input-normalizer.ts(line 106), which spreads the result — sotoArray()returning a defensive copy ([...this.targets]) instead of the live array is behavior-preserving and strictly safer. - The two private helpers (
pushMcpToolPermissionTargets,addDerivedMcpServerTargets) already took apushTargetcallback, so swapping it for an injectedMcpTargetListis a clean DIP-friendly substitution with no LoD / output-argument / reverse-search concerns. - Grep confirmed no
src/,test/, or skill file references the changed symbols beyondinput-normalizerand the two test files; the architecture doc (Finding 4 / Step 5) is the only doc needing an update. - TDD order is 3 cycles: (1)
test:addMcpTargetList+ tests, (2)refactor:rewrite dispatch, (3)docs:mark roadmap Step 5 done. Next step is/tdd-plan.
Stage: Implementation — TDD (2026-06-02T17:10:00Z)
Session summary
Completed all 3 TDD cycles from the plan: (1) exported McpTargetList class with 6 focused unit tests, (2) rewrote createMcpPermissionTargets, pushMcpToolPermissionTargets, and addDerivedMcpServerTargets to construct and tell an McpTargetList instead of threading a pushTarget callback, (3) updated docs/architecture/architecture.md to mark Finding 4 and Step 5 as ✅ resolved.
Test count rose from 1753 to 1759 (+6 new McpTargetList invariant tests).
All deterministic checks (check, lint, test, fallow dead-code) passed throughout.
Observations
- No deviations from the plan.
The two private helpers (
addDerivedMcpServerTargets,pushMcpToolPermissionTargets) already accepted apushTargetcallback, making the swap to an injectedMcpTargetListmechanical — exactly as anticipated. toArray()returning a defensive copy ([...this.targets]) was confirmed safe: the sole consumer (input-normalizer.ts) spreads the result, so the copy is behavior-invisible.- Pre-completion reviewer: PASS.
One WARN noted: the stepdown ordering in
src/mcp-targets.tshas private helpers listed above the exported caller (createMcpPermissionTargets) — this is pre-existing (not introduced by this PR) and left for a future cleanup. - Next step is
/ship-issue #318.
Stage: Final Retrospective (2026-06-02T22:09:04Z)
Session summary
Shipped the McpTargetList value-object extraction across three stages (Planning → TDD → Ship) with zero deviations and a PASS pre-completion review.
The pushTarget closure in src/mcp-targets.ts was replaced by an exported value object owning the ordered-uniqueness invariant; the per-mode dispatch now tells the list instead of asking the array via includes.
CI landed green on efee1b20, issue #318 was closed, and no release-please PR appeared (expected — the change is refactor:/test:/docs: only, no feat:).
Observations
What went well
- The single planning
ask_usergate (exportMcpTargetList+ direct tests vs. keep module-private) was the one genuine judgment call, and resolving it up front shaped the TDD order — it added the dedicated Step 1 red→green cycle that documents the invariant in isolation. A small, well-placed decision gate paid off downstream. - Verification ran incrementally rather than only at the end: green baseline (
check/lint/test) before any code, per-filevitest run test/mcp-targets.test.tsafter each red and green, then the full suite +check+lint+fallow dead-codeafter the last step. The red phase was genuinely observed (6 failures:McpTargetList is not a constructor) before implementing — a real TDD loop, not a retrofit. - Scope discipline held: the pre-completion reviewer flagged a pre-existing stepdown-ordering WARN (private helpers above their exported caller), and it was correctly left alone rather than opportunistically fixed inside a behavior-preserving refactor PR.
What caused friction (agent side)
other— thetest:(6d4da354) andrefactor:(f527ba7c) commit subjects omitted the(#318)issue ref, while the surroundingdocs:commits carried it. These followed the plan's suggested commit messages verbatim, which themselves lacked the ref. Impact: minor — at ship time thegit log --grep='#318'filter missed two commits, so the close-comment commit list had to be built from the push range (d509e960..HEAD) instead. No rework, no wrong artifact; thegit pushoutput already gave the exact range.
What caused friction (user side)
- None. User involvement was limited to the one planning decision gate and stage transitions; no mid-stage corrections or redirects were needed.
Diagnostic details
- Model-performance correlation — one subagent dispatch (
pre-completion-reviewer) ran on judgment-heavy review work (acceptance criteria, code design, conventional commits, Mermaid render check) and returned a thorough, correctly-scoped PASS with one accurate pre-existing WARN. Appropriate task/model match; no mismatch. - Feedback-loop gap analysis — no gap. Verification was incremental at every step (baseline before TDD, per-file after each red/green, full gate after the last step); nothing was deferred to the end that should have run earlier.
- Escalation-delay / unused-tool — not applicable; no
rabbit-holeormissing-contextfriction points arose.
Changes made
- Appended this Final Retrospective entry to
packages/pi-permission-system/docs/retro/0318-introduce-mcp-target-list.md. NoAGENTS.mdor.pi/prompts/changes — the session surfaced no friction justifying a rule change (the one cosmetic commit-ref gap is self-healing via the ship flow's push-range recovery).