feat: vendor permission system source

This commit is contained in:
云服务部-叶林立
2026-08-19 14:35:19 +08:00
parent 198584daf8
commit 410c50a3e5
809 changed files with 157793 additions and 139 deletions
@@ -0,0 +1,61 @@
---
issue: 129
issue_title: "refactor: extract PermissionSession class to encapsulate mutable session state"
---
# Retro: #129 — extract PermissionSession class
## Final Retrospective (2026-05-08T02:30:00Z)
### Session summary
Extracted a `PermissionSession` class that encapsulates all mutable session state (`PermissionManager`, `SessionRules`, cache keys, skill entries, runtime context) with operation-based methods replacing field access.
Migrated all 4 handler files and 6 handler test files to use the new class, shrinking `HandlerDeps` from 18 to 7 fields.
Released as v5.10.0 with 38 new unit tests and no behavioral change.
### Observations
#### What went well
- **`SkillPermissionChecker` interface extraction** — the plan didn't anticipate that `resolveSkillPromptEntries` took a concrete `PermissionManager` type.
Rather than using a cast, extracting a narrow `SkillPermissionChecker` interface in `src/skill-prompt-sanitizer.ts` let both `PermissionManager` and `PermissionSession` satisfy it structurally.
This "narrow interface at the callee" pattern avoided adapter objects and should be the default approach for similar migrations.
- **Phase collapse was efficient** — the plan's strict 4-phase separation (build class → wire type → migrate handlers → cleanup) would have required updating every `makeDeps` factory twice.
Collapsing phases 24 into handler-by-handler migration (each handler + its tests in one commit) was cleaner and produced smaller, reviewable diffs.
- **Handler test simplification was dramatic** — `makeSession()` factories went from 7 nested-mock fields with `as unknown as SessionState["permissionManager"]` casts to flat `vi.fn()` stubs.
The `tool-call-events.test.ts` file shrank from 375 to 302 lines while preserving all test cases.
#### What caused friction (agent side)
1. `premature-convergence` — initial `PermissionSession` constructor followed the plan's "4 deps" signature literally (`ExtensionPaths`, `SessionLogger`, `PermissionPrompterApi`, `ForwardingController`), then added `canPrompt()`/`prompt()` methods before realizing the prompting surface doesn't belong on the session (it depends on `ctx` + subagent detection logic the session doesn't own).
Resulted in writing and then deleting ~30 lines of prompting code, plus adding `PermissionSessionRuntimeDeps` as the actual 4th dep.
Impact: ~10 minutes of rework across two edits.
2. `missing-context` — the lifecycle handler rewrite (`src/handlers/lifecycle.ts`) was written from memory rather than referencing the original.
`handleResourcesDiscover` used an undeclared `session` variable and `handleSessionShutdown` called a non-existent `session.clearStatus()` method.
Caught immediately on the next read, but the file had to be rewritten.
Impact: one extra write cycle, no commit waste.
3. `missing-context``vi.mock` paths in the initial test file used `./src/` instead of `../src/`.
Tests run from `tests/`, so the relative paths were wrong.
Caught on first test run.
Impact: one extra edit, no rework.
4. `missing-context` — test mocks used shorthand `SkillPromptEntry` shapes like `{ name: "s", path: "/s", content: "c" }` that passed Vitest (esbuild, no type checking) but failed `tsc`.
The real type has 6 required fields (`name`, `description`, `location`, `state`, `normalizedLocation`, `normalizedBaseDir`).
Fixed by adding a `makeSkillEntry()` helper.
Impact: one extra commit fixup at the end, but could have been avoided by checking the `SkillPromptEntry` type before writing mock data.
5. `wrong-abstraction` — the plan proposed `PermissionSession` absorb `canPrompt(ctx)` and `prompt(ctx, details)`, but these methods require `isSubagentExecutionContext()` and `canResolveAskPermissionRequest()` which depend on `subagentSessionsDir` and config — concerns the session shouldn't own.
The correct boundary keeps prompting on `HandlerDeps` until #130 handler classes can own the `ctx`-capture pattern.
Impact: plan deviation documented in the summary, no code waste beyond friction point #1.
#### What caused friction (user side)
- No friction observed.
The user ran `/plan-issue`, `/tdd-plan`, and `/ship-issue` in sequence with no mid-session corrections needed.
The autoformat hooks ran cleanly throughout.
### Changes made
1. Created `docs/retro/0129-extract-permission-session.md` (this file).