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,173 @@
|
||||
---
|
||||
issue: 581
|
||||
issue_title: "pi-permission-system: decision record for the case-by-case model judge (ModelTriageAuthorizer)"
|
||||
---
|
||||
|
||||
# ADR 0007 — decision record for the case-by-case model judge (`ModelTriageAuthorizer`)
|
||||
|
||||
## Release Recommendation
|
||||
|
||||
**Release:** ship independently
|
||||
|
||||
Phase 11 Step 7 is tagged `Release: independent` in the roadmap, and it is not a member of any release batch (the only Phase 11 batch is "shell-tool-aliases": Steps 2, 3).
|
||||
This is a documentation-only step — a `docs:` ADR plus a roadmap-completion doc update — so it carries no code change and gates nothing.
|
||||
|
||||
## Problem Statement
|
||||
|
||||
Issue [#472] (a case-by-case model judge beyond `yoloMode`) has been deferred by name in Phases 9 and 10.
|
||||
The repeat-deferral rule requires an explicit decision this phase rather than a third silent re-defer.
|
||||
The `ModelTriageAuthorizer` design is already settled in the architecture doc's [Discriminating delegation](../architecture/architecture.md#discriminating-delegation-a-model-authorizer) section; what stands between "designed" and "schedulable" is committing the open parameters to a decision record.
|
||||
|
||||
This is documentation only.
|
||||
It does **not** implement the judge — [#472] stays open, tracking the implementation, and gains a linked ADR.
|
||||
|
||||
## Goals
|
||||
|
||||
- Write `docs/decisions/0007-model-triage-authorizer.md` recording the settled design parameters of `ModelTriageAuthorizer`:
|
||||
- **Decision surface** — ask-only (the judge sees `ask`, never `allow`/`deny`), preserving the deny-preserving boundary.
|
||||
- **Shape** — a decorator, `ModelTriageAuthorizer(inner)`, not a fourth channel.
|
||||
- **Failure behavior** — fail-closed delegation to `inner` on model-unreachable / timeout / low-confidence, never an auto-allow.
|
||||
- **Audit tagging** — a model grant is distinguished in the review log as `origin: "authorizer:model"` (with model version and structured intent), mirroring how yolo grants carry `origin: "yolo"`.
|
||||
- **Non-persistence** — a model verdict stays live-only; it does not silently become recorded authority.
|
||||
- **Bounded delegation** — which surfaces the model may auto-allow is itself ruleset-expressible, with `external_directory` and secret-shaped `path` rules excluded so they always reach the human.
|
||||
- Mark Phase 11 Step 7 complete in `docs/architecture/architecture.md` (step heading `✅` + Mermaid node `✅`), and link the new ADR from the `Discriminating delegation` section.
|
||||
- Leave [#472] open with a comment linking the ADR, so it becomes schedulable in a future phase on its own merits.
|
||||
|
||||
This change is **not breaking** — it adds a decision record and a doc-completion marker; no config, schema, behavior, or default changes.
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- Implementing `ModelTriageAuthorizer` — that is [#472]'s scope, gated on this ADR.
|
||||
No `src/` change, no `RuleOrigin` enum extension, no config field, no schema regeneration.
|
||||
- Deciding the model provider, prompt, confidence-threshold value, or timeout duration — those are implementation parameters for [#472], deliberately left to that issue.
|
||||
- The non-deterministic access-intent **classifier** (a model shaping the intent before `evaluate()`) — a distinct, more distant direction the architecture doc already flags as warranting its own future ADR.
|
||||
- Multi-hop escalation, grant-scope selection, and yolo inheritance — already settled in ADRs 0005 / 0006 and the architecture's `Resolved direction` section; the ADR references them but does not re-decide them.
|
||||
|
||||
## Background
|
||||
|
||||
Relevant existing surfaces:
|
||||
|
||||
- `src/authority/authorizer.ts` — the `Authorizer` interface (`authorize(details): Promise<PermissionPromptDecision>`) and `selectAuthorizer`, which picks `LocalUserAuthorizer` / `ParentAuthorizer` / `DenyingAuthorizer` once per session activation.
|
||||
The ADR's decorator wraps whichever of these is selected — this is the interface the future `ModelTriageAuthorizer(inner)` implements and composes over.
|
||||
- `src/rule.ts` — `RuleOrigin` (currently `global | project | agent | project-agent | builtin | baseline | session | yolo`) and `rewriteAsksToYolo`, the composition-stage `ask`→`allow` rewrite tagged `origin: "yolo"`.
|
||||
The audit-tagging decision names `"authorizer:model"` as the model-grant analogue; whether that lands as a `RuleOrigin` member or a separate review-log field is an implementation detail deferred to [#472] (a model grant is non-persistent, so it does not necessarily become a `Rule`).
|
||||
- ADR 0005 (`docs/decisions/0005-serving-authorizer-provenance.md`) — establishes that an `Authorizer` is live authority and never touches `evaluate()`; the non-determinism principle governs recorded authority only.
|
||||
This is the precedent the model-judge ADR extends: `LocalUserAuthorizer` is already a non-deterministic oracle (the human), so a model holding the same role is consistent with the existing model.
|
||||
|
||||
Constraints from AGENTS.md and the package skill that apply:
|
||||
|
||||
- ADR numbering is per-package; next free is `0007` (existing run `0001`–`0006`).
|
||||
- `docs/architecture/architecture.md` is shipped in the npm tarball allowlist and inline-copies core `rule.ts` types — but this ADR adds no `rule.ts` field, so that listing needs no edit.
|
||||
- Mark the roadmap step complete in the **implementation** doc-update commit (this build), not a deferred `/ship-issue` commit — `✅` on both the step heading and its Mermaid node.
|
||||
- Reference GitHub issues in long-lived docs with reference-style links (`[#N]` + a file-scoped `[#N]:` definition).
|
||||
|
||||
## Design Overview
|
||||
|
||||
This is a documentation change; the "design" is the ADR's decision content and its faithfulness to the settled architecture-doc section.
|
||||
|
||||
### ADR structure
|
||||
|
||||
Follow the established ADR template (0005 / 0006): YAML frontmatter (`status: accepted`, `date`), `# 0007 — <title>`, then `## Status`, `## Context`, `## Decision`, `## Consequences` (with `### Accepted limitations`), and reference-link definitions.
|
||||
|
||||
The `## Decision` section records six settled parameters as numbered decisions:
|
||||
|
||||
1. **Ask-only decision surface.**
|
||||
The judge sees only `ask`.
|
||||
Denies are decided by recorded authority (`evaluate()`) and structurally never reach an `Authorizer`, so the model *cannot* grant a hard deny — the safeguard for a sensitive resource stays an explicit `deny` rule, which survives the model exactly as it survives the yolo rewrite.
|
||||
Where yolo rewrites every `ask` to `allow`, the model resolves only the asks it is confident about and escalates the rest: a discriminating, deny-preserving yolo, a middle rung between prompt-everything and allow-everything.
|
||||
|
||||
2. **Decorator shape, not a fourth channel.**
|
||||
`ModelTriageAuthorizer(inner)` wraps whichever `Authorizer` `selectAuthorizer` produced (`LocalUser` / `Parent` / `Denying`) and implements the same one-method interface.
|
||||
It is the recursion "a node's `Authorizer` is its own parent" with the model's parent being `inner`.
|
||||
Rejected alternative: a distinct fourth selection channel alongside the three-way dispatch — rejected because it duplicates the escalation wiring the decorator gets for free.
|
||||
|
||||
3. **Fail-closed delegation.**
|
||||
Model unreachable, timeout, or low confidence delegates to `inner` (the human, `ParentAuthorizer`, or `DenyingAuthorizer`), never an auto-allow.
|
||||
Under a headless `DenyingAuthorizer` inner, an uncertain model verdict therefore denies — the fail-safe direction.
|
||||
|
||||
4. **Audit tagging.**
|
||||
A model grant is distinguished in the review log as `origin: "authorizer:model"`, carrying the model version and the structured intent, mirroring `origin: "yolo"`.
|
||||
Note the implementation seam (deferred to [#472]): a model grant is non-persistent, so unlike yolo it does not become a `Rule` in the ruleset — the tag rides the review-log entry / decision source, not necessarily the `RuleOrigin` enum.
|
||||
The ADR settles the *decision* (model grants are audited and distinguishable); the mechanism is [#472]'s.
|
||||
|
||||
5. **Non-persistence — live-only.**
|
||||
A model verdict stays live-only; it does *not* silently become recorded authority.
|
||||
Unlike a human's "for this session" ruling, a probabilistic judgment never hardens into durable config.
|
||||
Rejected alternative: persist model grants *quarantined* for later human review.
|
||||
Rejected for this ADR as added machinery with no present consumer — live-only is the simpler fail-safe default; a quarantine store can be a named future extension if a review workflow ever wants it.
|
||||
|
||||
6. **Bounded delegation, ruleset-expressible.**
|
||||
Which surfaces the model may auto-allow is itself expressed as ruleset config, with `external_directory` and secret-shaped `path` rules excluded so they always reach the human.
|
||||
This keeps the delegation boundary reviewable in the same config the rest of the policy lives in, honoring the package principle "prefer config patterns over new runtime mechanisms."
|
||||
|
||||
### Relationship to `evaluate()` and [#509]
|
||||
|
||||
The ADR situates the model judge as the ask-*consuming* side of the boundary, distinct from the ask-*producing* side (`evaluate()` and rule-driven promotion, [#509]).
|
||||
A promoted bare filename (`git grep id_rsa` prompts) is a deliberate fail-safe false positive on the producing side; `ModelTriageAuthorizer` dismisses such a false positive on the consuming side without hard-coding per-command file-argument tables.
|
||||
The ADR notes this is the principled successor to the per-command argument-position work deferred from [#509], and that the two compose cleanly because a promoted token emits the same structured descriptor a prefixed path does — the `Authorizer` needs no promotion-specific knowledge.
|
||||
|
||||
### No new collaborator to sketch
|
||||
|
||||
The ADR introduces no code and no runtime collaborator in this change.
|
||||
The decorator's call site is the existing `selectAuthorizer` return value wrapped as `new ModelTriageAuthorizer(selected)`, exercising the already-shipped one-method `Authorizer` interface — a Tell-Don't-Ask shape (`authorize(details)` returns a decision; the caller does not inspect the authorizer's state).
|
||||
The concrete wiring is [#472]'s to build and test.
|
||||
|
||||
## Module-Level Changes
|
||||
|
||||
Documentation only.
|
||||
|
||||
- **New:** `packages/pi-permission-system/docs/decisions/0007-model-triage-authorizer.md` — the ADR described in Design Overview.
|
||||
- **Changed:** `packages/pi-permission-system/docs/architecture/architecture.md`:
|
||||
- Mark Phase 11 Step 7 complete: `✅` on the `#### Step 7:` heading and on the `S7[...]` Mermaid node in the step-dependency diagram.
|
||||
- Add a link to the new ADR from the `### Discriminating delegation: a model Authorizer` section (a "Decision recorded in ADR 0007 (`docs/decisions/0007-model-triage-authorizer.md`)" note), and update the [#472]/Step-7 deferral references so they point at the recorded decision rather than an open deferral.
|
||||
- No edit to the inline `rule.ts` type listing — this change adds no `RuleOrigin` member.
|
||||
- **No change** to `src/`, `test/`, `schemas/`, `config/`, `README.md`, `docs/configuration.md`, or the package allowlist — the ADR ships via the already-listed `docs/decisions` path, and no user-facing config or command changes.
|
||||
|
||||
Grep confirmation performed during planning: `ModelTriageAuthorizer` appears only in `docs/architecture/architecture.md` (design narrative) — no `src/` or `test/` occurrence, no README/configuration mention — so no code or user-doc surface references the not-yet-built symbol.
|
||||
|
||||
## Test Impact Analysis
|
||||
|
||||
None.
|
||||
This is a documentation-only change with no test surface — it goes through `/build-plan`, not `/tdd-plan`.
|
||||
The verification gate is `pnpm exec rumdl check` on the new and edited markdown plus the standard `pnpm run lint` / link-reference checks; the `pre-completion-reviewer` covers Mermaid-diagram validity for the edited node.
|
||||
|
||||
## Invariants at risk
|
||||
|
||||
None.
|
||||
No prior phase step's code invariant is touched — the change adds a decision record and a completion marker.
|
||||
The one cross-doc invariant to preserve is internal consistency: the architecture doc's `Discriminating delegation` narrative and the new ADR must not disagree on any of the six parameters.
|
||||
The build step verifies this by re-reading both after editing.
|
||||
|
||||
## Build Order
|
||||
|
||||
This is a docs/config change (no red→green test cycles); execute as an ordered build.
|
||||
|
||||
1. **Write the ADR.**
|
||||
Create `docs/decisions/0007-model-triage-authorizer.md` per Design Overview.
|
||||
Lint with `pnpm exec rumdl check <file>`.
|
||||
Commit: `docs(pi-permission-system): record ADR 0007 for the model triage authorizer (#581)`.
|
||||
2. **Update the architecture roadmap.**
|
||||
Mark Step 7 `✅` (heading + Mermaid node), link the ADR from the `Discriminating delegation` section, and refresh the [#472] deferral references.
|
||||
Lint the file.
|
||||
Commit: `docs(pi-permission-system): mark Phase 11 Step 7 complete and link ADR 0007 (#581)`.
|
||||
3. **Comment on [#472].**
|
||||
Post a comment linking the recorded ADR so the issue is schedulable on its own merits (done at ship time via the normal flow, or noted here for the ship step — no code change).
|
||||
|
||||
The two doc commits may be squashed into one if preferred; both are `docs:` (a `hidden` changelog type) and neither cuts a release on its own.
|
||||
|
||||
## Risks and Mitigations
|
||||
|
||||
- **Risk: the ADR drifts from the settled architecture-doc design.**
|
||||
Mitigation: the ADR is transcribed directly from the `Discriminating delegation` section; the build step re-reads both for consistency, and the pre-completion reviewer checks documentation coherence.
|
||||
- **Risk: over-specifying implementation detail the ADR should leave to [#472].**
|
||||
Mitigation: the ADR records *decisions* (surface, shape, failure mode, tagging, persistence, bounds), and explicitly defers mechanism (model choice, threshold values, `RuleOrigin`-vs-log-field) to [#472] in an `Accepted limitations` / open-implementation note.
|
||||
- **Risk: marking Step 7 complete implies the judge is built.**
|
||||
Mitigation: Step 7's `Outcome` is explicitly "carries a linked ADR and becomes schedulable … no code change"; the completion marker and the retained-open [#472] together make the docs-only nature unambiguous.
|
||||
|
||||
## Open Questions
|
||||
|
||||
- **Audit-tag mechanism** — whether `origin: "authorizer:model"` lands as a `RuleOrigin` enum member or a distinct review-log field is deferred to [#472]; the ADR settles only that model grants are audited and distinguishable.
|
||||
- **Quarantine persistence** — the ADR settles live-only and records quarantined-for-review as a rejected-for-now alternative; if a human-review workflow later wants it, that is a named future extension, not reopened here.
|
||||
|
||||
[#472]: https://github.com/gotgenes/pi-packages/issues/472
|
||||
[#509]: https://github.com/gotgenes/pi-packages/issues/509
|
||||
Reference in New Issue
Block a user