---
package: pi-permission-system
phase: 11
---

# Retro: pi-permission-system — Phase 11 Planning (shell-tool-aliasing-elicitation-ux)

## Stage: Improvement Planning (2026-07-13T02:29:31Z)

### Session summary

The cause hypothesis going in was cross-session path portability (the `ServingPolicy` normalizes a child's forwarded path against the parent's cwd) — the direction Phase 10 named as the leading Phase 11 candidate.
Discovery confirmed it is real but chose to defer it to Phase 12: the Phase 9 serving machinery has just shipped, `#565` is the designated post-ship observation of exactly those behaviors, and two fresh user-reported requests (`#573`, `#574`) arrived during Phase 10's ship window.
The phase shape chosen is a full 7-step phase whose spine is the sibling boundary flaw in the same first-principles domain — the access-intent boundary is closed against the real tool ecosystem (`classifyToolKind` hardcodes built-in names, so `exec_command` from `pi-codex-conversion` bypasses the entire bash enforcement stack, `#574`).

### Observations

- **Cause the phase dissolves.**
  The access-intent boundary — turning `(toolName, input)` into "what is being accessed" — cannot record that a foreign tool name carries bash semantics.
  This is a Category C OCP flaw with a Category F cross-package flavor: the same shell operation is gated differently depending on which toolset is active, and a user's `bash` deny rules silently do not apply.
  The fix records the alias in config (`shellTools`, Step 2) and consumes it at the dispatch point (Step 3), keeping one dispatch point rather than scattering `toolName === …` checks.
- **Deferral-gate outcome: did not fire.**
  `#574` is a genuine cause-level, user-reported enforcement gap, so the phase is not polish-only.
  The Phase 12 candidate (cross-session intent spine) is recorded, not silently re-deferred — an explicit `ask_user` decision confirmed deferring it with `#565` gathering real-session evidence first.
- **Repeat deferrals resolved by decision.** `#472` (model judge) was deferred by name in Phases 9 and 10; this phase writes its decision record (Step 7, ADR 0007, filed as `#581`) so it becomes schedulable — implementation stays future work. `#519` (SDK UIContext) stays open by explicit decision, not a silent sweep: Step 4's `select`-fallback constraint keeps frontend-driven flows working meanwhile.
- **Feasibility probes that shaped steps.**
  - `#573` (keybind dialog): verified `ctx.ui.custom<T>()` exists on the current SDK and renders **inline by default** (`overlay ?? false` in Pi core `interactive-mode.ts` `showExtensionCustom`).
    No SDK evolution needed — this moved `#573` from "blocked like `#519`" to a schedulable Step 4.
    The `@eko24ive/pi-ask` inline flow (pure input-command decision layer, hotkeys, back-navigation between steps) is the named UX model; the user explicitly prefers the inline style over overlay.
  - The seam is narrow: `selectAuthorizer` reads `ctx.hasUI` and hands `ctx.ui` to `LocalUserAuthorizer`, which calls the single `requestPermissionDecisionFromUi(ui, …)` entry.
    The keybind flow slots in behind that one function with a mode-guarded fallback — localizing Step 4 and preserving the `#519` constraint.
- **Fallow corroboration, not agenda.**
  Health 78 (B), dead code 0, duplication 0.4%.
  Both clone groups are intentional near-duplicates (`literalTextOf` fails closed where `resolveNodeText` is best-effort; the two bash gate preambles) — kept per the wrong-abstraction rule.
  The repeated-discriminator sweep found no new family.
  The `value-guards.ts` "split" refactoring target was rejected: a 17-LOC pure-guard leaf with high fan-in is a healthy utility, not a coupling smell.
- **Tidy-first directory move.**
  The access-intent domain has a directory but four of its modules (`input-normalizer`, `mcp-targets`, `tool-input-path`, `path-surfaces`) still sit in the flat root (60 modules).
  Step 1 (`#579`) folds them in before Step 3 rewrites two of them — final location the first time.
  `bash-advisory-check.ts` deliberately stays out (a domain module must not import from `handlers/`).
- **Doc-hygiene fix rolled in.**
  The Phase 10 summary referenced `[#571]` and `[#575]` without reference-link definitions (dangling refs).
  The Phase 11 section adds the definitions.
- **Issues filed:** `#579` (Step 1), `#580` (Step 2), `#581` (Step 7).
  Steps 3–6 reuse existing `#574`, `#573`, `#571`, `#575`.
  Release: "shell-tool-aliases" batch = Steps 2 + 3 (tail Step 3); all others independently releasable.
