mirror of
https://bitbucket.org/siakitem/my-pi.git
synced 2026-08-28 08:35:57 +00:00
feat: vendor permission system source
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
---
|
||||
issue: 595
|
||||
issue_title: "pi-permission-system: ADR 0008 — forwarded access-intent portability and principal identity"
|
||||
---
|
||||
|
||||
# Retro: #595 — ADR 0008 forwarded access-intent portability and principal identity
|
||||
|
||||
## Stage: Planning (2026-07-17T23:56:20Z)
|
||||
|
||||
### Session summary
|
||||
|
||||
Planned Phase 12 Step 1 (Track A) — a docs-only ADR (`docs/decisions/0008-cross-session-access-intent.md`) that settles the cross-session access-intent contract before Steps 2–3 change the wire.
|
||||
Because the deliverable is a decision, ran the `ask_user` gate over the three deliberative parameters and confirmed all three with the operator.
|
||||
Wrote `packages/pi-permission-system/docs/plans/0595-adr-0008-cross-session-access-intent.md` and committed it.
|
||||
|
||||
### Observations
|
||||
|
||||
- **Three decisions confirmed with the operator** (two `ask_user` rounds — the first surfaced the parameters, the second tightened them after grounding in the code):
|
||||
1. **Agent-scoped serving** — `requesterAgentName` graduates from display-only to decision-participating; the parent resolves against its own ruleset scoped to the requester's agent name.
|
||||
Clarified the operator's double-apply worry: forwarding up means the child landed on `ask` (unresolved), and the parent applies a *different* ruleset — a strict superset of agent-neutral, no double-application.
|
||||
2. **Child-fixed match set** — ship `matchValues()` ∪ `boundaryValue()`; the parent never re-derives through its own `PathNormalizer`/cwd.
|
||||
3. **Required field, floor to `ask` on absence** — `ForwardedAccessIntent` is the sole resolution path (legacy `(surface, value)` branch retired in Step 3); a request missing it floors to `ask`, never a hard deny or silent grant.
|
||||
- **Key grounding finding that resolved the operator's portability uncertainty**: `AccessPath.matchValues()` already carries a **cwd-relative alias** (via `getCwdRelativePathPolicyValues`), not just absolute + canonical.
|
||||
So the worktree scenario resolves correctly without re-derivation — a relative parent rule (`src/**`) matches the child's relative alias across cwds, while an absolute rule matches only co-located paths (least privilege).
|
||||
Canonicalization does *not* bridge cwds (git worktrees are real dirs, not symlinks); the relative alias is the bridge.
|
||||
- **Revises ADR 0005** — the "agent-neutral resolution" section of `0005-serving-authorizer-provenance.md` is the exact decision being changed; the ADR must cross-link and state what 0005 behavior is preserved (recorded-authority-first, escalate-`ask`, provenance-on-the-ask).
|
||||
- **ADR 0002 boundary** — the wire schema carries strings (`matchValues: string[]`), never `AccessPath` instances, keeping the manager string-based.
|
||||
- **Release**: mid-batch — defer (batch "cross-session-intent", tail = Step 3 / #597).
|
||||
Both plan-execution commits are `docs:` (hidden), so this step cuts no release on its own.
|
||||
- **No follow-up issues filed** — Steps 2 (#596) and 3 (#597) already exist as the implementation of this contract.
|
||||
- Next stage is `/build-plan` (docs-only, no test cycles).
|
||||
|
||||
## Stage: Planning revision (2026-07-18T00:47:18Z)
|
||||
|
||||
### Session summary
|
||||
|
||||
The operator raised a meta-level doubt — whether the decisions felt hard due to a comprehension gap or a missing reframe — and an advisory dialogue surfaced the unifying principle: **the child owns the facts; the parent owns the judgment**.
|
||||
The plan was amended (commit `88d2c886`) to restructure the ADR principle-first, add a composition section, and name two explicitly deferred edges.
|
||||
|
||||
### Observations
|
||||
|
||||
- **The reframe was real, not over-application.**
|
||||
The principle retro-explains decisions made independently — ADR 0005's serving-is-resolution (judgment repeats at each node) and ADR 0007's chain (plurality of judges within a node) — and derives all three previously confirmed parameters (child-fixed match set = facts at origin; agent-scoped serving = parent's judgment; `ask` floor = no facts → no judgment → escalate).
|
||||
Three independent confirmations distinguished a genuine reframe from a concept stretched too far.
|
||||
- **The unified model** (validated against `authorizer.ts` / `authorizer-selection.ts`): authorization is a walk up a session tree; at each node an ordered sequence of judges (recorded authority → Track B chain links → terminal) examines the same fixed facts; the only inter-node operation is the courier move (`ParentAuthorizer`), which carries facts and never judgment.
|
||||
Key clarification for the operator's mental model: `ask` is *recorded authority's* non-definitive outcome, not an Authorizer's — today each session selects exactly one Authorizer, and every Authorizer answers definitively; the "set of Authorizers that may defer" is the Track B future (ADR 0007), which the operator's intuition anticipated.
|
||||
- **`ParentAuthorizer` is a courier, not a judge** — it occupies the `Authorizer` slot structurally but exercises no judgment; this explains *why* ADR 0005 made serving re-run resolution.
|
||||
- **Synergy consequence recorded**: once Tracks A and B both land, a serving node's chain links (e.g. the model judge) review forwarded asks against the child-fixed fact set — honest evidence, not a parent-side re-derivation.
|
||||
- **Two guards against over-application baked into the plan**: the composition section is descriptive-only (cites `docs/decisions/0007-model-judge-authorizer-chain-adr.md` by path, decides nothing new), and the deferred-edges section names where the model is known-incomplete (single-surface fact set — #565 item 3; multi-hop principal identity).
|
||||
- **For the `/build-plan` stage**: the ADR's Decision section leads with the principle; the four former "decisions" are now consequences derived from it; the two deferred edges belong in `## Consequences`, not silently omitted.
|
||||
|
||||
## Stage: Implementation — Build (2026-07-18T01:00:00Z)
|
||||
|
||||
### Session summary
|
||||
|
||||
Executed the docs-only plan in two `docs:` commits: wrote `docs/decisions/0008-cross-session-access-intent.md` (the principle-first ADR) and marked Phase 12 Step 1 complete in `docs/architecture/architecture.md` (heading `✅` + Mermaid `S1` node `✅` + a `Landed:` note).
|
||||
No `src/`/`test/`/schema/config changes, matching the plan's Non-Goals.
|
||||
Pre-completion reviewer returned PASS.
|
||||
|
||||
### Observations
|
||||
|
||||
- **ADR structure landed principle-first** as planned: `## Decision` leads with *the child owns the facts; the parent owns the judgment*, then four numbered consequences (path portability, wire schema, agent-scoped serving, version skew), a `### Composition` subsection situating the record against ADR 0007, and a `### Explicitly deferred edges` subsection.
|
||||
- **Deviation from the plan (cosmetic)**: the plan's Module-Level Changes placed the two deferred edges under `## Consequences`; the shipped ADR keeps them in a `### Explicitly deferred edges` subsection under `## Decision`, grouped with the four consequences and the composition section.
|
||||
The reviewer flagged this as a WARN but judged it "arguably better organized"; kept as-is to avoid churn on a cohesive structure.
|
||||
- **Recorded the resolved decisions in the architecture `Landed:` note** — the pre-decision Target bullet framed open questions ("tolerant read"; "whether `requesterAgentName` participates or serving stays agent-neutral"), which the ADR resolved (required field with `ask` floor; agent-scoped serving).
|
||||
The `Landed:` note explicitly states it supersedes that speculative framing so the roadmap is not left misleading.
|
||||
- **Pre-completion reviewer: PASS** — lint clean (Biome/ESLint/rumdl over docs); all four Mermaid blocks parse via `mmdc`; ADR cross-links to ADR 0005 ("Base ruleset (agent-neutral resolution)") and ADR 0007 (chain verdict range, terminal split, enforcement checkpoint) verified accurate against source; no scope creep into re-deciding ADR 0007; the one present-tense "is retired ([#597])" phrase is correctly `[#597]`-qualified, not a false implementation claim.
|
||||
- **Reviewer warnings**: one non-blocking WARN (deferred-edges subsection placement under `## Decision` vs. the plan's `## Consequences`); cosmetic only.
|
||||
- **Release**: mid-batch — defer (batch "cross-session-intent", tail = Step 3 / #597).
|
||||
Both commits are `docs:` (hidden), so this step cuts no release on its own; the batch ships when #597 lands.
|
||||
- Next step is `/ship-issue`.
|
||||
|
||||
## Stage: Final Retrospective (2026-07-18T15:03:31Z)
|
||||
|
||||
### Session summary
|
||||
|
||||
Shipped issue #595 end to end in one continuous session: planned the ADR, ran two `ask_user` rounds to settle three parameters, then — prompted by the operator's meta-doubt — held an advisory dialogue that surfaced a unifying principle (*the child owns the facts; the parent owns the judgment*), restructured the plan and ADR around it, built the two `docs:` commits, and shipped with the release deferred to the batch tail (#597).
|
||||
The issue closed on `main`; no release cut (docs-only on `exclude-paths`).
|
||||
A clean session whose standout event was the reframe dialogue that changed the deliverable's structure, not its decisions.
|
||||
|
||||
### Observations
|
||||
|
||||
#### What went well
|
||||
|
||||
- **Advisory reframe that grounded doubt in code, then generalized (novel win).**
|
||||
The operator said the decisions "feel difficult… I can't tell if it's a comprehension gap or a missing reframe."
|
||||
Rather than push forward, each open parameter was grounded in the actual source first: the worktree portability worry dissolved on finding that `AccessPath.matchValues()` already carries a **cwd-relative alias** (via `getCwdRelativePathPolicyValues` in `path-normalization.ts`), so relative parent rules stay portable across cwds without re-derivation.
|
||||
That grounding then generalized into the principle *child owns the facts; parent owns the judgment*, validated against `authorizer.ts`/`authorizer-selection.ts` — it retro-explained ADR 0005 (serving-is-resolution) and ADR 0007 (the chain) and derived all three confirmed parameters.
|
||||
The reframe reshaped the ADR (principle-first + a composition section) without changing a single decision.
|
||||
- **Two-round `ask_user` gate for an ADR issue.**
|
||||
Round 1 surfaced the three deliberative parameters; round 2 tightened them after grounding in the code (e.g. reframing version-skew from "tolerant read" to "required field + `ask` floor").
|
||||
The pattern fit the ADR-issue rule in `/plan-issue` exactly and produced decisions the operator could stand behind.
|
||||
- **Guards against over-application, baked into the artifact.**
|
||||
When a unifying model is attractive there is a real risk of stretching it; the ADR's composition section was kept descriptive-only (cites ADR 0007 by path, decides nothing new) and a deferred-edges section named exactly where the model is incomplete (single-surface fact set #565 item 3; multi-hop principal identity).
|
||||
The pre-completion reviewer confirmed no scope creep into re-deciding ADR 0007.
|
||||
- **Clean incremental verification.**
|
||||
`rumdl check` ran after each doc write (not just at the end); the green baseline (`check` + `lint`) was confirmed before any edit; pre-completion returned PASS.
|
||||
|
||||
#### What caused friction (agent side)
|
||||
|
||||
- `other` (self-caught, no rework) — the plan-restructure `Edit` call carried a stray `"newText2": null` key on its first entry (the `oldText2`/`newText2` anti-pattern AGENTS.md documents).
|
||||
The tool reported "12 block(s)" for 11 intended edits; caught immediately by counting reported blocks against intended edits and re-reading the regions — the exact mitigation AGENTS.md prescribes (Refs #605).
|
||||
Impact: none — the value was `null` (harmless) and the existing guard worked; evidence the rule is doing its job, not a gap.
|
||||
- `other` (minor accuracy) — the Build stage note used a rounded placeholder timestamp (`2026-07-18T01:00:00Z`) instead of a real `date -u` value.
|
||||
Impact: cosmetic; the breadcrumb ordering is still correct, but a real timestamp is trivially better for the cross-session trail.
|
||||
|
||||
#### What caused friction (user side)
|
||||
|
||||
- **None — the meta-doubt intervention was the session's highest-leverage moment.**
|
||||
The operator committed the plan, then reopened it with "these decisions feel difficult."
|
||||
That redirect (a question, not a correction) is exactly the strategic-judgment intervention the workflow wants: it arrived before the ADR was written, when restructuring was cheap, and it produced a materially better artifact.
|
||||
Nothing to change; recorded as a model of good bidirectional feedback.
|
||||
|
||||
### Diagnostic details
|
||||
|
||||
- **Model-performance correlation** — the session switched models frequently (operator-driven), with `claude-opus-4-8` and `claude-sonnet-5` present for the heavy reasoning stretches and lighter models (`claude-haiku-4-5`, `claude-fable-5`, `deepseek-v4-flash`) elsewhere.
|
||||
The one subagent dispatch (`pre-completion-reviewer`) ran on its configured model — judgment-appropriate.
|
||||
No mismatch to flag: the judgment-heavy reframe work had strong models available.
|
||||
- **Escalation-delay tracking** — no `rabbit-hole` friction; longest same-target streak was benign (verifying the multi-edit block count).
|
||||
- **Unused-tool detection** — no `missing-context` gaps; `grep`/`sed`/`Read` grounded every claim in source (the cwd-relative alias finding came from reading `path-normalization.ts` directly).
|
||||
- **Feedback-loop gap analysis** — verification was incremental: baseline `check`+`lint` before edits, `rumdl check` after each doc write, `lint` + `fallow dead-code` at pre-push, CI watched to green.
|
||||
|
||||
### Changes made
|
||||
|
||||
1. `packages/pi-permission-system/docs/retro/0595-adr-0008-cross-session-access-intent.md` — appended this Final Retrospective stage entry.
|
||||
No `AGENTS.md` or prompt changes: the operator confirmed landing the retro file only, since the session's friction was already covered by existing rules (the `newText2` block-count guard, Refs #605) or too trivial to encode (a placeholder timestamp).
|
||||
Reference in New Issue
Block a user