From e923d71efb233e5d78ca99c4095b98b40fc6f7cd Mon Sep 17 00:00:00 2001 From: "@permadeath.com" Date: Fri, 4 Sep 2026 10:06:31 -0400 Subject: [PATCH] docs(policy): make consent a denial, evaluated at authorize An agent has nobody at a consent screen, so the question is whether the application is denied. Permit-unless-denied holds only while a tree is loaded; an unreadable source still refuses. Co-Authored-By: Claude Opus 5 (1M context) Change-Id: Id083c07f04169ada3e1bcc0cbdd87d5e1fb79601 --- plan/policy.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/plan/policy.md b/plan/policy.md index f325ea60..162525e8 100644 --- a/plan/policy.md +++ b/plan/policy.md @@ -408,6 +408,29 @@ first: expect them is open. - **Whether deletes are evaluated**, per the item above. +## Consent is a denial, evaluated at authorize + +An agent has nobody at a consent screen, so the question at authorize is not +whether somebody approved an application — it is whether this one is denied. +That is this epic's own model applied one step earlier, and the machinery is +already shaped for it: the grant is a subject kind of its own, and denying an +application by its client identifier applies to a grant request as much as to +a write. + +- [ ] **Permit-unless-denied, but only when a tree is loaded.** "No denial" is + a decision; "policy could not be read" is not. The consent seam's own + invariant — approval must never happen merely because there was nobody + to say no — stays true if absence of a *denial* answers and absence of a + *source* refuses. The last-good machinery is what tells the two apart. +- [ ] **Write the lexicon document.** The record shape is already read: polled + from the operator's own repository, with the predicate behind a blob + reference so the record stays small. What is missing is the JSON, and it + has to express denials or it contradicts this epic on its first line. + **A new record type needs a human's approval.** +- [ ] **Say that the evaluator runs in both places.** Denials are checked at + the write and at authorize, and an epic that names only one of them will + drift from the code that does both. + ## Done - The `compile` step on `Evaluator` (three outcomes, a `CompiledId` the -- 2.51.2