diff --git a/INTENT.md b/INTENT.md index ff26ce2..b8bf976 100644 --- a/INTENT.md +++ b/INTENT.md @@ -1,13 +1,22 @@ --- -# NetKingdom security layer declaration (security-layer-model_v0.7 §11). +# NetKingdom security layer declaration (security-layer-model §11). # Machine-readable because §11 requires it: prose cannot distinguish a # declaration from a transcribed review. Reference form: ops-warden layer.yaml. +# +# This declares a BOUNDARY, and carries no standard version on purpose. The +# standard draws the distinction itself: assent "records assent to a BOUNDARY, +# given at the version named. It is not assent to the current text." flex-auth is +# Engine / PDP at v0.6, v0.7, v0.8 and after; the role does not change when the +# text is amended, so a version here would make every revision read as though it +# invalidated the declaration. Version-scoped conformance state is derived, moves, +# and lives in conformance_record below — which carries the version it was +# derived at, as §11 requires of a derived artifact. layer: Engine role: PDP framework: netkingdom-security-layer-model -standard_version: "0.7" declared_by: decisions/decisions.md FLEX-DEC-2026-001, FLEX-DEC-2026-002, FLEX-DEC-2026-003 declared_at: "2026-08-29" +conformance_record: docs/conformance/security-layer-conformance.md pep_stance: null # not PEP-shaped: flex-auth renders decisions, it causes no protected side effect tooling_contacts: [] # §5 binds Staff; flex-auth holds no Tooling client --- @@ -15,8 +24,8 @@ tooling_contacts: [] # §5 binds Staff; flex-auth holds no Tooling client # Flex-Auth Intent > **Layer declaration.** flex-auth is **Engine / PDP** under the NetKingdom -> Security Layer Model (`net-kingdom/canon/standards/security-layer-model_v0.7.md`, -> accepted 2026-08-29). It is **the only policy decision point in NetKingdom** +> Security Layer Model (`net-kingdom/canon/standards/`, the Security +> Layer Model; declared 2026-08-29). It is **the only policy decision point in NetKingdom** > (§6): no other repository, in any layer, renders or caches an authorization > decision. The frontmatter above is the machine-readable form §11 requires; this > paragraph is the declaration in flex-auth's own voice. @@ -38,7 +47,7 @@ tooling_contacts: [] # §5 binds Staff; flex-auth holds no Tooling client > three records: `FLEX-DEC-2026-001` (Engine framing, the `access-engine` name, > the authoring/evaluation split), `FLEX-DEC-2026-002` (§9.3 contested and > upheld), and `FLEX-DEC-2026-003` (v0.6 review; §6.4.2, §9.7.2 and §17 as -> adopted in v0.7). +> adopted into the standard). > > *One item remains open: the ruled rename to `access-engine`. It is a separate > governed migration — it touches `FLEX-WP` prefix ownership, State Hub @@ -62,7 +71,7 @@ enterprise-grade authorization without giving up clear ownership, local development ergonomics, or inspectable policy decisions. It is an **Engine**, role **PDP**, in the NetKingdom security layer model -(`net-kingdom/canon/standards/security-layer-model_v0.7.md`): a deterministic +(`net-kingdom/canon/standards/`, the Security Layer Model): a deterministic API for a modeled concept, where the same authoritative input state yields the same result. diff --git a/SCOPE.md b/SCOPE.md index 3bcb08b..2cf2ce3 100644 --- a/SCOPE.md +++ b/SCOPE.md @@ -4,8 +4,8 @@ flex-auth is a policy-as-code authorization registry and decision engine for NetKingdom-aligned systems. Under the NetKingdom Security Layer Model -(`security-layer-model_v0.7`, accepted 2026-08-29) it is **Engine / PDP** — the -estate's only policy decision point. +(declared 2026-08-29) it is **Engine / PDP** — the estate's only policy decision +point. ## Core Idea @@ -36,7 +36,7 @@ can be coordinated behind a stable flex-auth API. - Delegated PDP and directory adapter boundaries after the standalone core is stable. - The **decision-record schema**, published as flex-auth's own contract - (`security-layer-model_v0.7` §17). + (Security Layer Model §17). - The **canonical request digest** as the published replay test for consumers (§6.4.2) — `binding.submitted_request_digest`, over the request as sent. `request_digest` is over the *enriched* request and is flex-auth's own @@ -152,11 +152,13 @@ certificates the ops-bridge tunnels depend on, one of which carries the policy call itself. `ADR-0009` retires `policy.enabled` and the global `policy.fail_closed` 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` +`z3-critical`. That map is the reference shape for the Security Layer Model's §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 +and test-pinned), so §13.1's register grew past the single row the standard +recorded as itself the finding. It now carries **five** — `ops-warden`, +`user-engine`, `tenant-engine`, `secrets-engine`, and `ops-mason` with no map at +all — two of them marked non-conformant. 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 @@ -172,15 +174,52 @@ PDP at all** and stays a consumer-side setting. The enforcing pins stay enforcing regardless — an enforcing pin with no enforcing consumer still turns the anonymous path into a 401 rather than a decision. -**Layer position (2026-08-29).** The NetKingdom Security Layer Model was -accepted at v0.7. flex-auth is **Engine / PDP**, declared machine-readably in -`INTENT.md` frontmatter per §11. Four of flex-auth's review findings were adopted -into the standard: §9.3's two-owner split for degraded mode, §6.4.2 scoped to the -decision's own binding with the canonical request digest as its mechanical test -and negative caching ruled explicitly, §9.7.2 split by role, and §17 moving the -decision-record schema to flex-auth. Conformance state is **conforming with one -declared gap** — the registry-snapshot digest in decision provenance, which §9.7.2 -promotes to a conformance prerequisite. `FLEX-WP-0019` carries the work. +**Layer position (declared 2026-08-29).** flex-auth is **Engine / PDP** under +the NetKingdom Security Layer Model, declared machine-readably in `INTENT.md` +frontmatter per §11. That declaration deliberately carries **no standard +version**: a layer is a boundary and does not change when the text is amended, +which is the distinction the standard itself draws when it says assent is *"to a +BOUNDARY, given at the version named. It is not assent to the current text."* The +absence is enforced by test in `internal/layer`, and raised to `gate-house` as +`FLEX-WP-0030` B4 in case §11 should say so generally. + +Version-scoped state therefore lives in +`docs/conformance/security-layer-conformance.md` — a derived artifact that names +what it derives from and the version it was derived at, as §11 requires. It +carries the assent history (v0.4 `FLEX-DEC-2026-002`, v0.6 `FLEX-DEC-2026-003`, +v0.8 `FLEX-DEC-2026-011`) and the gap table below. + +Findings of flex-auth's adopted into the standard: §9.3's two-owner split for +degraded mode, §6.4.2 scoped to the decision's own binding with the canonical +request digest as its mechanical test and negative caching ruled explicitly, +§9.7.2 split by role, §17 moving the decision-record schema to flex-auth, and +four more in the v0.8 round (`FLEX-DEC-2026-011`), of which F1 — that a decision +must be *attributable* to flex-auth and that a digest comparison does not +discharge it — was the finding of the round. + +Conformance state is **conforming with three declared gaps**, each with an owner +and a route rather than a sentence: + +| # | Gap | Owner | Route | +| --- | --- | --- | --- | +| G1 | Registry-snapshot digest absent from decision provenance (§9.7.2 conformance prerequisite) | `flex-auth` | `FLEX-WP-0019` | +| G2 | No emission guarantee declared, where §11 requires one of every §4 source of evidence | `gate-house` to rule, then `flex-auth` | `FLEX-WP-0030` B3 | +| G3 | Published stance-register review stale — written at two register rows, §13.1 now carries five | `flex-auth` | `FLEX-WP-0029` | + +G2 is new and is recorded as a gap rather than as conformance on purpose: whether +flex-auth is a §4 *source of evidence* or only the producer of an artifact +`audit-core` sources has been asserted by nobody but flex-auth, and §11 says a +source declaring no emission guarantee is not conforming. The conservative entry +is the honest one until the boundary is ruled. + +**Boundaries review (2026-09-21).** `FLEX-WP-0030` reviews flex-auth's boundary +against every security-relevant counterpart and raises five items: inconsistent +`layer:` casing across the estate against §11's mechanical-checkability claim +(B1); four security-relevant repositories with no layer declaration at all — +`gate-house`, `key-cape`, `ops-mason`, `net-kingdom` — of which `key-cape` is the +identity source whose claims flex-auth consumes as normative input, making that +boundary asserted from one side only (B2); G2 above (B3); B4; and canon naming +`access-engine` while the repository still answers to `flex-auth` (B5). State Hub integration is present through: @@ -297,12 +336,12 @@ description: Workplan for Topaz evaluation, relationship PDP adapters, rule PDP type: contract title: Decision-record contract status: current -description: Published decision-record schema, the submitted-request digest as the replay test for consumers, request-enrichment precedence, and decision provenance — flex-auth's own contract under security-layer-model v0.7 section 17. +description: Published decision-record schema, the submitted-request digest as the replay test for consumers, request-enrichment precedence, and decision provenance — flex-auth's own contract under the Security Layer Model section 17. ``` ```capability type: orientation title: Layer declaration status: current -description: Machine-readable Engine/PDP declaration in INTENT.md frontmatter with conformance state and the one declared gap, per security-layer-model v0.7 section 11. +description: Machine-readable Engine/PDP boundary declaration in INTENT.md frontmatter, carrying no standard version by design, with version-stamped conformance state and three declared gaps in docs/conformance/security-layer-conformance.md, per Security Layer Model section 11. ``` diff --git a/docs/conformance/boundaries-review.md b/docs/conformance/boundaries-review.md new file mode 100644 index 0000000..3781aa6 --- /dev/null +++ b/docs/conformance/boundaries-review.md @@ -0,0 +1,68 @@ +# Boundaries review — flex-auth against its security-relevant counterparts + +> **Derived artifact** (§11). Derives from +> `net-kingdom/canon/standards/security-layer-model_v0.8.md` +> (`status: proposed`, `source_revision: gate-house@516ed4e`), from each +> counterpart's `INTENT.md` / `layer.yaml` as of the date below, and from +> flex-auth's own `decisions/decisions.md`. +> Derived at: **2026-09-21**. Reviewer: `flex-auth` (Engine / PDP). +> Carried by `FLEX-WP-0030`. + +## Why this exists + +`INTENT.md` was carrying a standard version it should not (`FLEX-WP-0030` T01). +Fixing one line meant reading what the declaration actually asserts, and the +declaration asserts a boundary — which is only half of a boundary. The other half +is held by someone else, and flex-auth had never checked whether the other halves +were stated at all. + +They are not, in four cases. That is the finding of this review, and it was not +the one being looked for. + +## The rule this review holds itself to + +§11 is explicit: *"A layer stated **about** a repository by another repository is +not a declaration."* So nothing below grades a peer. flex-auth reports what it can +observe from outside, names the owner, and says which of three states the boundary +is in. **`unstated` is not recorded as `agreed`** — that conflation is the whole +reason the four cases below went unnoticed. + +## Boundary status + +| Counterpart | Boundary | State | +| --- | --- | --- | +| `gate-house` | owns invariants, authority ceilings, operating modes; flex-auth authors no doctrine and consumes authority context as claims | **agreed in substance, undeclared in form** — no `layer:` key (B2) | +| `key-cape` / NetKingdom SSO | identity source; flex-auth consumes verified claims as normative input and never redefines them | **asserted from one side only** — no `layer:` key (B2) | +| `ops-warden` | consumer / PEP; owns SSH CA, OpenBao signing, actor inventory and its own stance map. flex-auth owns the decision, never the credential | **agreed, and the most exercised boundary in the estate** | +| `secrets-engine` | consumer / PEP; owns credential lifecycle downstream of a decision | **agreed** | +| `approval-engine` | owns the approval object, its storage and lifecycle; flex-auth validates approvals as input claims and never mutates them | **agreed** — `GH-DEC-2026-005`, accepted in `FLEX-DEC-2026-006` against flex-auth's own proposal | +| `zone-engine` | zone **membership** compiles into the registry snapshot flex-auth consumes; per-zone **stance** is the consumer's | **held by flex-auth since 2026-08-19; not contested, not confirmed** | +| `user-engine` | PIP; supplies subject facts | **agreed** | +| `tenant-engine` | PIP and consumer. What `CheckRequest.tenant` denotes on the write API — caller, target, or guardrail scope — is **unanswered since 2026-09-15** | **unclear, and live** — `FLEX-WP-0022`, re-asked 2026-09-20 | +| `audit-core` | records evidence; flex-auth produces the decision record | **unclear** — which of the two is the §4 *source of evidence* decides whether flex-auth owes an emission guarantee (B3 / G2) | +| `maturity-engine` | PIP; supplies maturity claims. §9.5 forbids ranking blocked-clean below conforming | **agreed** | +| `kings-guard` | Staff; raised the authentication/assurance evidence gap that flex-auth **declined** (§13, `FLEX-DEC-2026-002`) | **agreed, by mutual declining** | +| `ops-mason` | catalogued PEP-shaped in §4 | **undeclared twice over** — no stance map (§13.1 marks it), no `layer:` key (B2) | + +## Findings raised for resolution + +Full statements in `workplans/FLEX-WP-0030-boundary-declaration-cleanup.md` T04. + +| # | Finding | Owner | +| --- | --- | --- | +| B1 | `layer:` casing is inconsistent across ten declarations; §11 calls the check mechanical, and flex-auth's own validator would fail eight of them | `gate-house` | +| B2 | `gate-house`, `key-cape`, `ops-mason`, `net-kingdom` carry no machine-readable layer declaration | each named repository | +| B3 | flex-auth declares no emission guarantee; whether it owes one turns on an unruled boundary with `audit-core` | `gate-house`, `audit-core` | +| B4 | a layer declaration should not pin a standard version; if §11 agrees it should say so generally | `gate-house` | +| B5 | canon names `access-engine`; the repository still answers to `flex-auth` and the rename has not landed | `gate-house` to note | + +## What flex-auth is not claiming + +- **Not a conformance finding against anyone.** B1 and B2 are observations about + form. A repository may be entirely conformant in substance and simply not have + written the key; `gate-house` is the clearest case. +- **Not a request that any peer change casing.** B1 may equally be resolved by + ruling the vocabulary case-insensitive, which would make flex-auth's validator + the thing that changes. +- **Not a grade.** §9.3's two-owner split is flex-auth's own finding and it cuts + here: the PDP does not get to score the repositories whose facts it consumes. diff --git a/docs/conformance/security-layer-conformance.md b/docs/conformance/security-layer-conformance.md new file mode 100644 index 0000000..d943a4c --- /dev/null +++ b/docs/conformance/security-layer-conformance.md @@ -0,0 +1,72 @@ +# Security Layer Model — flex-auth conformance record + +> **Derived artifact** (§11). Derives from +> `net-kingdom/canon/standards/security-layer-model_v0.8.md` +> (`status: proposed`, `source_revision: gate-house@516ed4e`). +> Derived at: **2026-09-21**. Maintained by `flex-auth`. + +This file exists because `INTENT.md` deliberately does not carry one. The layer +declaration states a **boundary** — flex-auth is Engine / PDP — and a boundary +does not change when the standard text is amended. Conformance state does change, +every revision, and belongs in an artifact that is expected to move and that +carries the version it was derived at. + +The split is the standard's own. Its frontmatter: *"`assented_by` records assent +to a BOUNDARY, given at the version named. It is not assent to the current text."* + +## Declaration + +| Field | Value | +| --- | --- | +| Layer | `Engine` | +| Role | `PDP` — the estate's only policy decision point (§6) | +| Declared at | 2026-08-29 | +| Declared by | `FLEX-DEC-2026-001`, `FLEX-DEC-2026-002`, `FLEX-DEC-2026-003` | +| PEP stance | `null` — not PEP-shaped; flex-auth renders decisions and causes no protected side effect | +| Tooling contacts | none — §5 binds Staff; flex-auth holds no Tooling client | + +## Assent history + +Assent is to the boundary, recorded at the version where it was given. + +| Version | Record | Outcome | +| --- | --- | --- | +| v0.4 | `FLEX-DEC-2026-002` | §9.3 contested and upheld | +| v0.6 | `FLEX-DEC-2026-003` | review; §6.4.2, §9.7.2, §17 adopted into v0.7 | +| v0.7 | — | four flex-auth findings adopted | +| v0.8 (`proposed`) | `FLEX-DEC-2026-011` | assent with four findings, all adopted | + +## Conformance state at v0.8 + +**Conforming, with declared gaps below.** §11's four states: conforming, +blocked-clean, declared gap, undeclared violation. flex-auth claims no +blocked-clean capability and holds no undeclared violation it is aware of. + +| # | Gap | Owner | Blocked on | Review | +| --- | --- | --- | --- | --- | +| G1 | Registry-snapshot digest absent from decision provenance. §9.7.2 promotes it to a conformance prerequisite: a decision that turned on registry content must be replayable from its own record. | `flex-auth` | implementation | `FLEX-WP-0019` | +| G2 | **Emission guarantee not declared.** §11 requires every §4 repository catalogued as a source of evidence to declare class, cadence and detection surface in its machine-readable declaration, and says a source declaring none is not conforming. flex-auth declares none. | `gate-house` to rule, then `flex-auth` | whether flex-auth is a §4 *source of evidence* or only the producer of an artifact `audit-core` sources — see `FLEX-WP-0030` B3 | `FLEX-WP-0030-T04` | +| G3 | Published stance-register review is stale: written at two register rows, §13.1 now carries five, and its Finding 1 was ruled by v0.8 §6.4 obligation 3. | `flex-auth` | second edition | `FLEX-WP-0029` | + +G2 is recorded as a gap rather than as conformance because the flattering reading +— that `audit-core` is the source and flex-auth merely produces — has not been +confirmed by anyone but flex-auth. §11 says a source that declares no emission +guarantee is not conforming; until the boundary is ruled, the conservative entry +is the honest one. + +## Not gaps + +- **No standard version in `INTENT.md`.** Deliberate, enforced by test + (`internal/layer`), and raised to `gate-house` as `FLEX-WP-0030` B4 in case §11 + should say so generally. +- **Canon says `access-engine`, the repository answers to `flex-auth`.** The + rename is ruled (`FLEX-DEC-2026-013`) and sequenced (`FLEX-WP-0020`); the + coordinate is pending, not broken. Raised as B5. + +## How this file is kept true + +`internal/layer` asserts that `INTENT.md` carries no `standard_version` and names +a `conformance_record` that exists on disk. That is the same property flex-auth +praised in `ops-warden`'s stance map: the published artifact is asserted equal to +the shipped one by test, rather than merely written down. It does **not** assert +the contents below the declaration — a reviewer still has to read this file. diff --git a/internal/layer/conformance.go b/internal/layer/conformance.go index f65cabd..42a561e 100644 --- a/internal/layer/conformance.go +++ b/internal/layer/conformance.go @@ -37,9 +37,15 @@ type Declaration struct { Layer string `yaml:"layer"` Role string `yaml:"role"` Framework string `yaml:"framework"` - StandardVersion string `yaml:"standard_version"` DeclaredBy string `yaml:"declared_by"` DeclaredAt string `yaml:"declared_at"` + // ConformanceRecord points at the derived, version-stamped conformance state. + // The declaration itself is a boundary and carries no standard version. + ConformanceRecord string `yaml:"conformance_record"` + // StandardVersion must stay empty: a layer is a boundary, and the standard + // says assent is "to a BOUNDARY, given at the version named. It is not + // assent to the current text." + StandardVersion string `yaml:"standard_version"` PepStance any `yaml:"pep_stance"` ToolingContacts []any `yaml:"tooling_contacts"` } @@ -98,6 +104,12 @@ func ValidateDeclaration(decl Declaration) error { if decl.PepStance != nil { return fmt.Errorf("flex-auth is not PEP-shaped; pep_stance must be null") } + if strings.TrimSpace(decl.StandardVersion) != "" { + return fmt.Errorf("layer declaration must carry no standard_version: a layer is a boundary, not a version-scoped claim; version-stamped state belongs in conformance_record (got %q)", decl.StandardVersion) + } + if strings.TrimSpace(decl.ConformanceRecord) == "" { + return fmt.Errorf("layer declaration must name a conformance_record: §11 requires a derived artifact to carry the version it was derived at") + } return nil } diff --git a/internal/layer/conformance_test.go b/internal/layer/conformance_test.go index 5f4dba0..77df515 100644 --- a/internal/layer/conformance_test.go +++ b/internal/layer/conformance_test.go @@ -1,6 +1,7 @@ package layer_test import ( + "os" "path/filepath" "runtime" "testing" @@ -27,8 +28,28 @@ func TestLayerDeclarationConforms(t *testing.T) { if decl.Framework != "netkingdom-security-layer-model" { t.Fatalf("framework = %q", decl.Framework) } - if decl.StandardVersion != "0.7" { - t.Fatalf("standard_version = %q; want 0.7", decl.StandardVersion) + // The declaration is a boundary and must NOT pin a standard version: the + // role does not change when the standard text is amended. Version-scoped + // conformance state lives in the derived record named below. + if decl.StandardVersion != "" { + t.Fatalf("standard_version = %q; want empty (boundary, not version-scoped)", decl.StandardVersion) + } + if decl.ConformanceRecord == "" { + t.Fatal("conformance_record is empty; version-stamped state must have a home") + } + if _, err := os.Stat(filepath.Join(repoRoot(t), decl.ConformanceRecord)); err != nil { + t.Fatalf("conformance_record %q does not exist: %v", decl.ConformanceRecord, err) + } +} + +func TestVersionPinInDeclarationIsRejected(t *testing.T) { + err := layer.ValidateDeclaration(layer.Declaration{ + Layer: "Engine", Role: "PDP", + ConformanceRecord: "docs/conformance/security-layer-conformance.md", + StandardVersion: "0.8", + }) + if err == nil { + t.Fatal("a standard_version pin in the boundary declaration was accepted") } } diff --git a/workplans/FLEX-WP-0030-boundary-declaration-cleanup.md b/workplans/FLEX-WP-0030-boundary-declaration-cleanup.md new file mode 100644 index 0000000..050ff72 --- /dev/null +++ b/workplans/FLEX-WP-0030-boundary-declaration-cleanup.md @@ -0,0 +1,223 @@ +--- +id: FLEX-WP-0030 +type: workplan +title: "The layer declaration pins a version it should not, and four security-relevant peers do not declare at all" +domain: infotech +repo: flex-auth +status: ready +flavor: review +owner: claude +topic_slug: netkingdom +planning_priority: P1 +planning_order: 300 +related_workplans: + - FLEX-WP-0019 + - FLEX-WP-0029 +created: "2026-09-21" +updated: "2026-09-21" +--- + +# FLEX-WP-0030 — Boundary declaration cleanup and estate boundaries review + +`INTENT.md` declares `standard_version: "0.7"` in the frontmatter that §11 +requires. That conflates two things the standard itself separates. The standard's +own frontmatter says it outright: + +> `assented_by` records assent to a **BOUNDARY**, given at the version named. +> It is not assent to the current text. + +A layer is a boundary. flex-auth is Engine / PDP whether the standard is at v0.6, +v0.7, v0.8 or v1.0 — the role does not change when the text is amended. Pinning a +version in the boundary declaration makes every standard revision look like it +invalidates the declaration, and generates exactly the churn seen this month: +v0.8 lands, the file still says `0.7`, and a reader cannot tell whether that is +staleness or a deliberate position. + +**It is also decorative.** `internal/layer/conformance.go:40` parses +`standard_version` into `Declaration.StandardVersion` and `ValidateDeclaration` +never reads it. The field is enforced by nothing, in a file whose entire purpose +is being mechanically checkable. + +## 1. Make the declaration version-agnostic + +```task +id: FLEX-WP-0030-T01 +status: todo +priority: high +``` + +Owner: `flex-auth`. + +- Drop `standard_version` from the `INTENT.md` frontmatter. §11 requires a + `layer:` key; it does not require a version, and flex-auth's own validator + does not check one. +- Replace versioned prose references (`security-layer-model_v0.7.md`) with the + unversioned standard path. `INTENT.md` is declared "aspirational and stable" + in its own words; a stable file should not carry a number that moves quarterly. +- Move version-scoped conformance state out of `INTENT.md` into a conformance + record that is *supposed* to move: which version was reviewed, the conformance + state, and each declared gap with owner, blocker and review date (§11's + four-state table). +- That split also satisfies §11's derived-artifact rule, which requires a + derived artifact to name what it derives from **and the version it was derived + at**. The boundary declaration is not derived; the conformance record is, and + it is the artifact that should carry the version. +- Keep `internal/layer` green, and decide whether `StandardVersion` stays as a + tolerated-but-unused field or is removed. Do not leave a parsed field that + nothing validates without saying which it is. + +Gate: `INTENT.md` names no standard version; conformance state is version-stamped +somewhere that is maintained; `go test ./internal/layer/...` passes. + +## 2. Update SCOPE.md and assess the gaps + +```task +id: FLEX-WP-0030-T02 +status: todo +priority: high +``` + +Owner: `flex-auth`. + +`SCOPE.md`'s Current State is accurate but drifting: it says §13.1's register has +**two rows** (it has five — `FLEX-WP-0029` owns that), and it states conformance +as "conforming with one declared gap" without naming where the gap list is +maintained. + +Assess and state, per gap: what it is, who owns it, what unblocks it, and when it +is next reviewed. Known candidates: + +| Candidate gap | State | +| --- | --- | +| Registry-snapshot digest in decision provenance | declared, `FLEX-WP-0019`, §9.7.2 conformance prerequisite | +| Emission guarantee not declared (see T04 / B3) | **unassessed** — may be a second declared gap | +| Stance-register review stale at five rows | `FLEX-WP-0029` | + +Gate: every gap in `SCOPE.md` has an owner and a route; no gap is described only +as a sentence. + +## 3. Publish the boundaries review + +```task +id: FLEX-WP-0030-T03 +status: todo +priority: high +``` + +Owner: `flex-auth`. + +Review flex-auth's boundary against every security-relevant repository it names +or is named by, and publish findings as a derived artifact marked with the +version it was derived at. Five findings are already identified in T04; the +review is the durable form of them. + +Cover at minimum: `gate-house` (doctrine), `key-cape` (identity claims in), +`ops-warden`, `secrets-engine`, `user-engine`, `tenant-engine`, `zone-engine`, +`approval-engine`, `audit-core`, `maturity-engine`, `kings-guard`, `ops-mason`. + +Gate: the review states, for each counterpart, whether the boundary is agreed, +contested, or unstated — and does not record "unstated" as if it were "agreed". + +## 4. Raise the conflicting and unclear boundaries for resolution + +```task +id: FLEX-WP-0030-T04 +status: todo +priority: high +``` + +Owner: `flex-auth` to raise; the named owner resolves each. + +### B1 — `layer:` case is inconsistent, and §11 calls the check mechanical + +| Value written | Repositories | +| --- | --- | +| `Engine` | `flex-auth`, `maturity-engine` | +| `engine` | `secrets-engine`, `approval-engine`, `user-engine`, `tenant-engine`, `zone-engine`, `audit-core` | +| `staff` | `ops-warden`, `kings-guard` | + +flex-auth's own validator rejects anything outside `{Staff, Engine, Tooling}` +(`internal/layer/conformance.go:86`), so run across the estate it would fail +eight of ten declarations on casing alone. Either the §3 vocabulary is +case-insensitive and every checker MUST fold case, or it is not and eight +declarations are non-conformant. Today each repository's checker only reads its +own file, so the disagreement is invisible by construction. + +Owner: `gate-house`. This is the §11 mechanical-checkability claim meeting its +first cross-repository run. + +### B2 — four security-relevant repositories carry no layer declaration + +`gate-house`, `key-cape`, `ops-mason`, and `net-kingdom` have no `layer:` key in +`INTENT.md` and no equivalent declaration file. §11 requires one of every +estate-authored repository in §4, and is explicit that a layer stated *about* a +repository by another repository is not a declaration. + +Two matter directly to flex-auth: + +- **`key-cape`** is the identity source whose verified claims flex-auth consumes + as normative input and never redefines. That boundary is load-bearing for + every decision flex-auth renders, and it is asserted only from flex-auth's + side. +- **`gate-house`** authors the obligation. A doctrine owner that has not + discharged its own §11 obligation is the standard's §9.1 defect pointed at + itself. + +`ops-mason` is already marked non-conformant in §13.1 for publishing no stance +map; a missing layer declaration is the same gap one level up. + +Owner: each named repository. flex-auth raises, does not grade. + +### B3 — flex-auth declares no emission guarantee, and may owe one + +§11 requires every repository catalogued in §4 **as a source of evidence** to +declare its emission guarantee — class, cadence, and detection surface — in its +machine-readable layer declaration, and says a source that declares none is not +conforming. + +flex-auth produces the decision record, which §17 moved to flex-auth as its own +contract and calls "the one thing in the estate only `access-engine` produces". +flex-auth's frontmatter declares no emission guarantee. + +The unclear boundary is whether flex-auth is a §4 *source of evidence* or merely +the producer of an artifact that `audit-core` is the source of. The answer +decides whether this is a second declared gap or nothing at all. flex-auth should +not answer it alone, and should not assume the flattering reading. + +Owner: `gate-house` to rule; `audit-core` to confirm which side of the line it +holds. + +### B4 — the declaration pins a version the standard says is not part of the boundary + +Raised here as the general form of T01, because if flex-auth is right that a +layer declaration should not carry a standard version, the fix belongs in §11 and +not only in flex-auth's file. If §11 wants a version, it should say so and say +what it means when the standard moves. + +Owner: `gate-house`. + +### B5 — canon names a repository that does not resolve + +v0.8 refers to the PDP as `access-engine` throughout (§4 catalog row, §13.1, +§17), while the repository, runtime, namespace, images, and API vocabulary all +remain `flex-auth` per `FLEX-DEC-2026-013`, and the rename has not landed — +`access-engine` raw returns 404, verified 2026-09-20 under `FLEX-WP-0020`. + +Neither side is wrong: the rename is ruled and sequenced, and canon named the +end state. But a reader of v0.8 cannot resolve the repository it keeps naming, +and `reuse-surface` already probed the 404 independently. Worth one line in the +standard recording that the coordinate is pending rather than broken. + +Owner: `gate-house` to note; `flex-auth` to ping when `FLEX-WP-0020` lands. + +Gate: every finding above is sent to its named owner with what would resolve it. +An unanswered finding stays open and is not closed by silence. + +## Out of scope + +- Bumping flex-auth to declare v0.8. v0.8 is `status: proposed`; T01 removes the + version from the declaration entirely, which makes the question moot rather + than answering it. +- Grading any peer repository's conformance. flex-auth reports what it can + observe and names the owner; §9.3's two-owner split cuts here too. +- `FLEX-WP-0029`'s stance-register second edition. Adjacent, separately owned.