diff --git a/ArchitectureBlueprint.md b/ArchitectureBlueprint.md index 87954a1..438ba6c 100644 --- a/ArchitectureBlueprint.md +++ b/ArchitectureBlueprint.md @@ -560,7 +560,29 @@ Instances: `GH-DEC-2026-010` (a decision envelope reads identically whether `access-engine` issued it or a responder forged it), `GH-DEC-2026-011` (`unknown` versus `absent` in a stance map), `GH-DEC-2026-013` (a `tenant` claim directory-asserted about the principal versus registration-supplied about the -client), `GH-DEC-2026-014` (*erased* versus *never held* on an evidence path). +client, and — `key-cape`'s own extension — a third route where nobody asserted a +zone and a profile default supplied it), `GH-DEC-2026-014` (*erased* versus +*never held* on an evidence path). + +**Rider — who writes the marker.** A-16 is only as strong as the independence of +the party that writes the route marker from what the marker asserts. Where the +marker is written by the party whose conduct the route describes, **A-16 +constrains a defect but not an adversary, and MUST NOT be read as establishing the +distinction against a compromised source.** + +The four instances are not equally strong and the register should say so: a +signature by the issuer is independent of the party relying on it; a PEP labelling +its own input gains nothing by mislabelling; a tenant provenance written by the +issuer is independent of the consumer; and an *erased*-versus-*never-held* marker +written by the party the evidence is about is a **self-attestation**. The last +reduces to the residual `informed-decision` has twice been refused credit for +closing, and `GH-DEC-2026-014` §4 narrows it — non-production becomes attributable +— without removing it, because the attribution still rests on that party's own +marker. + +Without this rider A-16 becomes the thing it exists to prevent: a sound check read +as establishing a property it does not carry. Raised by `informed-decision` +against its own instance, which is the weak one. ### A-17 — Fail-closed transitions @@ -573,13 +595,30 @@ length of the transition, but the direction of failure at the distinguishing case. A promise that fails open is a permission; a promise that fails closed is a gap. +**Precondition — the distinguishing case must be observable first.** A-17 asks +which way a case fails, and that question is unanswerable where the case cannot be +seen. **Where the distinguishing case is not observable, making it observable is a +precondition of applying A-17, not an outcome of it.** Otherwise A-17 admits +anything whose distinguishing case is merely invisible, which reads as failing +closed because nothing visibly fails. + +**A-17 therefore depends on A-16.** One cannot ask which direction a case fails in +until the record can distinguish that case from its neighbours. + Instances: `GH-DEC-2026-011` (a dated transitional `unknown: fail_open` declined — its distinguishing case is exactly where it fails open, so the transition licenses the forbidden thing and dates it), `GH-DEC-2026-013` (a registration-bound tenant granted — registration and directory disagreeing refuses issuance rather than picking a winner), `GH-DEC-2026-014` (commitment-only -evidence granted — a reviewer who cannot obtain the content gets no -reconstruction rather than a wrong one). +evidence granted — but **only after** §4 made the case observable; as proposed it +had no failure direction at all, because a reviewer receiving a blank could not +tell *erased* from *withheld* from *lost* from *never held*). + +The third instance is why the precondition is stated. `GH-DEC-2026-014` §4 did not +**test** the direction of failure; it **manufactured** one, by requiring an +existence assertion that turns non-production into a finding. That was the right +outcome reached without noticing it was a different operation from the one A-17 +describes. Raised by `informed-decision`, from the instance it bears. **A-16 and A-17 are newer than A-01…A-15 and are not yet estate doctrine.** They are stated here because a property recorded only against the instance that diff --git a/INTENT.md b/INTENT.md index ca4ec7f..c9b0ed1 100644 --- a/INTENT.md +++ b/INTENT.md @@ -302,8 +302,8 @@ engines and Staff repositories implement them. 13. **Audit evidence is protected from the actor being audited.** 14. **Failure of critical policy or authorization dependencies fails closed.** 15. **Production guarantees must survive incorrect agent behavior.** -16. **Where one appearance is reachable by two routes, the record says which route.** -17. **A transitional deviation is admissible only where it fails closed on the case that distinguishes it from the conformant state.** +16. **Where one appearance is reachable by two routes, the record says which route** — and A-16 is only as strong as the marker-writer's independence from what the marker asserts. +17. **A transitional deviation is admissible only where it fails closed on the case that distinguishes it from the conformant state** — and where that case is not observable, making it observable is a precondition rather than an outcome. **On rules 16 and 17, which are newer than the rest.** Both were derived in September 2026 from rulings that kept arriving at the same shape, and they are @@ -324,6 +324,12 @@ that the two routes must behave differently — usually they must behave identic and safely. It is that the **record** must distinguish them, or the safer reading of the appearance becomes unavailable to every later reader. +**The rider matters as much as the rule.** Where the route marker is written by the +party whose conduct the route describes, rule 16 constrains a defect but not an +adversary. `informed-decision` raised this against its own instance — the weakest +of the four, an *erased*-versus-*never-held* marker written by the party the +evidence is about — and it is the difference between a rule and a reassurance. + **Rule 17** governs what may enter a declared-gap register. It exists because two requests for transitional relief arrived in one week and had to be answered oppositely without the answers looking arbitrary: `ops-warden`'s dated transitional @@ -334,6 +340,14 @@ which way it fails on the case that separates it from the conformant state. A promise that fails open is a permission; a promise that fails closed is a gap; only the second is a thing a register can hold. +**Rule 17 depends on rule 16**, which was not noticed when either was written. The +question *"which way does it fail"* is unanswerable where the case cannot be seen, +so an unobservable distinguishing case must be made observable before rule 17 is +applied at all — otherwise the rule admits anything merely invisible, which reads +as failing closed because nothing visibly fails. Raised by `informed-decision` +from the instance it bears, which is also the instance where this repository +manufactured a failure direction while believing it had tested one. + Neither rule has graduated to `net-kingdom/canon/standards/`. They are Gate House doctrine at repository level until they have been argued by a repository that bears a cost under them — which is how `security-layer-model` earned its acceptance and is diff --git a/README.md b/README.md index 379e1a8..f2be8df 100644 --- a/README.md +++ b/README.md @@ -140,7 +140,11 @@ The last two are September 2026 and are repository-level, not estate doctrine. 1 is why an `unknown` and an `absent` stance cell must be told apart in the record even though both fail closed, and why a decision envelope that looks authentic is not therefore attributable. 17 is why one request for transitional relief was -granted and another declined in the same week. See `INTENT.md` § Core Rules. +granted and another declined in the same week. Both were corrected within a day of +being written, by the repository that bears their weakest instance: 16 holds only +as far as the marker-writer is independent of what the marker asserts, and 17 +cannot be applied at all until the distinguishing case is observable. See +`INTENT.md` § Core Rules. ## Agentic operating modes diff --git a/decisions/decisions.md b/decisions/decisions.md index b94f325..8312a13 100644 --- a/decisions/decisions.md +++ b/decisions/decisions.md @@ -2260,3 +2260,342 @@ Raised by `informed-decision` (`INFD-IN-0003`), which proposed the shape, declar gap it leaves, refused a third option against its own convenience, and asked which half of the question was doctrine. The half that was is ruled here; the payload is `audit-core`'s and it has it. §4 was not requested by anyone. + +## GH-DEC-2026-015 — GH-DEC-2026-012 R3 revised: nesting is permitted for this pair, conditioned on the exclusion being normative + +```yaml +id: GH-DEC-2026-015 +kind: decision +title: 'GH-DEC-2026-012 R3 revised: nesting is permitted for this pair, conditioned + on the exclusion being normative' +status: resolved +owner: Bernd Worsch +repo: gate-house +standard: net-kingdom/canon/standards/security-layer-model_v0.8.md +source_note: informed-decision/docs/finding-r3-linkage-conflict.md (INFD-IN-0004); + approval-engine docs/approval-claim.md @ 62233c7 +requested_dispositions: +- approved +- revised +- rejected +affects: +- gate-house +- informed-decision +- approval-engine +- access-engine +- net-kingdom +decided_by: Bernd Worsch +rationale: 'GH-DEC-2026-012 R3 refused option (c) and required linkage by co-reference. + approval-engine, answering the same question from the layer that owns the digest, + recommended exactly (c). informed-decision could not comply with both and raised + it as a finding rather than choosing, which is the correct handling. The refusal + is revised on two grounds. First, the hash-cycle objection requires mutual containment, + and approval-engine has since published that its binding digest covers exactly five + act fields and that widening it to cover presentation would be a defect for an independent + reason - a new UI release would invalidate every prior approval - so one-directional + nesting raises no cycle. Second and decisively, co-reference by identifier alone + leaves informed-decision independently canonicalizing principal and target, two + of the five fields in the other layer digest, which is a partial recomputation of + one act in a second vocabulary and is closer to the translation R3 forbade than + nesting is. The original ruling therefore worked against its own rule. Nesting is + permitted for this pair, conditioned on approval-engine stating the presentation + exclusion as normative and testable rather than as design intent, because the cycle + cannot arise here is a belief and the cycle may not arise here is a rule with an + owner. The ordering objection is withdrawn as mistaken: the digest is over act material + and is computable before a human is presented anything.' +created: '2026-09-10T13:19:48.436344Z' +updated: '2026-09-10T13:19:48.436344Z' +``` + +## Context + +`GH-DEC-2026-012` R3 ruled that `view_hash` and `approval-engine`'s binding digest are +distinct attestations linked by **co-reference**, and refused option (c) — `view_hash` +carrying the binding digest as a field — on two grounds: an ordering dependency, and the +`GH-DEC-2026-008` hash cycle. + +`approval-engine` then answered the same question in `docs/approval-claim.md` (`62233c7`) +and recommended **exactly (c)**: that `informed-decision`'s binding document carry +`binding.digest` as a field rather than re-canonicalize `action`/`actor`/`principal`/ +`purpose`/`target` itself, so there is one canonicalization of the act, computed by the +layer that owns it. + +`informed-decision` could not comply with both, **made no design change**, and raised it +as a finding. That is the correct handling and it is worth naming: it declined to adopt a +recommendation that came from the layer owning the artifact, having previously offered to +let that layer settle R3 bilaterally, on the ground that a bilateral agreement produces +agreement rather than an authority rule. It applied that reasoning against its own +convenience twice. + +## Decision + +### 1. The refusal of (c) is revised. Nesting is permitted for this pair + +**`view_hash` MAY carry `binding.digest` as a field**, and `informed-decision`'s binding +slice then stops independently canonicalizing act material. One canonicalization of the +act, computed by the layer that owns the act, committed to by reference. + +### 2. The cycle objection does not hold here, and the reason it does not is narrow + +`GH-DEC-2026-008`'s cycle required **mutual containment**: a claim had to name the digest +of a request that would come to contain that claim. `approval-engine` has since published +that `binding.digest` covers exactly five act fields, and that widening it to cover +presentation material **would be a defect** — a new UI release would change the digest of +an act whose act did not change, invalidating every prior approval. So the containment is +one-directional and no cycle arises. + +`informed-decision` declined to assert that this settles it, on the ground that *"the +cycle cannot arise here"* is precisely the belief such failures punish. That caution is +correct and is why §4 exists rather than a bare permission. + +### 3. The decisive ground is that the original ruling worked against its own rule + +R3 required: *"You MUST NOT recompute or restate `approval-engine`'s binding digest from +your own vocabulary."* But `informed-decision`'s binding slice canonicalizes `principal` +and `target` — **two of the five fields in that digest**. Under co-reference by +identifier alone, two independent canonicalizations of one act exist, linked by a shared +id, and the presenting surface is performing a partial recomputation of the other layer's +material in its own vocabulary. + +**That is closer to the translation R3 forbade than nesting is.** Co-reference by id +*manages* the duplication with an authority rule; nesting *removes* it. Removing a +failure mode beats managing one, and the original ruling reached for the management +option while stating the rule that recommends the removal. + +This is the second time this month a Gate House ruling has been correct in substance and +wrong in the mechanism it prescribed, found by the repository that had to build on it. +The pattern is recorded rather than smoothed over: `GH-DEC-2026-008` mandated a +comparison that could never pass; this one mandated a linkage that reintroduced the thing +it forbade. + +### 4. Condition: the exclusion becomes normative, not intentional + +**Permission under §1 is conditioned on `approval-engine` stating, as a rule rather than +as design intent, that `binding.digest` MUST NOT cover presentation material** — and on +that constraint being asserted by a test, so that widening fails loudly rather than +silently. + +`approval-engine` already holds the reason, and it is a good one that does not depend on +this ruling: widening would invalidate every prior approval on a UI release. What it does +not yet hold is the *rule*. The difference is exactly A-17's precondition — the +distinguishing case here is *someone widens the digest*, and today that case is +unobservable until approvals start failing. Making it observable is a precondition of the +permission, not a follow-up to it. + +Until that lands, `GH-DEC-2026-012` R3's co-reference form remains in force. +`informed-decision` changes nothing on its own initiative; the permission activates when +the condition is met. + +### 5. What does not change + +- **The authority rule stands.** The binding digest is authoritative for what the request + *is*; `view_hash` is authoritative only for what was *shown*. Under §1 the two can no + longer disagree about the act, which retires the disagreement case rather than + reversing the rule that governed it. +- **(a) remains refused**, on its original and undisturbed ground: merging the two + canonicalizations would make one repository authoritative over another layer's + material. +- **`view_hash` still MUST NOT travel inside hashed request material while containing the + binding digest.** §2's one-directionality is a fact about the present pair, not a + licence. If a future design puts `view_hash` inside a hashed request, the cycle + reappears and this permission is void for that path. + +### 6. The ordering objection is withdrawn as mistaken + +`GH-DEC-2026-012` gave an ordering dependency as (c)'s first cost, taking it from +`informed-decision`'s own statement of it. It does not hold: `binding.digest` is computed +over **act material**, which is determined before a human is presented anything. The +approval need not exist before the memo is rendered — only the act need be determined, +which it is by construction. + +Recorded rather than dropped, because a cost accepted from the requester and never +checked is how a wrong reason survives into a ruling. + +## On the exposure `informed-decision` asked to have visible + +It asked that if co-reference stood, the record show that it would be the party a +disagreement was a finding against, for a disagreement arising from **two correct +canonicalizations** rather than from any defect of its own — the design creating the +exposure, not its conduct. + +That request is granted in a better form: §1 removes the exposure rather than documenting +it. The asymmetry it accepted was right in principle and it should not have had to carry +it in practice. + +## Reversal condition + +§1 reverses if `approval-engine`'s digest ever widens to cover presentation material, +which §4's rule exists to prevent and which would restore mutual containment. It also +reverses for any path that places `view_hash` inside hashed request material. + +## Provenance + +Raised by `informed-decision` (`INFD-IN-0004`), which made no design change, declined a +recommendation from the layer owning the artifact, applied Gate House's own bilateral- +settlement reasoning against its own convenience, and supplied the published-coverage +fact the original refusal did not have. `approval-engine` supplied the recommendation and +the structural reason behind it. + +## GH-DEC-2026-016 — Where an approval discharges a human-in-the-loop control, the approver must be a human principal and the engine must refuse at bind time + +```yaml +id: GH-DEC-2026-016 +kind: decision +title: Where an approval discharges a human-in-the-loop control, the approver must + be a human principal and the engine must refuse at bind time +status: resolved +owner: Bernd Worsch +repo: gate-house +standard: net-kingdom/canon/standards/security-layer-model_v0.8.md +source_note: informed-decision INFD-IN-0004 section 6 (NC-03); approval-engine, which + declined to invent the rule +requested_dispositions: +- approved +- revised +- rejected +affects: +- gate-house +- approval-engine +- informed-decision +- access-engine +- audit-core +- net-kingdom +decided_by: Bernd Worsch +rationale: 'approval-engine records the approver principal type as auditable metadata + but restricts only /consume by principal type, and its own operator service client + holds approval:approve, so a service principal can supply approver evidence for + a control whose purpose is to put human judgment in the path. informed-decision + enforces humans-bind-agents-draft at its own surface and correctly observed that + without an engine-side refusal its enforcement is one component policy rather than + a property of the approval object. Both referred the question up rather than settling + it. Ruled: where an approval is declared as discharging a human-in-the-loop or dual-control + requirement, the approver MUST be a human principal and approval-engine MUST refuse + a non-human bind at issue rather than record it. Recording the principal type after + the fact is detection and not prevention, which is the distinction this repository + applies everywhere else, and an approval control satisfiable by the same class of + actor it exists to check is theatre. The rule attaches to approvals declared as + discharging such a control rather than to all approvals, because service-to-service + approvals are legitimate and the requirement must be declared at issue and never + inferred - the same shape approval-engine established for pdp_path. Enforcement + at one surface is insufficient because a second caller reaching the engine directly + bypasses it, and the property belongs to the object rather than to whichever component + happens to create it.' +created: '2026-09-10T13:20:35.560626Z' +updated: '2026-09-10T13:20:35.560626Z' +``` + +## Context + +`approval-engine` put the question rather than answering it, which is the right instinct: + +> `entries[].principal_type` is the auditable half only. It tells you what bound an +> approval after the fact; it does not stop anything. If your doctrine needs a service +> principal refused at bind time, that is a `gate-house` question and we would implement +> a ruling — we simply will not invent one. + +`informed-decision` enforces *"humans bind, agents draft"* at its own surface and observed +that there is **no upstream backstop**: `approval-engine` restricts only `/consume` by +principal type, and its own operator service client holds `approval:approve`. It is not +asking for the refusal on its own account — its surface enforces it for its own callers +regardless. Its question is whether the estate wants *approver evidence itself* to be +human-only at the engine, in which case its enforcement is a property of the approval +object rather than one component's policy. + +Both referred it up rather than settling it between them. That is the same restraint that +produced `GH-DEC-2026-015`, and it is the reason this question is answerable at all. + +## Decision + +### 1. Where an approval discharges a human-in-the-loop control, the approver MUST be a human principal + +**And `approval-engine` MUST refuse a non-human bind at issue, rather than record it.** + +An approval control exists to put a judgment of a particular kind into the path of an +act. Where that kind is *human*, an approval bound by a service principal does not +weakly satisfy the control — it does not engage it at all. A control satisfiable by the +same class of actor it exists to check is theatre, and the estate's first rule is that +**no privilege comes from cognition**: an agent determining that an action is appropriate +never makes it authorized, and it does not become authorized because the agent recorded +its determination in an approval object. + +### 2. Recording is detection. Refusal is prevention. The distinction is not new here + +`entries[].principal_type` is exactly right as a record and is not the control. +`approval-engine`'s own framing — *"the auditable half only… it does not stop anything"* — +is the same distinction this repository applies to reconstructability at the issuer, to +audit completeness, and to `GH-DEC-2026-014`'s commitment-only records. Detection belongs +in the same register as the other residuals. It is not a substitute for a refusal that +can be made cheaply at issue. + +The bind is the cheap moment. Refusing at `/consume` is refusing after the approval has +been presented as satisfying a control it never satisfied, and after anything relying on +its existence has relied on it. + +### 3. The rule attaches to declared approvals, not to all approvals + +**Service-to-service approvals are legitimate** and this ruling does not disturb them. +The requirement attaches where an approval is **declared** as discharging a human-in-the- +loop or dual-control requirement. + +**Declared at issue, never inferred.** A human approver who happens to be present is not +a declaration, and an approval MUST NOT be retroactively read as human-discharging +because its entries turn out to be human. This is the shape `approval-engine` itself +established for `pdp_path`: intent is declared, `create()` refuses the declaration +without what it requires, legacy rows migrate to the negative rather than being +back-filled, and a successor inherits its predecessor's declaration. Back-filling would +manufacture a statement no requester made — `GH-DEC-2026-008`'s objection to translation, +in another form, and `approval-engine`'s own argument returned to it. + +Where the requirement is declared and the binding principal is not human, `create()` +refuses — the same failure-at-issue shape that already refuses `pdp_path: true` without a +`pdp_digest`, so an approval that would be unusable fails when it is made rather than at +the protected side effect. + +### 4. One surface enforcing it is not the property being held + +`informed-decision`'s enforcement is correct and insufficient, exactly as it said. A +second caller reaching `approval-engine` directly bypasses it, and the estate's operator +service client holds `approval:approve` today. **The property belongs to the approval +object, not to whichever component happened to create it** — otherwise the guarantee is +"human-approved unless someone used a different client", which is not a guarantee. + +`informed-decision` should keep its surface enforcement. Defence at the surface and at +the engine are not redundant: the surface refuses earlier and with a better error, and +the engine is what makes the refusal a property of the object. + +### 5. What this does not rule + +Not ruled: what makes a principal *human* — that is `key-cape`'s and the identity +layer's, and note it interacts with `GH-DEC-2026-013` §5. A principal-type claim is +subject to A-16 like any other: if `human` is reachable by two routes — asserted by the +directory about the person, or supplied by a registration about the client — the record +must say which, and a human-in-the-loop control MUST NOT be discharged on a +registration-supplied claim. Refusing a service principal while accepting an unverified +assertion of humanity would move the defect rather than close it. + +Not ruled: which acts require human approval. That is doctrine, it is Gate House's, and +it is not settled here — this record says what a *declared* human control requires, not +where such controls are mandatory. + +Not ruled: `approval-engine`'s scope model, or whether `approval:approve` on an operator +service client is itself correct. Flagged as worth their review, since it is the concrete +path by which the gap exists today. + +## Reversal condition + +§1 reverses if a case is found where a human-in-the-loop control is legitimately +discharged by a non-human principal — which would mean the control was misdeclared rather +than that the rule is wrong, and the correct repair is the declaration. + +§3's declared-only scoping reverses if declarations turn out to be systematically omitted +where they are needed, making the rule vacuous in practice. That would be evidence for +requiring the declaration rather than for widening the enforcement. + +## Provenance + +Raised by `approval-engine`, which named the gap in its own enforcement, stated plainly +that its own operator client holds the permission that makes it reachable, and declined +to invent the rule. Carried by `informed-decision`, which enforces the property already, +does not need the ruling for itself, and asked anyway because a property held by one +component is not a property of the object. Neither repository asked for anything that +benefits it.