Review the stance register's second row; record why T04 cannot proceed
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s

TWO FINDINGS, BOTH FROM READING OTHER REPOSITORIES RATHER THAN OURS.

The §13.1 stance register has a second row. secrets-engine published
pep-stance.yaml -- total over catalog stage plus unknown, runtime-read
and pinned to SHIPPED_STANCE by test. SCOPE.md claimed ops-warden's was
the estate's only published map and that the register's single row was
itself the finding; that is no longer true and is corrected.

docs/stance-register-review.md is the first exercise of the
aggregate-divergence capability flex-auth claimed on 2026-08-29 and then
recorded as unexercised because one row cannot diverge from anything.
Three findings:

The two maps take opposite stances on unknown -- ops-warden fail_open by
versioned build profile under ADR-0009, secrets-engine fail_closed. Both
conformant, neither a defect, and they disagree about the one case nobody
planned for. Reported as an observation for gate-house's register, not as
a request that either repository change: a PDP does not set a consumer's
stance, and §9.3's two-owner split is our own finding.

The rows are not comparable. ops-warden scopes by security-zone,
secrets-engine by catalog-stage. §6.4 permits both, but the register
cannot then answer what the estate's stance is for a z2 workload. Worth
recording before a third row arrives.

secrets-engine's map defines fail_closed in terms of a durable
ActionAuthorization record, which GH-DEC-2026-005 shelved. The stance is
unaffected -- only the artifact name is stale -- but the file is read at
runtime and pinned by test, so the stale name outlives a comment.

SEPARATELY, T04 IS BLOCKED AND THE REASON IS STRUCTURAL. The task assumed
the ops-warden/tenant-engine/user-engine pattern, where the pin's
default-deny NetworkPolicy admits one approved consumer workload.
secrets-engine has no Kubernetes deployment at all -- it is a CLI. There
is no pod selector to write, and inventing one would repeat the error
corrected in T02. Three possible shapes recorded in the workplan and
raised with them; callerAuth, not the NetworkPolicy, becomes the real
boundary if an operator CLI is the caller, so the FLEX-WP-0016 precedent
does not transfer unexamined.

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

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 412054@bnt-lap001
Assistant-Session: 3968fae1-8d59-4209-9bd6-c22594b8ab19
This commit is contained in:
tegwick 2026-09-06 09:31:40 +02:00
parent 591df344fb
commit c3ede0b494
3 changed files with 133 additions and 2 deletions

View file

