3.7 KiB
issue, issue_title
| issue | issue_title |
|---|---|
| 56 | Unify Rule type and normalize config into flat Ruleset |
Retro: #56 — Unify Rule type and normalize config into flat Ruleset
Final Retrospective (2026-05-03T20:00:00-04:00)
Session summary
Implemented normalizeConfig() in src/normalize.ts and getSurfaceDefault()/mergeDefaults() in src/defaults.ts, then refactored PermissionManager to store a flat Ruleset instead of per-surface compiled pattern arrays.
Removed BashFilter, six per-surface type aliases, and AgentPermissions/GlobalPermissionConfig — replaced by ScopeConfig.
Released as v3.9.0 with no user-visible behavior change.
Observations
What went well
- The plan's decision to keep
defaultPolicyseparate from theRulesetwas validated by the MCP baseline auto-allow tests — ifdefaultPolicy.mcphad been a catch-all rule, the heuristic would have been bypassed. The analysis during planning correctly identified this constraint. - Combining plan steps 12–14 into a single commit was the right call. Attempting separate commits would have introduced intermediate broken states for no reviewability benefit.
What caused friction (agent side)
-
premature-convergence— The plan confidently stated thattools.bash: "allow"normalizes to{ surface: "bash", pattern: "*", action: "allow" }and "naturally preserves both tool exposure and command fallback." This was wrong:tools.bashin the old model was a fallback default (consulted only when no bash pattern matches), not a catch-all rule (always matches and competes with specific patterns from other scopes). Six tests failed on the first run of the refactoredcheckPermission(). Impact: required reworking bothsrc/normalize.ts(addingTOOL_SURFACE_OVERRIDE_KEYSto excludetools.bash/tools.mcp) andsrc/permission-manager.ts(addingbashDefault/mcpToolLevelextraction), plus updating 3 normalize tests. ThebashDefaultcascade in the oldresolvePermissions()was visible during planning but its semantic implications were not fully traced. Self-identified during the implementation phase. -
wrong-abstraction— The plan listed steps 12, 13, and 14 as separate refactoring commits, but all three depend on the sharedResolvedPermissionstype. Changing the type in step 12 immediately breakscheckPermission()(step 13) andgetToolPermission()(step 14). Impact: added ~5 minutes of re-reading to determine they must be combined. No rework — the combination was straightforward — but the plan was misleading about commit granularity. -
instruction-violation— The pre-commit biome hook rejected the type-alias removal commit due to an unusedgetSurfaceDefaultimport and a formatting inconsistency. Runninggit commit --amendafter the fix silently folded the type-alias removal into the BashFilter removal commit instead of creating a separate commit. Impact: two logically distinct changes (BashFilter removal + type alias removal) landed in one commit. Self-identified viagit logimmediately after.
What caused friction (user side)
- The biome warnings in
src/index.tsandtests/handlers/before-agent-start.test.tswere pre-existing but only flagged after the user asked to fix them. Proactively cleaning lint warnings during the "final verification" step (rather than noting them as pre-existing and moving on) would have avoided the extra round-trip.
Changes made
- Added
AGENTS.md§ Implementation Priorities bullet documentingtools.bash/tools.mcpas fallback overrides excluded fromRulesetnormalization. - Added
AGENTS.md§ Testing bullet about folding tightly coupled TDD steps that share a type definition.