14 KiB
issue, issue_title
| issue | issue_title |
|---|---|
| 342 | Retire the permission-system.test.ts catch-all |
Retire the permission-system.test.ts catch-all
Problem Statement
test/permission-system.test.ts is a 2,785-line legacy catch-all holding ~80 flat test() blocks across ~10 unrelated concerns plus a dozen internal clone groups (duplicated createToolCallHarness / createMockContext / config-writing setup).
Every one of those concerns already has a dedicated, co-located test file now that the Phase 4 production refactor (Steps 1-8, #334-#341) made the collaborators independently constructable.
This step redistributes the catch-all's tests into the co-located files and deletes the emptied shell, so the suite is fully co-located and the clone groups vanish with the monolith.
Goals
- Move every catch-all test that has a clear co-located home into that file, behavior-preserving.
- Drop catch-all end-to-end async tests that are already covered by existing handler / composition-root tests; move only genuinely-unique async cases.
- Delete
test/permission-system.test.tsand its now-orphaned local helpers. - Keep the full suite green at every commit (no red phase — this is a lift-and-shift, not new behavior).
- Mark roadmap Step 9 (#342) complete in
docs/architecture/architecture.md.
Non-Goals
- No production source changes. This is a test-only redistribution.
- No assertion strengthening, no new test cases, no coverage additions beyond what the catch-all already exercises.
- No refactor of the destination files' existing tests or their local helpers (e.g.
permission-manager-unified.test.ts'smakeManagerWithConfigstays as-is; we reuse the sharedcreateManagerfor moved tests rather than rewriting existing ones). - No consolidation of
createManagervsmakeManagerWithConfig— that is a separate fixture-hygiene concern.
Background
Relevant existing modules and their co-located test files:
- Shared fixtures live in
test/helpers/(manager-harness.tscreateManager,make-fake-pi.tsmakeFakePi,handler-fixtures.tsmakeHandler/makeCtx,session-fixtures.ts). - The catch-all uses three local helpers the rest of the suite has moved past:
createToolCallHarness(builds the whole extension viapiPermissionSystemExtension(pi)and firespi.fire("tool_call", …)end-to-end),createMockContext,runToolCall,withIsolatedSubagentEnv, andcreateManagerWithProject. - The existing handler integration files (
test/handlers/external-directory-integration.test.ts,test/handlers/external-directory-session-dedup.test.ts) already cover the same external-directory and session-dedup scenarios through the lightermakeHandlerfixture. test/composition-root.test.ts(viamakeFakePi) already covers full-factory wiring, the shutdown teardown chain, and session-state visibility end-to-end.
Constraints from AGENTS.md and the package skill that apply:
- Run the full suite (
pnpm --filter @gotgenes/pi-permission-system exec vitest run) before each commit when shared helpers change — moving tests touchesmanager-harness.ts. - When a roadmap step ships, append
✓ completeto the step line indocs/architecture/architecture.md. - Use Conventional Commits; test moves are
test:.
This step depends on the full production refactor; #341 (Step 8) is closed, so all prerequisites are met.
Design Overview
Two test families, two strategies
The catch-all splits cleanly by fixture:
- Synchronous config-resolution / pure-unit tests (use
createManager,createManagerWithProject,new PermissionManager, or no fixture at all). These have unambiguous co-located homes and move verbatim (imports adjusted). - End-to-end async tests (use
createToolCallHarness, firingtool_call/session_shutdownthrough the whole extension). Per the issue's intent — "now that the collaborators are independently constructable, reusing the now-simpler fixtures" — these are handled drop-redundant / move-unique (decision recorded below).
Decisions (recorded from planning interview)
- Async integration tests: drop the ones already covered by existing
makeHandler-based handler tests andcomposition-root.test.ts; move only genuinely-unique cases, rewritten ontomakeHandler(ormakeFakePifor the shutdown-lifecycle case). - Assertion fidelity: behavior-preserving, not byte-for-byte. When a moved test lands on a different fixture, its assertions may adapt to that fixture's shape provided the behavior under test is identical — no coverage added or dropped.
Per-test redundancy rule for the async family
For each createToolCallHarness test, before moving it:
- Grep the candidate destination handler file for an
it/testasserting the same behavior (same policy state × surface × outcome). - If an equivalent exists, drop the catch-all test (it is one of the clone groups the issue targets).
- If none exists, rewrite the catch-all test onto
makeHandlerin the destination handler file (ormakeFakePiincomposition-root.test.tswhen it needs the full session-shutdown lifecycle).
Presumptive redundancy from the inventory (verified during execution, not assumed):
external_directorytool_callintegration (5 tests, lines 1801-1934) — covered byexternal-directory-integration.test.ts(path scope, allow/deny/ask, confirmation-unavailable).- bash
external_directoryintegration (5 tests, lines 1935-2057) — covered byexternal-directory-integration.test.ts+bash-external-directory.test.ts. - generic ask-prompt serialization (line 2060) — covered by
tool-input-preview.test.ts. - session approval (5 tests, lines 2179-2429) — four covered by
external-directory-session-dedup.test.ts; one (session_shutdown clears session approvals, line 2287) exercises the shutdown-clears lifecycle end-to-end and is the likely-unique case → rewrite ontomakeFakePiincomposition-root.test.ts(itsshutdown teardown chaindescribe) only if no equivalent already asserts re-prompt-after-shutdown.
Promoted fixture
createManagerWithProject (catch-all local, lines 1118-1171) is used by five moved project-scope tests.
Promote it to test/helpers/manager-harness.ts alongside createManager, mirroring its signature (config, agentFiles, options with projectConfig / projectAgentFiles), returning { manager, cleanup }.
This is a test fixture, not a production seam — no Tell-Don't-Ask or Law-of-Demeter concern, and no production interface changes, so the design-review checklist does not apply.
Module-Level Changes
New files:
test/status.test.ts— home forgetPermissionSystemStatus(1 test).test/logging.test.ts— home forcreatePermissionSystemLoggerdebug-toggle / review-log-default (1 test).test/before-agent-start-cache.test.ts— home forshouldApplyCachedAgentStartState/createActiveToolsCacheKey/createBeforeAgentStartPromptStateKey(2 tests).
Changed (tests appended; imports extended only as needed):
test/helpers/manager-harness.ts— add and exportcreateManagerWithProject.test/yolo-mode.test.ts— yolo auto-approve,canResolveAskPermissionRequest, yolo-bypasses-delegated-ask (3 tests).test/system-prompt-sanitizer.test.ts— sanitizer removal cases (3 tests).test/skill-prompt-sanitizer.test.ts— multi-block regression tests (3 tests).test/tool-registry.test.ts—getToolNameFromValue/checkRequestedToolRegistration(2 tests).test/permission-forwarding.test.ts— forwarding target resolution, routing, sentinel rejection (6 tests).test/permission-manager-unified.test.ts— the largest bucket: built-in / bash / mcp / skill / tool resolution,getToolPermission,external_directoryconfig resolution + frontmatter pattern maps,PI_CODING_AGENT_DIR,getConfigIssues, project/per-agent scope precedence, and session-awarecheckPermissionsource-sessiontests (~50 tests, routed into existingdescribeblocks).test/config-store.test.ts—getResolvedPolicyPathsfiles-exist / files-missing (2 tests).test/handlers/external-directory-integration.test.tsand/ortest/handlers/external-directory-session-dedup.test.ts— only genuinely-unique async cases, rewritten ontomakeHandler.test/composition-root.test.ts— the shutdown-clears-approvals case if unique.docs/architecture/architecture.md— append✓ completeto the Step 9 (#342) line.
Removed:
test/permission-system.test.ts— deleted, along with its orphaned local helpers (createToolCallHarness,createMockContext,runToolCall,withIsolatedSubagentEnv,createManagerWithProject,createToolCallHarnesstypes, the trailingPermissionStateno-op import suppressor at line 2783).
Test Impact Analysis
This issue is itself a test-cleanup, so the three lenses read as:
- New lower-level tests enabled: none beyond relocation. The collaborators are already independently tested in their co-located files; this step removes the duplicate higher-level catch-all coverage, it does not add a layer.
- Tests that become redundant: the ~16 end-to-end async tests in the external-directory, bash-external-directory, generic-ask, and session-dedup clone groups, which the existing
makeHandler/makeFakePihandler and composition-root tests already cover. These are dropped, not moved. - Tests that must stay (as moved, not dropped): every synchronous config-resolution test (built-ins, bash, mcp, skill, tool, scope precedence, external_directory policy, session-rule source derivation, forwarding, sanitizers, cache, logger, status).
They genuinely exercise the
PermissionManager/ pure-function layers and have no equivalent in the destination files; they move verbatim.
Migration Order (move → verify → commit)
There is no red phase: the suite stays green throughout.
Each step moves a concern, runs the full suite (pnpm --filter @gotgenes/pi-permission-system exec vitest run), and commits.
Recommend executing with /build-plan rather than /tdd-plan — there are no failing-test cycles.
- New homeless-module files — create
status.test.ts,logging.test.ts,before-agent-start-cache.test.ts; move their 4 tests.test: co-locate status, logging, and before-agent-start-cache tests (#342) - Sanitizer concerns — move the 3 system-prompt-sanitizer tests and 3 skill-prompt-sanitizer regression tests into their existing files.
test: co-locate system-prompt and skill-prompt sanitizer tests (#342) - Yolo + tool-registry + forwarding — move yolo (3), tool-registry (2), and permission-forwarding (6) tests.
test: co-locate yolo, tool-registry, and forwarding tests (#342) - Promote
createManagerWithProject+ move scope-precedence tests — add the helper tomanager-harness.ts; move the 5 project/per-agent precedence tests intopermission-manager-unified.test.ts's multi-scopedescribe.test: promote createManagerWithProject; co-locate scope-precedence tests (#342) - Move surface-resolution tests into unified — built-ins / bash / mcp / skill / tool /
getToolPermission/external_directoryconfig + frontmatter pattern maps /PI_CODING_AGENT_DIR/getConfigIssues(~30 tests) into the matchingdescribeblocks, reusingcreateManager.test: co-locate PermissionManager surface-resolution tests (#342) - Move session-aware
checkPermissiontests into unified — the source-sessiontests (~13) into the existingcheckPermission — session rulesdescribe.test: co-locate session-aware checkPermission tests (#342) - Move
getResolvedPolicyPathstests — intoconfig-store.test.ts(2 tests).test: co-locate getResolvedPolicyPaths tests (#342) - Resolve the async family — for each
createToolCallHarnesstest, apply the drop-redundant / move-unique rule; rewrite any unique case ontomakeHandler(handler files) ormakeFakePi(composition-root.test.ts).test: redistribute or drop end-to-end tool_call tests (#342) - Delete the shell — remove
test/permission-system.test.tsand confirm no orphaned helpers remain; runpnpm run check,pnpm run lint, the full suite, andpnpm fallow dead-code; append✓ completeto the Step 9 line inarchitecture.md.test: delete the permission-system.test.ts catch-all (#342)
Risks and Mitigations
- Re-introducing duplication by moving a redundant async test instead of dropping it.
Mitigation: the per-test grep-the-destination rule in step 8;
pnpm fallow dead-code/ duplication check in step 9. - Losing end-to-end coverage by dropping an async test whose behavior is only superficially covered. Mitigation: drop only when an existing destination test asserts the same policy-state × surface × outcome; otherwise rewrite, do not drop.
- Orphaned local helpers left behind after the shell is partially emptied. Mitigation: defer all helper deletion to the final step (step 9) where the whole file is removed; fallow confirms no dangling references.
- Helper drift between
createManagerand the unified file'smakeManagerWithConfig. Mitigation: moved tests use the sharedcreateManager; existing unified tests are untouched (Non-Goal). - A moved test relies on the catch-all's
withIsolatedSubagentEnvenv isolation. Mitigation: only the forwarding tests touch subagent env; verify they pass under the destination file's setup, adding local isolation if the destination lacks it.
Open Questions
- Exact destination for the unique
session_shutdown clears session approvalscase —composition-root.test.ts(full lifecycle viamakeFakePi) vs a handler-level rewrite. Defer to step 8; pick whichever already has the shutdown-firing harness so no new fixture is introduced. - Whether
getResolvedPolicyPathsbelongs inconfig-store.test.tsorpolicy-loader.test.ts(both already exercise it). Defer to step 7; route to whichever already drives a realPermissionManagerwith on-disk config.