@ -135,8 +135,15 @@ call itself. `ADR-0009` retires `policy.enabled` and the global `policy.fail_clo
outright and replaces them with a total per-zone stance map in the consumer PEP
(`ops-warden/pep-stance.yaml`): open for `z0`-`z2` and `unknown`, closed for
`z3-critical`. That map is the reference shape for `security-layer-model_v0.7`
§6.4 obligation 3, and it is the estate's only published one — §13.1's register
has one row, which the standard records as itself the finding.
§6.4 obligation 3. It is no longer the only published one: `secrets-engine`
published `pep-stance.yaml` (total over catalog stage plus `unknown`, runtime-read
and test-pinned), so §13.1's register now has **two rows** rather than the one the
standard recorded as itself the finding. flex-auth reviewed the pair in
`docs/stance-register-review.md` — the first exercise of the aggregate-divergence
capability it claimed on 2026-08-29 — and found that the two maps take opposite
stances on `unknown` (`fail_open` vs `fail_closed`) and scope on different axes
(security-zone vs catalog-stage), which makes the rows conformant but not
comparable.
flex-auth reviewed the zone model as the PDP on 2026-08-19 and holds one
boundary: zone **membership** compiles into

View file

@ -0,0 +1,101 @@
# Stance-register review — the register has a second row
Status: published
Date: 2026-09-06
Standard: `security-layer-model_v0.7` §6.4 obligation 3, §13.1
Reviewer: flex-auth (Engine / PDP)
## Why flex-auth is writing this
In the 2026-08-29 alignment review flex-auth told gate-house it is "the
repository positioned to notice when the aggregate of consumer stances diverges
from what the policy packages say," and then recorded that the claim was sound
but unexercised: §13.1's register existed with **one row**, and one row cannot
diverge from anything.
It now has two. This is that claim being exercised for the first time, and
flex-auth is reporting a divergence it found rather than waiting to be asked.
## The two published maps
| | `ops-warden` | `secrets-engine` |
| --- | --- | --- |
| File | `pep-stance.yaml` | `pep-stance.yaml` |
| Rule of record | `ADR-0009` | `INTENT.md` / `layer.yaml` |
| Protected action | SSH certificate issuance | OpenBao metadata/KV lifecycle and delivery |
| Scope axis | `security-zone` | `catalog-stage` |
| Pinned to code by test | yes (`PolicyConfig.failure_modes`) | yes (`SHIPPED_STANCE`) |
Both are §6.4-conformant. Both are total over their axis, both publish rather
than bury the map, both carry no implicit default, and both are asserted equal
to the shipped value by a test — which is the property that makes a published
map worth relying on at all.
## Finding 1 — the two maps take opposite stances on `unknown`
| | `unknown` |
| --- | --- |
| `ops-warden` | `fail_open` — explicit, versioned build profile, `ADR-0009` |
| `secrets-engine` | `fail_closed` |
Both are defensible and neither is a defect. `ops-warden`'s `unknown` is a
build-profile artifact on a path whose z3 rows are closed; `secrets-engine`
treats an unmapped stage as production-shaped. But the estate's only two
published maps disagree about the case that, by construction, is the one nobody
planned for.
That is the aggregate divergence flex-auth said it would notice. It is reported
as an observation for gate-house's register, **not** as a request that either
repository change. A PDP does not get to set a consumer's stance — §9.3's
two-owner split is flex-auth's own finding and it cuts this way too.
What is worth deciding, and it is gate-house's to decide, is whether §6.4 should
say anything about `unknown` specifically. Today it requires only that the map
be total; totality is satisfied by either answer.
## Finding 2 — the rows are not comparable, and the register cannot say so
`ops-warden` scopes by `security-zone`; `secrets-engine` scopes by
`catalog-stage`, explicitly "the equivalent scope until security-zone
membership arrives as a claim on the decision." §6.4's "per zone or equivalent"
permits both.
The consequence is that the register cannot answer the question an inventory
exists to answer. "What is the estate's stance for a `z2-protected` workload?"
has no answer for `secrets-engine`, because that repository has no zone axis —
and the mapping between a catalog stage and a zone is not published anywhere.
This is a property of the register rather than a fault in either map. It is
worth recording before a third row arrives, because the cost of two
incommensurable axes is small and the cost of five is not.
flex-auth holds a relevant boundary here, unchanged from 2026-08-19: zone
**membership** compiles into the registry snapshot flex-auth already consumes,
while per-zone **stance** belongs to the consumer. If zone membership becomes a
claim on the decision, `secrets-engine`'s axis can converge onto zones without
either side inventing a mapping.
## Finding 3 — `secrets-engine`'s map cites a shelved artifact
`pep-stance.yaml` defines `fail_closed` as "no protected side effect without a
durable access-engine / `ActionAuthorization` record."
`ActionAuthorization` was shelved on the PEP consumption path by
`GH-DEC-2026-005`, which flex-auth accepted in `FLEX-DEC-2026-006` — arguing
against its own proposal. The step-1 artifact is `approval-engine`'s
approval-claim; the step-2 artifact is flex-auth's `DecisionEnvelope`.
The stance itself is unaffected: `fail_closed` still means no protected side
effect without a durable record, and the records exist. Only the name of the
artifact is stale. Because this file is read at runtime and pinned by test, the
stale name is more durable than a comment would be, which is why it is worth a
line rather than being left.
## What flex-auth is not claiming
- No stance is wrong. flex-auth cannot express fail-open at all, and does not
get to grade a consumer's residue.
- No change is requested of `ops-warden`. Its `unknown: fail_open` is
documented, versioned, and reasoned.
- This is not a §13.1 register. gate-house owns the register; this is one
reviewer's reading of the two rows now in it.

View file

@ -178,6 +178,29 @@ Owner: `flex-auth`; deployment approval required.
Gate: the pin answers `POST /v1/check` for the secrets-engine workload and
default-denies every other source; the other two pins are unchanged.
**Blocked 2026-09-06 — the consumer is not a workload.** This task was written
from the `ops-warden` / `tenant-engine` / `user-engine` pattern, where the pin's
default-deny `NetworkPolicy` admits ingress from exactly one approved consumer
*workload*. `secrets-engine` has no Kubernetes deployment at all: it is a CLI
(`src/`, `cli.py`, `pyproject.toml`) with no manifests, no namespace, and no pod
labels. There is no selector to write, and inventing one would be the same error
corrected in `FLEX-WP-0021-T02` — authoring against a shape that does not exist.
Three shapes are possible and the choice is not flex-auth's alone:
1. **Operator-run CLI reaching an in-cluster pin.** Needs a decided ingress
path (tunnel or port-forward), and `callerAuth` becomes the real boundary
rather than the `NetworkPolicy`, because the source address is an operator's
machine rather than a pod.
2. **A `secrets-engine` workload that does not exist yet.** Then this task is
correct as written but waits on `secrets-engine` to have one.
3. **No pin at all**, if the gate is only ever consulted from an operator
context that already holds a decision.
Raised with `secrets-engine`; T04 does not proceed until the shape is decided.
Note this also changes what `callerAuth.mode: warn` is warning about, so the
`FLEX-WP-0016` precedent does not transfer unexamined.
## 5. Hand the pin coordinates back and close
```task