16 KiB
issue, issue_title
| issue | issue_title |
|---|---|
| 559 | pi-permission-system: complete the authority/ directory migration |
Complete the authority/ directory migration
Release Recommendation
Release: ship independently
This is Phase 9 Step 5 — a pure directory move tagged Release: independent in the roadmap.
It lands as a refactor: commit (hidden changelog type), so it does not cut a release on its own; it auto-batches into the next feat:/fix:/unhidden-docs: release.
Phase 9 declares no multi-step release batch — every step leaves the package consistent on its own — so there is nothing to defer or coordinate.
Problem Statement
Phase 8's forward-looking src/ directory sketch places the whole escalation/forwarding/subagent domain under src/authority/: the subagent machinery is the cross-session edge of the authority domain, not a peer domain.
Phase 9 Steps 1–4 rewrote most of those modules into src/authority/ as they were reworked (tidy-first — a file reaches its final home as it is rewritten).
Five modules never needed a rewrite, so they still sit in the flat src/ root.
This step moves that mechanical remainder so the domain is closed, the flat root shrinks, and each file has moved exactly once across Phase 9.
Goals
- Relocate the five remaining escalation/forwarding/subagent modules into
src/authority/. - Rewrite every import of the moved modules (parent-relative imports become
#src/authority/…aliases; same-directory refs stay./). - Mirror the established
src/authority/foo.ts↔test/authority/foo.test.tslayout by moving the five modules' test files intotest/authority/. - Mark Phase 9 Step 5 complete in
docs/architecture/architecture.md(module tree, step heading, Mermaid node) in the same commit as the move. - Preserve behavior exactly — this is a directory move, not a logic change.
Non-breaking: no exported symbol, config default, or observable behavior changes.
The moved modules are all internal (no package.json exports re-export among them), so no consumer outside the package is affected.
Non-Goals
- No logic, signature, or behavior changes to any moved module.
- No renaming of any exported symbol — only file locations and import specifiers change.
- No rework of the
authority/subtree's existing residents (Steps 1–4 modules). - No update to
docs/architecture/v3-architecture.md— it is a frozen pre-authority/snapshot (last touched at #314, before theauthority/subtree existed) and is not maintained as current state. Touching two of its stale tree lines would leave it internally inconsistent; leave it as the historical artifact it is. - No changes to
docs/plans/*,docs/retro/*, ordocs/architecture/history/*— those are frozen per-issue records.
Background
Relevant modules and their current relationships (verified against src/):
| Module | Imports (relative) | Imported by |
|---|---|---|
src/permission-dialog.ts |
none (leaf) | 9 authority/ files, handlers/gates/runner.ts |
src/subagent-registry.ts |
none (leaf) | 5 authority/ files |
src/subagent-lifecycle-events.ts |
./subagent-registry |
index.ts, tests |
src/permission-forwarding.ts |
./permission-dialog, ./permission-events, ./subagent-registry |
5 authority/ files, test helpers |
src/forwarding-manager.ts |
./authority/forwarded-request-server, ./authority/subagent-detection, ./permission-forwarding |
index.ts, test helpers |
src/authority/ already holds the Phase 8/9 residents (authorizer.ts, local-user-authorizer.ts, authorizer-selection.ts, permission-prompter.ts, approval-escalator.ts, forwarded-request-server.ts, forwarding-io.ts, subagent-context.ts, subagent-detection.ts, forwarder-context.ts, denying-authorizer.ts).
AGENTS.md / eslint constraints that shape the move:
- The root
eslint.config.jslocal-rules/no-parent-relative-importsrule flags any../…import inpackages/*/src|test/and auto-fixes it to the package's#src//#test/alias. Same-directory./…imports are permitted (that is whyindex.tsand these modules use./siblingtoday). So after the move,eslint --fixmechanically rewrites the parent-relative cases;tsccatches any missed path. #src/…aliases are package-root-absolute: an importer's#src/permission-dialogbecomes#src/authority/permission-dialogbecause the target moved, regardless of the importer's own location.- The package skill requires the architecture step-complete marker to land in the implementation commit, not a deferred ship commit.
Design Overview
Pure relocation.
Five files move from src/ root into src/authority/; their five test files move from test/ root into test/authority/.
The authority/ subtree already imports four of the five via #src/… aliases, so the dependency direction is unchanged — the modules are already logical authority/ residents, only their physical location is stale.
Import-rewrite rules by category (mechanically enforced by tsc + eslint --fix):
- Moved modules that reference each other stay
./sibling(all five land in the sameauthority/directory):permission-forwarding.tskeeps./permission-dialogand./subagent-registry;subagent-lifecycle-events.tskeeps./subagent-registry;forwarding-manager.tskeeps./permission-forwarding. - Moved modules that reference an existing
authority/resident lose theauthority/path segment:forwarding-manager.ts's./authority/forwarded-request-server→./forwarded-request-server,./authority/subagent-detection→./subagent-detection. - Moved modules that reference a module staying in
src/root gain a#src/alias (was same-dir./, now parent-relative):permission-forwarding.ts's./permission-events→#src/permission-events. - Existing
authority/importers of a moved module gain theauthority/segment:#src/permission-dialog→#src/authority/permission-dialog,#src/subagent-registry→#src/authority/subagent-registry,#src/permission-forwarding→#src/authority/permission-forwarding. handlers/gates/runner.tsand test files/helpers: same#src/…→#src/authority/…rewrite as (4).index.ts(composition root, same-dir./style):./forwarding-manager→./authority/forwarding-manager,./permission-dialog→./authority/permission-dialog,./subagent-lifecycle-events→./authority/subagent-lifecycle-events,./subagent-registry→./authority/subagent-registry.
No collaborator is introduced, no signature changes, no state moves — this is not an extraction, so the anti-procedure-splitting and Tell-Don't-Ask reviews do not apply.
The existing test suite plus tsc and eslint are the full correctness proof: a green suite after the move demonstrates behavior preservation.
Module-Level Changes
Source moves (git mv, no content change beyond import specifiers):
src/permission-dialog.ts→src/authority/permission-dialog.ts(leaf; no import edits in the file itself).src/subagent-registry.ts→src/authority/subagent-registry.ts(leaf; no import edits).src/subagent-lifecycle-events.ts→src/authority/subagent-lifecycle-events.ts(./subagent-registryunchanged — same dir).src/permission-forwarding.ts→src/authority/permission-forwarding.ts(./permission-events→#src/permission-events;./permission-dialogand./subagent-registryunchanged).src/forwarding-manager.ts→src/authority/forwarding-manager.ts(./authority/forwarded-request-server→./forwarded-request-server;./authority/subagent-detection→./subagent-detection;./permission-forwardingunchanged).
Importer updates (#src/… → #src/authority/… for the moved symbols):
src/authority/authorizer-selection.ts,approval-escalator.ts,forwarded-request-server.ts,forwarder-context.ts,denying-authorizer.ts,permission-prompter.ts,local-user-authorizer.ts,forwarding-io.ts,authorizer.ts,subagent-context.ts,subagent-detection.ts— update their#src/permission-dialog/#src/subagent-registry/#src/permission-forwardingimports to the#src/authority/…form (11 files; some import two of the moved modules).src/handlers/gates/runner.ts—#src/permission-dialog→#src/authority/permission-dialog.src/index.ts— the four./…imports offorwarding-manager,permission-dialog,subagent-lifecycle-events,subagent-registry→./authority/….
Test moves (mirror the test/authority/ layout) + import updates:
test/permission-dialog.test.ts→test/authority/permission-dialog.test.ts.test/subagent-registry.test.ts→test/authority/subagent-registry.test.ts.test/subagent-lifecycle-events.test.ts→test/authority/subagent-lifecycle-events.test.ts.test/permission-forwarding.test.ts→test/authority/permission-forwarding.test.ts.test/forwarding-manager.test.ts→test/authority/forwarding-manager.test.ts. Each moved test updates its#src/<module>import to#src/authority/<module>(the#src//#test/aliases are absolute, so the test's own new location needs no other edits).
Test-helper importer updates (helpers stay in test/helpers/):
test/helpers/forwarding-fixtures.ts—#src/permission-forwarding→#src/authority/permission-forwarding;#src/subagent-registry→#src/authority/subagent-registry.test/helpers/session-fixtures.ts—#src/forwarding-manager→#src/authority/forwarding-manager.test/composition-root.test.tsand any remaining test importers (test/authority/*.test.tsalready using#src/permission-forwarding/#src/subagent-registry) — same#src/…→#src/authority/…rewrite (caught bytsc).
Documentation updates (current-state docs only; land in the same commit):
docs/architecture/architecture.md:- Move the five module descriptions out of the flat-root listing (lines for
permission-dialog.ts,subagent-registry.ts,subagent-lifecycle-events.ts,permission-forwarding.ts,forwarding-manager.ts) into theauthority/subtree block, preserving their descriptions and fixing the tree connector glyphs (├──/└──) so the subtree's last child is the only└──. - Mark Step 5 complete: prefix the
**Complete theauthority/migration.**step heading with✅and update the MermaidS5node label to"✅ Step 5 (#559)<br/>Complete authority/ migration". - The Phase 9 target table's
Flatsrc/root modules ~67 → ~62row is a target/exit table and needs no edit — the move realizes the already-stated target.
- Move the five module descriptions out of the flat-root listing (lines for
docs/subagent-integration.md— update the two prose path referencessrc/subagent-lifecycle-events.ts→src/authority/subagent-lifecycle-events.tsandsrc/subagent-registry.ts→src/authority/subagent-registry.ts..pi/skills/package-pi-permission-system/SKILL.md— update the two prose path references (src/subagent-lifecycle-events.ts,src/subagent-registry.ts) to theirsrc/authority/…forms.
No moved module has a package.json exports re-export, Symbol.for() cross-extension surface, or event-channel name that changes — the getSubagentSessionRegistry() accessor and its Symbol.for("@gotgenes/pi-permission-system:subagent-registry") key are unchanged; only subagent-registry.ts's file location moves.
So no wider docs/ grep beyond the three current-state files above is warranted (the remaining hits are frozen plans/retros/history).
Test Impact Analysis
This is a move, not an extraction, so the three extraction questions resolve trivially:
- No new unit tests are enabled — no new seam or collaborator is created.
- No existing tests become redundant — every test still exercises the same module at the same granularity; only the file path and one import line change.
- All five moved test files must stay as-is (content-wise) — they genuinely exercise the moved modules.
They relocate to
test/authority/for layout consistency, keeping their assertions intact.
The full existing suite (pnpm --filter @gotgenes/pi-permission-system run test) staying green after the move is the behavior-preservation proof.
Invariants at risk
The move touches modules that Steps 1–4 wired into the authority/ spine, so the relevant invariants are those steps' documented outcomes:
- Step 3's pinned invariant — the forwarded
permissions:ui_promptbroadcast stays non-degraded (originalsource,surface/valueprojection, populatedforwardingcontext) — is exercised by the composition-root round-trip andtest/authority/forwarded-request-server.test.ts. A pure file move cannot regress it; the same code runs. The green suite (includingtest/composition-root.test.ts's subagent-registry-sharing and forwarding round-trip) confirms it. - Step 4's whole-session-grant round-trip (composition-root test) likewise rides on unchanged logic.
No invariant lives only in prose here — each is pinned by an existing test that runs unchanged after the move.
TDD Order
This is a behavior-preserving move; there is no red phase.
The proof is tsc + eslint + the unchanged green suite.
One atomic commit (the moves and importer rewrites must land together — tsc rejects a half-moved state).
- Move + rewrite + doc-mark (single commit).
git mvthe five source files intosrc/authority/and the five test files intotest/authority/.- Run
pnpm --filter @gotgenes/pi-permission-system exec eslint . --fixto auto-rewrite parent-relative imports to#src/authority/…aliases, then hand-fix the same-dir cases per the Design Overview rules (forwarding-manager.ts's./authority/*→./*;permission-forwarding.ts's./permission-events→#src/permission-events). - Update
index.ts's four./…imports to./authority/…. - Apply the three documentation edits (architecture tree + Step 5
✅+ Mermaid node;subagent-integration.md; SKILL.md). - Verify:
pnpm --filter @gotgenes/pi-permission-system run check(tsc),pnpm --filter @gotgenes/pi-permission-system run lint,pnpm --filter @gotgenes/pi-permission-system run test, andpnpm fallow dead-codeall pass;git statusshows only renames + import-line/doc diffs. - Commit:
refactor(pi-permission-system): move escalation/forwarding/subagent modules into authority/ (#559).
Folding the doc updates into the refactor: commit keeps the change hidden-changelog (no stray docs: release) and satisfies the package skill's "mark the roadmap step complete in the implementation commit" rule.
Risks and Mitigations
- A missed importer.
Mitigation:
tsc(pnpm run check) fails on any unresolved specifier;eslint'sno-parent-relative-importsfails on a stray../. The suite will not go green until every reference is updated. git mvrecorded as delete+add, losing history. Mitigation: usegit mv(not delete+rewrite) so Git records renames; the content change per file is one or two import lines, well within rename-detection thresholds.- Tree-glyph corruption in the architecture listing.
Mitigation: after editing, re-read the
authority/subtree block to confirm exactly one└──(the last child) and correct├──connectors — per the AGENTS decorative-rule caution. - Stale prose path in an unsearched current-state doc.
Mitigation: the
src/-symbol grep was widened to the three current-state docs (architecture.md,subagent-integration.md, SKILL.md); remaining hits are frozen plans/retros/history, explicitly out of scope.
Open Questions
None. The scope is fully mechanical and enumerated; no follow-up issues are filed.