Files
my-pi/pi-permission-system/docs/plans/0559-complete-authority-migration.md
T

16 KiB
Raw Blame History

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 14 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.tstest/authority/foo.test.ts layout by moving the five modules' test files into test/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 14 modules).
  • No update to docs/architecture/v3-architecture.md — it is a frozen pre-authority/ snapshot (last touched at #314, before the authority/ 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/*, or docs/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.js local-rules/no-parent-relative-imports rule flags any ../… import in packages/*/src|test/ and auto-fixes it to the package's #src//#test/ alias. Same-directory ./… imports are permitted (that is why index.ts and these modules use ./sibling today). So after the move, eslint --fix mechanically rewrites the parent-relative cases; tsc catches any missed path.
  • #src/… aliases are package-root-absolute: an importer's #src/permission-dialog becomes #src/authority/permission-dialog because 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):

  1. Moved modules that reference each other stay ./sibling (all five land in the same authority/ directory): permission-forwarding.ts keeps ./permission-dialog and ./subagent-registry; subagent-lifecycle-events.ts keeps ./subagent-registry; forwarding-manager.ts keeps ./permission-forwarding.
  2. Moved modules that reference an existing authority/ resident lose the authority/ path segment: forwarding-manager.ts's ./authority/forwarded-request-server./forwarded-request-server, ./authority/subagent-detection./subagent-detection.
  3. 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.
  4. Existing authority/ importers of a moved module gain the authority/ segment: #src/permission-dialog#src/authority/permission-dialog, #src/subagent-registry#src/authority/subagent-registry, #src/permission-forwarding#src/authority/permission-forwarding.
  5. handlers/gates/runner.ts and test files/helpers: same #src/…#src/authority/… rewrite as (4).
  6. 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):

  1. src/permission-dialog.tssrc/authority/permission-dialog.ts (leaf; no import edits in the file itself).
  2. src/subagent-registry.tssrc/authority/subagent-registry.ts (leaf; no import edits).
  3. src/subagent-lifecycle-events.tssrc/authority/subagent-lifecycle-events.ts (./subagent-registry unchanged — same dir).
  4. src/permission-forwarding.tssrc/authority/permission-forwarding.ts (./permission-events#src/permission-events; ./permission-dialog and ./subagent-registry unchanged).
  5. src/forwarding-manager.tssrc/authority/forwarding-manager.ts (./authority/forwarded-request-server./forwarded-request-server; ./authority/subagent-detection./subagent-detection; ./permission-forwarding unchanged).

Importer updates (#src/…#src/authority/… for the moved symbols):

  1. 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-forwarding imports to the #src/authority/… form (11 files; some import two of the moved modules).
  2. src/handlers/gates/runner.ts#src/permission-dialog#src/authority/permission-dialog.
  3. src/index.ts — the four ./… imports of forwarding-manager, permission-dialog, subagent-lifecycle-events, subagent-registry./authority/….

Test moves (mirror the test/authority/ layout) + import updates:

  1. test/permission-dialog.test.tstest/authority/permission-dialog.test.ts.
  2. test/subagent-registry.test.tstest/authority/subagent-registry.test.ts.
  3. test/subagent-lifecycle-events.test.tstest/authority/subagent-lifecycle-events.test.ts.
  4. test/permission-forwarding.test.tstest/authority/permission-forwarding.test.ts.
  5. test/forwarding-manager.test.tstest/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/):

  1. test/helpers/forwarding-fixtures.ts#src/permission-forwarding#src/authority/permission-forwarding; #src/subagent-registry#src/authority/subagent-registry.
  2. test/helpers/session-fixtures.ts#src/forwarding-manager#src/authority/forwarding-manager.
  3. test/composition-root.test.ts and any remaining test importers (test/authority/*.test.ts already using #src/permission-forwarding / #src/subagent-registry) — same #src/…#src/authority/… rewrite (caught by tsc).

Documentation updates (current-state docs only; land in the same commit):

  1. 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 the authority/ 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 the authority/ migration.** step heading with and update the Mermaid S5 node label to "✅ Step 5 (#559)<br/>Complete authority/ migration".
    • The Phase 9 target table's Flat src/ root modules ~67 → ~62 row is a target/exit table and needs no edit — the move realizes the already-stated target.
  2. docs/subagent-integration.md — update the two prose path references src/subagent-lifecycle-events.tssrc/authority/subagent-lifecycle-events.ts and src/subagent-registry.tssrc/authority/subagent-registry.ts.
  3. .pi/skills/package-pi-permission-system/SKILL.md — update the two prose path references (src/subagent-lifecycle-events.ts, src/subagent-registry.ts) to their src/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:

  1. No new unit tests are enabled — no new seam or collaborator is created.
  2. 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.
  3. 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 14 wired into the authority/ spine, so the relevant invariants are those steps' documented outcomes:

  • Step 3's pinned invariant — the forwarded permissions:ui_prompt broadcast stays non-degraded (original source, surface/value projection, populated forwarding context) — is exercised by the composition-root round-trip and test/authority/forwarded-request-server.test.ts. A pure file move cannot regress it; the same code runs. The green suite (including test/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).

  1. Move + rewrite + doc-mark (single commit).
    • git mv the five source files into src/authority/ and the five test files into test/authority/.
    • Run pnpm --filter @gotgenes/pi-permission-system exec eslint . --fix to 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, and pnpm fallow dead-code all pass; git status shows 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's no-parent-relative-imports fails on a stray ../. The suite will not go green until every reference is updated.
  • git mv recorded as delete+add, losing history. Mitigation: use git 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.