8.9 KiB
issue, issue_title
| issue | issue_title |
|---|---|
| 288 | Extract shared test fixtures to cut permission-system test duplication |
Retro: #288 — Extract shared test fixtures to cut permission-system test duplication
Stage: Planning (2026-05-31T00:00:00Z)
Session summary
Produced a numbered migration plan for extracting duplicated test setup in pi-permission-system/test/ into focused test/helpers/ modules.
Grounded the clone families in a live fallow dupes run (122 groups, 9.1%) and confirmed the divergent makeCheckResult defaults across gates/runner, gates/bash-path, and tool-call copies.
Plan is a pure test refactor (no src/ changes); next step is /build-plan since cycles are migrate → full-suite-green → commit, not red→green.
Observations
- Three user-confirmed design decisions via
ask_user: no co-located helper tests (transitive coverage), focused files by concern (mirrorpi-subagents/test/helpers/), and a single neutral-defaultmakeCheckResultwith explicit per-call overrides. - The divergent
makeCheckResultdefaults are the main correctness risk —bash-pathusestoolName: "path"/source: "special"/origin: "global";runneraddsmatchedPattern: "*". Migration must pass each site's original fields as explicit overrides. - Watch the testing-skill trap: do not annotate mock-bag factories (
makeHandler,makeRunnerDeps) with the production interface, or.mockReturnValueaccess is erased. - Keep the regression-guard import in
external-directory-integration.test.ts— it intentionally fails the load if a message helper is removed. permission-system.test.tsis 2839 lines; only the targeted intra-filecreateManager/config clones are in scope — leavewithIsolatedSubagentEnvand env handling untouched.- Step 5 (lifecycle setup) and the ext-dir block's final home are flagged as open questions to settle during implementation.
- Initial
Writehit an external-directory denial from a wrong absolute path (/Users/chris/development/pi/pi-permission-system/...); the repo root ispi-packages. Use repo-relative paths.
Stage: Implementation — TDD (2026-05-31T14:45:00Z)
Session summary
Completed all 6 migration steps from the plan: handler fixtures (Step 1), external-directory family (Step 2), gate fixtures (Step 3), manager harness (Step 4), lifecycle setup (Step 5), and docs refresh (Step 6). Test count held steady at 71 files / 1628 tests throughout — pure refactor, no assertions changed. Pre-completion reviewer returned WARN (resolved before shipping).
Observations
- Step 1
makeCheckResultsignature change required converting positional-statecalls intool-call-events.test.tsto override-bag form with explicitmatchedPattern: "*"where the original factory had it as a default. All other files used the neutral default safely. - Step 2
makeToolCallEventinexternal-directory-integration.test.tsusedinputas a direct second argument (not wrapped); migrated all call sites to{ input: {...} }wrapper convention to align with the shared factory. No test failures. - Step 3
makeCheckResultdefaults diverged across runner vs bash-path/path files; gate-fixtures introducesmakeGateCheckResult(path defaults) alongside the neutralmakeCheckResultfrom handler-fixtures to avoid verbose per-call overrides in the 20+ bash-path call sites. - Step 4
CreateManagerOptionswas still used increateManagerWithProjectinpermission-system.test.tsafter removing the local definition — needed an explicit import from the harness. TheTS2345error at line 1170 (pre-existing latent type issue) was resolved as a side effect onceCreateManagerOptionswas properly imported. - Step 5
makeSessioninbefore-agent-start.test.tsandlifecycle.test.tshave different method sets (different lifecycle phases), so onlymakeCtxwas extracted. The 39-line fallow clone was primarily themakeCtxbody. - WARN 1 resolved: stale
PermissionGateHandlerimport intool-call.test.tsremoved (biome lint warning, exit 0). - WARN 2 resolved:
package-pi-permission-systemSKILL.md Testing section updated withtest/helpers/layout and the divergent-defaultmakeCheckResultoverride pattern. - Pre-completion reviewer verdict: WARN (2 findings, both resolved before shipping).
Stage: Final Retrospective (2026-05-31T18:56:41Z)
Session summary
Single-session execution of the full lifecycle (plan → TDD → ship → retro) for the test-fixture extraction.
All 6 migration steps landed green, duplication dropped 9.1% → 7.1% (clone groups 122 → 113), and pi-permission-system-v8.2.1 released cleanly.
The dominant friction was a recurring import-reconciliation slip when removing local factory definitions during migration — three instances, two caught by tsc, one that escaped both green-gates to the pre-completion reviewer.
Observations
What went well
- The upfront
ask_userin planning (three design decisions: no co-located helper tests, focused files by concern, single neutral-defaultmakeCheckResult) paid off — zero design churn during implementation across all 6 steps. - Incremental verification:
pnpm run check+vitest runran after every step (msgs 58, 65, 76, 79, 89, 96, 101), so each commit left the suite green; no broken-baseline commits. - The
makeGateCheckResultdecision (Step 3) — introducing a path-surface factory alongside the neutralmakeCheckResultrather than forcing four explicit overrides at 20+ bash-path call sites — was a sound mid-implementation judgment that stayed within the plan's intent. - The pre-completion reviewer was the only safety net that caught the stale
PermissionGateHandlerimport; the deterministic green-gates (check,lint) both passed it.
What caused friction (agent side)
missing-context— import reconciliation after removing local factory definitions (recurring, 3 instances). Inbash-path.test.tsthe removedPermissionCheckResultimport was still used by theCheckPermissionFntype alias (tsccaught it, msg 76-78); inpermission-system.test.tsthe removed localCreateManagerOptionswas still referenced bycreateManagerWithProject(tsccaught two errors, msg 89-95); intool-call.test.tsthe now-unusedPermissionGateHandlervalue import was left behind and escaped to the reviewer. Impact: 2 extra edit+recheck cycles (~6 tool calls) plus 1 post-reviewer cleanup commit (67259f66).other(tooling) — the multi-blockEditontool-call.test.tsfailed on whitespace matching (msg 41); fell back to full-fileWrite. Acat -Adiagnostic also failed (macOScatlacks-A). Impact: ~2 extra tool calls; no rework.instruction-violation(self-identified) — the first plan-fileWritetargeted/Users/chris/development/pi/pi-permission-system/..., dropping thepi-packagesrepo segment, and hit an external-directory denial (msg 18-20). Self-corrected in one retry with a repo-relative path. Impact: 1 wastedWrite+ 1 diagnosticbash.
What caused friction (user side)
- None substantive.
The three
Continue.nudges (msgs 64, 70, 74, 121, 127) were mechanical pacing prompts, not redirections — the work was on-track at each.
Diagnostic details
- Model-performance correlation — no mismatches.
Planning + retro ran on
claude-opus-4-8(judgment-heavy synthesis), TDD onclaude-sonnet-4-6(mechanical migration), the pre-completion reviewer subagent onanthropic/claude-sonnet-4-6(judgment-heavy review), and shipping onopencode-go/deepseek-v4-flash(deterministic checklist). Each model was well-matched to task complexity; the cheap flash model on the mechanical ship checklist is appropriate cost optimization, not a mismatch. - Feedback-loop gap analysis —
checkandvitestran per-step, butpnpm run lintran only at the end (msg 109). The decisive gap:pnpm run lintexits 0 on biome warnings, and an unused value import is a warning — so the stalePermissionGateHandlerimport passed bothpnpm run check(tsc does not flag unused imports withoutnoUnusedLocals) and thepnpm run lintexit code. Only the reviewer's reading of the biome warning output caught it. - Unused-tool detection — a
grepfor each removed symbol before deleting its import would have pre-empted all threemissing-contextinstances;grepwas available and used elsewhere but not systematically before import removal.
Follow-up proposal
A testing-skill bullet capturing the import-reconciliation step and the biome-warning gotcha was proposed but declined by the user — the observation lives here in the retro only.
If the import-reconciliation slip recurs in a future session, revisit promoting it to the testing skill.
Changes made
- Appended this Final Retrospective stage entry to
packages/pi-permission-system/docs/retro/0288-extract-shared-test-fixtures.md. No prompt orAGENTS.mdchanges — the user chose to record the import-reconciliation observation in the retro only.