# Decision request to gate-house — layer placement and approver-UI ownership **From:** informed-decision **Raised:** 2026-09-09 **Intake:** `INFD-IN-0001` **Blocks:** `INFD-WP-0001-T02`, and through it T05 and T07 **Standard:** `net-kingdom/canon/standards/security-layer-model_v0.7.md`, companion v0.2 --- ## 1. The gap being closed `approval-engine` names an approvals inbox under Non-Goals, deliberately and correctly — an engine that renders its own object is self-dealing, and the same reasoning kept the approval object out of `access-engine`. The consequence is that the surface a human actually touches has no owner. This is no longer theoretical: - `approval-engine` is a bearer-token resource server with no Ingress, no redirect endpoint and no browser client (`deploy/approval-engine.yaml` is Namespace + ClusterIP + StatefulSet). - On 2026-09-08 `key-cape` (`KEY-WP-0013-T02`) asked `approval-engine` for the human approver client's `client_id` and full callback URI. - `approval-engine` declined to invent them and recorded in `docs/keycape-service-registrations.md`: *"The human approver flow therefore belongs to whichever browser-facing approver UI presents `approval:approve` tokens to this engine. That component is not in this repo and is not named in `APPROVAL-WP-0002`."* - `APPROVAL-WP-0002-T01` remains `progress` in part because of it. `informed-decision` exists to take this. **We are not asking whether the work is ours to do. We are asking what layer the statute says we are, and asking it before we build, so the answer constrains the architecture rather than being retrofitted to it.** ## 2. What is being asked Three rulings, in dependency order. R3 is the one we least want to get wrong. ### R1 — Layer and role **Proposal:** `informed-decision` is **PEP-shaped** under statute §6.4 and owes companion §5. Reasoning: it is browser-facing and it causes a protected side effect on the far side of a decision — it submits an approval entry to `approval-engine` carrying an authenticated human's identity. `approval-engine`'s own INTENT anticipates exactly this class of caller: *"Callers that do are PEP-shaped and owe companion §5 / statute §6.4."* We are **not** proposing an Engine row. We hold no state another layer reads at runtime to reach a verdict. ### R2 — Does a presentation record make this a PIP as well? `informed-decision` produces one fact other layers may want as a claim: **what was presented, to whom, in what version** — carried as `view_hash`. We do not evaluate that fact; we emit it. **Proposal:** the presentation attestation is a PIP-like claim emitted by a PEP-shaped component, not a second catalog row. If the statute does not permit a component to be both, we ask which one governs and whether the presentation claim must instead be published as evidence to `audit-core` and read from there. We flag §17: the shared request-claim schema has **no assigned owner**. Whatever shape we publish must yield to that schema when it exists rather than becoming a permanent local invention — the same position `approval-engine` took in `APPROVAL-IN-0001`. ### R3 — `view_hash` versus the approval binding digest **This is the highest-risk item in the request and the reason it is filed before any code.** `approval-engine`'s claim already carries *"a digest over the same canonical binding the decision point already computes, so that approved and approved for this exact request are distinguishable at decision time."* `informed-decision` computes `view_hash`: a SHA-256 over a canonicalized *binding document* — the memo id and version, question, requested act, binding level, brief, locale, UI release, normalized packet and highlights, and the identity/scope being committed. Algorithm and vectors: `history/20260909-initial-exploration/canonicalize.md`. These are **not obviously the same hash**, and they are **not obviously different**. Both claim to canonicalize "the binding". They differ in what they cover: | | approval-engine binding digest | informed-decision `view_hash` | | --- | --- | --- | | Covers action, target, actor, principal, purpose | yes | yes, as `binding` | | Covers the brief, packet and highlights shown to the human | no | yes | | Covers locale and UI release | no | yes | | Exists when no human is in the loop | yes | no | | Purpose | replay protection at decision time | proof of what was presented | Three candidate rulings, with what each costs: - **(a) They are the same digest.** Cleanest for the estate — one canonicalization, one authority. Cost: the approval binding digest would have to cover presentation material that does not exist for machine-to-machine approvals, or `view_hash` would have to drop the packet and highlights, which destroys the property this repository exists to provide. - **(b) `view_hash` is a distinct presentation attestation carried alongside the binding digest.** Our proposal. Cost: two hashes over one act, requiring an explicit rule that the binding digest is authoritative for replay protection and `view_hash` is authoritative only for *what was shown*. Neither may be substituted for the other. - **(c) `view_hash` contains the binding digest as a field.** Composition rather than duplication; `view_hash` becomes a superset commitment. Cost: ordering dependency — the approval must exist before a memo can be rendered. **We propose (b) and will implement whichever is ruled.** What we ask gate-house to prevent is the outcome where both hashes ship without a stated relationship and the estate later discovers it has two canonicalizations of the same act with no rule for which one governs. ## 3. The self-dealing objection, stated against ourselves A surface that renders both the question and the answer sits close to the objection that kept the approval object out of `access-engine` and the inbox out of `approval-engine`. We think it is not the same failure, and we would rather argue it here than be told later: - `access-engine` could not own the approval object because an evaluator that owns what it evaluates can grade its own inputs. - `approval-engine` could not own the inbox because an engine that renders its own object controls the presentation of the evidence it issues. - `informed-decision` **holds no state any layer reads as authority.** It does not evaluate, does not issue the approval, and does not archive the trail. The presentation record is evidence *about* the act, produced by the party that performed the presentation — which is the only party that can produce it. The residual we accept and do not paper over: **a compromised surface can present X and attest Y.** `view_hash` is computed by the same component that renders. This is structurally the same residual `approval-engine` names for adversarial omission at a compromised source, and we make the same claim it does — atomicity and attestation cover accident and later tampering, not a compromised source. We do not claim otherwise, and we ask that any ruling not credit us with closing it. ## 4. What we are not asking - Not asking for approval doctrine. Which acts need approval, how many approvers, and which separations of duty apply stay `gate-house`'s. - Not asking to render decisions. No endpoint here answers "may this actor do X"; `access-engine` remains the only PDP. - Not asking to hold approval state. `approval-engine` remains the sole mutator, and `GH-DEC-2026-003` ordering is honoured unchanged. - Not asking for a Railiance operational axis. ## 5. What we do on each answer | Ruling | Consequence | | --- | --- | | R1 confirms PEP-shaped | `layer.yaml` written as such; companion §5 declared shapes enumerated in T05 | | R1 places us elsewhere | `INTENT.md`, `GOAL.md`, `SCOPE.md` amended to match; the ruling wins | | R2 permits a presentation claim | Published at our boundary, explicitly yielding to §17 | | R2 refuses it | Presentation evidence goes to `audit-core` only; no claim endpoint | | R3 = (a) | One digest; `view_hash` is retired or redefined as its input | | R3 = (b) | Both ship with a stated authority rule, recorded as a decision | | R3 = (c) | Composition; memo rendering is ordered after approval issuance | Until R1 and R3 are answered, `INFD-WP-0001-T05` (ArchitectureBlueprint) and T07 (the `key-cape` client registration) do not proceed. T03, T04 and T06 are independent of the ruling and continue. ## 6. Cost of delay `KEY-WP-0013-T02` is blocked today on two strings this repository now owns and cannot responsibly publish until it knows what it is. Registering a redirect URI for a component whose layer is unsettled is the drift statute §17 exists to prevent.