3.0 KiB
issue, issue_title
| issue | issue_title |
|---|---|
| 43 | Eliminate module-scope mutable state and cached getAgentDir() in src/index.ts |
Retro: #43 — Eliminate module-scope mutable state
Final Retrospective (2026-05-03T19:04:00Z)
Session summary
Replaced all module-scope mutable state in src/index.ts (cached getAgentDir() paths, mutable config, logger singletons, setter-injection functions) with an ExtensionRuntime context object created at factory invocation time.
src/index.ts went from 466 → 99 lines; src/runtime.ts (318 lines) now holds the runtime interface, factory, and all relocated helpers.
The forwarded-permissions IO module was also refactored to accept an explicit logger parameter instead of using a module-scope singleton.
Observations
What went well
- The 7-step TDD sequence from the plan executed cleanly — each commit was independently valid and the full test suite stayed green throughout.
The plan's decision to add
runtimetoHandlerDepsalongside the old stubs (step 3) before removing the stubs (step 4) allowed both phases to compile and test independently. - The
createExtensionRuntime({ agentDir: tmpDir })pattern immediately proved its value: 29 tests intests/runtime.test.tsexercise the runtime in isolation without anyPI_CODING_AGENT_DIRtiming hacks. - Forwarded-permissions logger threading touched 30+ call sites but landed in a single clean commit with no rework — the mechanical nature of "prepend logger parameter" made it safe to do in bulk.
What caused friction (agent side)
-
missing-context— Usedvi.fn(() => ({}))to mockPermissionManagerconstructor intests/runtime.test.ts. Arrow functions are not constructable, sonew PermissionManager()threw"() => ({}) is not a constructor". The same pattern caused friction in #42. Impact: 1 failed test run + 1 edit to fix; added friction but no rework beyond the immediate fix. Self-identified. -
missing-context— Wroteawait import("../src/permission-manager")inside non-asyncit()callbacks in step 2 tests forcreatePermissionManagerForCwd. Biome flaggedawaitoutside async function. Impact: 1 failed lint + 1 edit to switch to a static top-level import of the already-mockedPermissionManager. Self-identified. -
wrong-abstraction— The plan's step 3 ("update test mocks") and step 4 ("update handlers + type") were described as sequential, but TypeScript rejects extra properties on typed object literals, soruntimecouldn't be added to test mocks untilHandlerDepsdeclared the field. The solution was to addruntimetoHandlerDeps(with old stubs still present) in step 3, making the type change part of the same commit. Impact: minor plan deviation but no rework — the commit sequence remained valid. Self-identified.
What caused friction (user side)
- Nothing notable. The user provided a clear plan, and the session ran without corrections or redirections.
Changes made
- Added class-constructor mocking rule to
AGENTS.md§ Testing.