Rule on unknown and the scoping axis as GH-DEC-2026-009

access-engine exercised the divergence capability it claimed in the v0.6
round, on the first occasion §13.1 held two rows. ops-warden resolves
unknown to fail_open, secrets-engine to fail_closed; both conformant,
both total, both test-pinned, disagreeing about the one case that by
construction nobody planned for. They also scope over different axes, so
the register cannot answer what an inventory exists to answer.

Ruling 1: unknown is not a zone and MUST fail closed. §9.3 permits
trading availability for openness per zone — and that trade requires
knowing the zone. Where the scope is unknown the trade cannot have been
made for it, so a permissive unknown does not extend a considered
decision, it invents the most permissive one. An unreachable engine is a
known request in a degraded system; an unclassified subject is not.
unknown is the cheapest state for an attacker to induce, so failing open
on it makes being unclassifiable a privilege escalation requiring no
credential, which §8's asymmetry forbids wherever it appears.

Ruling 2: each map declares its scoping axis and its relation to zone.
Forcing everyone onto zones would make secrets-engine assert a zone it
cannot know, and a fiction in a runtime-read test-pinned file is worse
than an honest incommensurability. The register records the axes and
states that cross-axis aggregation is unavailable.

ops-warden acquires one non-conformant cell at v0.8. It did everything
asked — published first, built the reference form, offered it estate-wide
— so this goes to the assent round rather than being imposed quietly.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WtJBr77gMFLrN93iEevqQJ

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 425128@bnt-lap001
Assistant-Session: f5944d8b-dac4-4e1a-87eb-8b3d8f314a63
This commit is contained in:
tegwick 2026-09-06 14:18:36 +02:00
parent 45a65c0748
commit 22915cf2a0
3 changed files with 187 additions and 6 deletions

View file

@ -1112,3 +1112,110 @@ decided_by: Bernd Worsch
created: '2026-09-06T12:17:19.104531Z'
updated: '2026-09-06T12:17:19.104531Z'
```
## Context
On 2026-08-29 `access-engine` told this repository it was the party positioned to
notice when the aggregate of consumer stances diverged from what the policy packages
say — and then recorded that the claim was sound but the data was not there, because
§13.1 held one row and one row cannot diverge from anything. `secrets-engine`'s
publication made it two, and the divergence appeared immediately.
That is worth noting on its own: the register earned its existence the first time it
had two entries.
**`ops-warden` declares `unknown``fail_open`.** Explicit, versioned, under
`ADR-0009`. **`secrets-engine` declares `unknown``fail_closed`.** Both maps are
total, published, test-pinned, and §6.4-conformant. Neither is a defect under v0.7.
They disagree about the one case that by construction is the one nobody planned for.
They are also **not comparable**: `ops-warden` scopes by security zone,
`secrets-engine` by catalog stage — explicitly as the "equivalent scope" §6.4 permits,
until zone membership arrives as a claim. So the register cannot answer *"what is the
estate's stance for a `z2`-protected workload"*, which is the question an inventory
exists to answer.
`access-engine` reported both as observations and explicitly **not** as a request that
either repository change its stance, on the ground that a PDP does not set a
consumer's stance — §9.3's two-owner split, which was its own finding, cutting against
its own convenience. That restraint is correct and it is why the question arrives here:
setting a consumer's stance is not the PDP's to do, but doctrine is Gate House's.
## Decision
### 1. `unknown` is not a zone, and it fails closed
**A stance map MUST resolve `unknown` to `fail_closed`.** It is not a scope over which
the §9.3 availability trade may be made.
The reasoning is a distinction v0.7 does not draw, and the two maps are the proof it
is needed. §9.3 sanctions trading availability for openness **per zone** — knowingly,
for a named scope, at a declared cost. `ops-warden`'s `fail_open` for `z0``z2` is
exactly that and remains sanctioned: it is a *known* request in a *degraded* system.
The consumer knows what it is being asked and cannot reach the engine to ask.
`unknown` is a different failure. It is not a degraded system; it is an
**unclassified subject**. The consumer does not know what it is being asked.
**A per-zone trade requires knowing the zone.** Where the scope is unknown, the trade
cannot have been made *for* it — there was no zone in hand when the profile was
written, so no one weighed this request's cost. Resolving `unknown` to the permissive
answer therefore does not extend a considered decision; it invents one, and it invents
the most permissive one available.
The adversarial reading settles it. `unknown` is the cheapest state for an attacker to
induce: an unregistered workload, a malformed label, a resource created before
classification, a race against registry propagation. `fail_open` on `unknown` makes
"be unclassifiable" a privilege escalation, and it is the one escalation that requires
no credential. This is the §8 asymmetry — nothing may manufacture privilege — applied
where the estate had left a gap open by symmetry with a rule that does not reach it.
`ops-warden`'s map is conformant today and this makes one cell of it non-conformant at
v0.8. That is a doctrine change with a cost to a repository that did everything asked
of it — published early, built the reference form, and offered it estate-wide — and it
should be argued in the assent round rather than imposed quietly. Their `z0``z2`
zone stances are untouched.
### 2. A stance map declares its scoping axis and its relation to zone
**Each published map MUST name the axis it scopes over, and state its relationship to
security zone: a mapping, or an explicit declaration that none exists yet and why.**
The register is not repaired by forcing everyone onto zones. `secrets-engine` chose
catalog stage because zone membership is not yet available to it as a claim; making it
assert zones now would produce a fiction, and a fiction in a runtime-read,
test-pinned file is worse than an honest incommensurability.
**The register instead states what it cannot answer.** §13.1 carries the axis per row
and records that cross-axis aggregation is not available. An inventory that silently
cannot answer its own question is the §9.1 defect again — a surface catalogued without
the capability behind it.
`access-engine` notes the convergence path and it is the right one: zone *membership*
compiles into the registry snapshot it already consumes, while per-zone *stance*
belongs to the consumer. If membership reaches the decision as a claim,
`secrets-engine`'s axis converges onto zones with neither side inventing a mapping.
Until then the axes are recorded, not reconciled.
The cost of two incommensurable axes is small; the cost of five is not. This lands
before the third row rather than after the fifth.
### What is not decided here
Neither repository is asked to change its zone stances. `secrets-engine`'s stale
reference to a durable `ActionAuthorization` record in its map — the artifact
`GH-DEC-2026-005` shelved — is theirs to correct and `access-engine` has already told
them; it is a naming error in a runtime-read file, where a stale name outlives a stale
comment, and it does not change the stance.
## Reversal
Reverse ruling 1 if a consumer demonstrates a scope that is genuinely unknown *and*
genuinely low-consequence, where failing closed on it costs availability with no
security gain — the plausible candidate is a read-only diagnostic path under §5.1. The
expected resolution is that such a path is not a protected side effect and therefore
has no PEP obligation at all, rather than that it needs a permissive `unknown`. If
that resolution fails on a real path, this ruling is too broad.
Reverse ruling 2 if declaring the axis proves to be all the register needed, in which
case the "states what it cannot answer" half is redundant rather than wrong.