diff --git a/SCOPE.md b/SCOPE.md index dec64d5..e6e4468 100644 --- a/SCOPE.md +++ b/SCOPE.md @@ -197,15 +197,19 @@ four more in the v0.8 round (`FLEX-DEC-2026-011`), of which F1 — that a decisi must be *attributable* to flex-auth and that a digest comparison does not discharge it — was the finding of the round. -Conformance state is **not conforming on §11, held as three declared gaps**, +Conformance state is **not conforming on §11, held as two declared gaps**, each with an owner and a route rather than a sentence. G2 is the non-conformance -(`GH-DEC-2026-018`): +(`GH-DEC-2026-018`). The former G3 (published stance-register review going +stale against a growing register) closed 2026-09-27: +`docs/stance-register-review-second-edition.md` re-derives Findings 1-3 +across the five current rows — finding a third scope axis rather than the two +converging to one the first edition anticipated — and marks the first edition +superseded rather than amended (`FLEX-WP-0029`). | # | Gap | Owner | Route | | --- | --- | --- | --- | | G1 | Registry-snapshot digest absent from decision provenance (§9.7.2 conformance prerequisite) | `flex-auth` | `FLEX-WP-0019` | | G2 | Emission guarantee declared per event class (`cadence.yaml`) but not delivered — no outbox, heartbeat, or audit-core sender registration. Review 2026-10-19 | `flex-auth` | `FLEX-WP-0031` | -| G3 | Published stance-register review stale — written at two register rows, §13.1 now carries five | `flex-auth` | `FLEX-WP-0029` | G2 was held open as a question — is flex-auth a §4 *source of evidence*, or only the producer of an artifact `audit-core` sources? — and `gate-house` ruled it diff --git a/cadence.yaml b/cadence.yaml index 7278b61..f0aa2b1 100644 --- a/cadence.yaml +++ b/cadence.yaml @@ -49,10 +49,27 @@ state: declared-not-yet-emitting gap: G2 # docs/conformance/security-layer-conformance.md heartbeat: - class: flex-auth.decision.heartbeat - interval: 24h - assertion: nothing-to-report - missing: finding + # Per rare load-bearing class (FLEX-WP-0031-T05), not one combined class: a + # single heartbeat can go quiet on one suppressed class while the others + # keep reporting, and rate monitoring is forbidden on all four for the same + # reason. Each entry names the class it asserts nothing-to-report for. + classes: + - class: flex-auth.decision.deny + interval: 24h + assertion: nothing-to-report + missing: finding + - class: flex-auth.decision.redact + interval: 24h + assertion: nothing-to-report + missing: finding + - class: flex-auth.decision.not_applicable + interval: 24h + assertion: nothing-to-report + missing: finding + - class: flex-auth.decision.audit_only + interval: 24h + assertion: nothing-to-report + missing: finding reconciliation: # Declared for EVERY class, including the volume one. Rate monitoring can see diff --git a/docs/stance-register-review-second-edition.md b/docs/stance-register-review-second-edition.md new file mode 100644 index 0000000..c26dc85 --- /dev/null +++ b/docs/stance-register-review-second-edition.md @@ -0,0 +1,133 @@ +# Stance-register review, second edition — five rows, one axis question resolved by doctrine + +Status: published +Date: 2026-09-27 +Standard: `security-layer-model_v0.7` §6.4 obligation 3, §13.1 (v0.8 `proposed`, +reviewed and assented in `FLEX-DEC-2026-011`, not yet accepted) +Reviewer: flex-auth (Engine / PDP) +Supersedes: `docs/stance-register-review.md` (2026-09-06), which stays +unamended per `FLEX-DEC-2026-008` +Workplan: `FLEX-WP-0029` + +**This is still not a §13.1 register.** gate-house owns the register. This is +one reviewer's reading of the rows in it. + +## What changed since the first edition + +| | 2026-09-06 (v0.7 §13.1) | 2026-09-27 (v0.8 §13.1) | +| --- | --- | --- | +| Rows | 2 | **5** — `ops-warden`, `user-engine`, `tenant-engine`, `secrets-engine`, `ops-mason` | +| `unknown` divergence | open, reported as observation | **ruled**: v0.8 §6.4 obligation 3 requires `unknown` to resolve to `fail_closed` | +| Marked non-conformant | none | two rows — `ops-warden`'s `unknown` cell, `ops-mason`'s absent map | + +The first edition's closing line predicted a third row would arrive; five +arrived instead. That landing is itself part of this edition, not a footnote. + +## Finding 1 — the `unknown` divergence was resolved in `secrets-engine`'s +direction, by doctrine, not by either side persuading the other + +flex-auth reported the split as an observation and explicitly asked for no +change. gate-house answered the open question anyway: v0.8 §6.4 obligation 3 +states `unknown` is not a zone and MUST resolve to `fail_closed`, because being +unclassifiable must not buy permissiveness. `secrets-engine`'s +`unknown: fail_closed` was already conformant; `ops-warden`'s `unknown: +fail_open` now is not. + +`ops-warden` **assented to the rule and deliberately did not flip the cell.** +Verified against `ops-warden/pep-stance.yaml` (2026-09-27): 0 of its 3 signing +targets resolve to a zone. Converting `unknown` to `fail_closed` today would +fail closed on essentially every certificate during a flex-auth outage — +including the certificate needed to reach the host and repair flex-auth. That +is `ADR-0006`'s rejected configuration reached by another route, and the +refusal is correct. `WARDEN-WP-0040` is the route: classify the continuity +path, raise coverage, then convert. + +§13.1 therefore marks `ops-warden`'s row non-conformant **while the row is +right to be unconverted.** Conformance and correctness have come apart on this +cell. flex-auth did not cause the rule and does not get credit for it — the +finding is that the estate resolved a disagreement flex-auth only surfaced. + +## Finding 2 — re-run across five rows: the axis is not converging, and the +count of distinct axes went up, not down + +The first edition found two incommensurable axes (`security-zone` vs. +`catalog-stage`) and warned the cost would grow with a third row. Reading all +five files directly (not carried forward from the workplan's draft table, +which had this wrong): + +| Repository | Scope axis (as published) | `unknown` | +| --- | --- | --- | +| `ops-warden` | `security-zone` | `fail_open` (non-conformant, assented) | +| `user-engine` | `security-zone` | `fail_closed` | +| `tenant-engine` | `engine-reachability` (not `security-zone`) | `fail_closed` | +| `secrets-engine` | `catalog-stage`, explicitly interim "pending zone membership as a claim" | `fail_closed` | +| `ops-mason` | no map published | n/a — marked non-conformant by absence | + +That is **three** distinct scope axes in play (`security-zone`, +`catalog-stage`, `engine-reachability`), not the two-converging-to-one the +workplan draft assumed. `tenant-engine`'s axis is a genuine third shape: it +scopes on whether the engine can be reached and what it said, not on a +property of the target resource, and every cell of its map is `fail_closed` — +so it is trivially conformant on `unknown` but not comparable to a zone-scoped +row at all. The alarm from the first edition does not weaken at five rows; it +sharpens, because a third axis appeared rather than the two converging. + +flex-auth's boundary claim from 2026-08-19 stands and is restated here: zone +**membership** compiles into the registry snapshot flex-auth already consumes, +while per-zone **stance** belongs to the consumer. If zone membership arrives +as a claim on the decision, `secrets-engine`'s catalog-stage axis can converge +onto zones without either side inventing a stage-to-zone mapping. +`tenant-engine`'s reachability axis does not converge under that boundary at +all — it is answering a different question (was the PDP reachable and what did +it say) than a zone-scoped map answers (what should happen to this target). +That distinction is worth gate-house's attention on its own; this review notes +it rather than resolving it. + +On `ops-mason`'s absent row versus the axis question: an absent map is the +more useful subject. A published map that scopes on the "wrong" axis is still +reviewable, inventoriable, and testable against its own code — `ops-mason` +having none means §13.1 cannot even ask it what its `unknown` residue is. The +absence costs the estate more than the axis mismatch does, because the axis +mismatch is at least visible. + +## Finding 3 — `secrets-engine`'s map still cites the shelved artifact name + +Read directly from `secrets-engine/pep-stance.yaml` on 2026-09-27: line 35 +still 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`, accepted by +flex-auth in `FLEX-DEC-2026-006`. This is an **open item, not a new finding +and not a resolved one** — the stance itself is unaffected (`fail_closed` +still means no protected side effect without a durable record, and the +records exist under their current names: `approval-engine`'s approval claim +and flex-auth's `DecisionEnvelope`), but the file's prose has not been +corrected since the first edition reported it. `secrets-engine` owns the file; +flex-auth can only keep verifying. + +## Corrections to the record this edition makes + +- `SCOPE.md` already states five rows and the `unknown`/axis findings + accurately; no change was needed there. +- `INTENT.md` carries **no `standard_version` field at all** — the earlier + premise that it declares `"0.7"` and must not be bumped does not match the + file. There is nothing to avoid bumping. The version-scoped state that does + exist lives in `docs/conformance/security-layer-conformance.md`, which + correctly does not assert v0.8 acceptance. +- The first edition is not listed in `SCOPE.md`'s `contract`/`orientation` + capability blocks, so this edition does not invent one either. + +## What flex-auth is not claiming + +- No stance is wrong by virtue of being non-conformant. `ops-warden`'s + `unknown: fail_open` is a measured, reasoned, tracked exception with a route. +- No change is requested of any repository. `WARDEN-WP-0040` owns the order + for `ops-warden`'s cell and flex-auth agreed the order is right; + `secrets-engine` owns its own file's prose. +- This is not a §13.1 register and does not attempt to be one. + +## Out of scope (carried from the workplan) + +- Adopting v0.8 as flex-auth's declared standard version. +- gate-house's A-16 / A-17 (`DISTINGUISHABLE ROUTES`, and `unknown` vs. + `absent` as two meanings behind one runtime behaviour) — noted as adjacent + to Finding 2's third axis, not absorbed here. diff --git a/docs/stance-register-review.md b/docs/stance-register-review.md index 5e6ec31..4658598 100644 --- a/docs/stance-register-review.md +++ b/docs/stance-register-review.md @@ -1,6 +1,9 @@ # Stance-register review — the register has a second row -Status: published +Status: superseded — see `docs/stance-register-review-second-edition.md` + (2026-09-27, `FLEX-WP-0029`). This edition stays as written; it is not + amended to match the five-row register (`FLEX-DEC-2026-008`'s rule that a + correction a reader cannot see is not a correction). Date: 2026-09-06 Standard: `security-layer-model_v0.7` §6.4 obligation 3, §13.1 Reviewer: flex-auth (Engine / PDP) diff --git a/workplans/FLEX-WP-0020-repository-identity-migration.md b/workplans/FLEX-WP-0020-repository-identity-migration.md index 8e85a4c..1260957 100644 --- a/workplans/FLEX-WP-0020-repository-identity-migration.md +++ b/workplans/FLEX-WP-0020-repository-identity-migration.md @@ -274,6 +274,22 @@ design and close on their side after T06; `flex-auth` owes each a "rename landed" notice. Recorded in the evidence file. Seven owners remain pending. +2026-09-27: `net-kingdom` confirmed `NK-WP-0039-T02` done (package coordinate +unchanged, pins stay, no other coordinate reference found) and `runtime.yaml` +now declares current tenant-engine/user-engine digests. `NK-WP-0039-T03` waits +only on flex-auth's announcement that `access-engine` resolves, i.e. it is +gated on T06 like the others. Separately, `net-kingdom` found +`sso-mfa/k8s/tenant-engine/runtime.yaml` live-ahead-of-file beyond digests +(missing `--caller-auth-mode enforce` and caller bindings) and proposed +replacing the flex-auth part of it with a pointer to `values/.yaml` +per their `ADR-0015`; flex-auth replied with no objection, since that matches +the pattern flex-auth already uses for other consumers and points at a file +that actually tracks caller-auth state. Six owners now confirmed acknowledged +or resolved on their side; the remainder is still tracked in the evidence +file. This does not change T04's own gate — the external ownership ledger +still requires every discovered change to land its own owning-repository +handoff before T05/T06. + Reviewed inventory baseline: | Owner | Required source/verification surface | diff --git a/workplans/FLEX-WP-0027-t03-human-review.md b/workplans/FLEX-WP-0027-t03-human-review.md index e276500..edfa8c0 100644 --- a/workplans/FLEX-WP-0027-t03-human-review.md +++ b/workplans/FLEX-WP-0027-t03-human-review.md @@ -4,12 +4,12 @@ type: workplan title: "Admit scoped human review for the three T03 actions" domain: infotech repo: flex-auth -status: active +status: blocked flavor: implementation owner: codex topic_slug: netkingdom created: "2026-09-14" -updated: "2026-09-14" +updated: "2026-09-27" state_hub_workstream_id: "954635b2-8377-5227-ab4f-10607b2a02c6" --- @@ -52,3 +52,8 @@ state_hub_task_id: "9417d64a-308c-566f-af29-217c5c45d294" Wait for the operator's exact signed-in account, then address the three memos and verify actual human acknowledgements/entries. Synthetic policy checks are not acceptance evidence. SECRETS-WP-0010-T03 retains live consume and execution. + +2026-09-27: no operator sign-in has occurred yet. This is the only remaining +task and it is irreducibly a human action (the operator's own signed-in +account exercising the review), so the workplan is marked `blocked` rather +than `active` until that happens. diff --git a/workplans/FLEX-WP-0029-stance-register-second-edition.md b/workplans/FLEX-WP-0029-stance-register-second-edition.md index f7e0352..3a12aa1 100644 --- a/workplans/FLEX-WP-0029-stance-register-second-edition.md +++ b/workplans/FLEX-WP-0029-stance-register-second-edition.md @@ -4,7 +4,7 @@ type: workplan title: "The stance register outgrew the review that read it: five rows, and the divergence was ruled rather than resolved" domain: infotech repo: flex-auth -status: ready +status: finished flavor: review owner: claude topic_slug: netkingdom @@ -13,7 +13,7 @@ planning_order: 290 related_workplans: - FLEX-WP-0019 created: "2026-09-20" -updated: "2026-09-20" +updated: "2026-09-27" state_hub_workstream_id: "5a11099d-b492-535c-af88-c334db5e8ee6" --- @@ -54,11 +54,15 @@ reviewer's reading of the rows in it, and the second edition must keep saying so ```task id: FLEX-WP-0029-T01 -status: todo +status: done priority: high state_hub_task_id: "5a7ed269-974f-5c6a-8e80-e9aa0077f8aa" ``` +Done 2026-09-27: recorded in `docs/stance-register-review-second-edition.md` +Finding 1. Verified against `ops-warden/pep-stance.yaml` that the cell is +unflipped by deliberate assent (0 of 3 signing targets resolve to a zone). + Owner: `flex-auth`. v0.8 §6.4 obligation 3 states that **`unknown` is not a zone and MUST resolve to @@ -91,11 +95,19 @@ it. No claim that flex-auth's review produced the rule. ```task id: FLEX-WP-0029-T02 -status: todo +status: done priority: high state_hub_task_id: "991ebb94-cdc4-574a-ba60-f8960ec885fc" ``` +Done 2026-09-27: recorded in the second edition's Finding 2. Reading all five +files directly (not the draft table above) found **three** distinct scope +axes, not two converging to one — `tenant-engine` scopes on +`engine-reachability`, not `security-zone` as assumed here. The alarm +sharpens rather than weakens. `ops-mason`'s absent row judged the more costly +of the two problems: an axis mismatch is at least visible, an absent map is +not even reviewable. + Owner: `flex-auth`. Finding 2 said the register cannot answer *"what is the estate's stance for a @@ -127,11 +139,15 @@ from the two-row text. ```task id: FLEX-WP-0029-T03 -status: todo +status: done priority: medium state_hub_task_id: "6dd2c9fd-8ad9-54d6-8fb1-f24e44189f00" ``` +Done 2026-09-27: read `secrets-engine/pep-stance.yaml` directly. It still +cites `ActionAuthorization` (line 35). Recorded in the second edition as an +open item, not re-reported as new and not claimed resolved. + Owner: `flex-auth` to verify; `secrets-engine` owns the file. Finding 3 reported that `secrets-engine`'s `pep-stance.yaml` defines @@ -151,11 +167,19 @@ reply. ```task id: FLEX-WP-0029-T04 -status: todo +status: done priority: medium state_hub_task_id: "4dbadd0b-7670-5837-90c9-6201c10479d7" ``` +Done 2026-09-27: `SCOPE.md` already stated five rows accurately (its own G3 +row was the only stale sentence, now updated below); no rewrite was needed +there. `INTENT.md` carries no `standard_version` field at all, so the +premise that it declares `"0.7"` was itself stale — nothing was bumped, and +this is noted in the second edition rather than silently corrected. The +first edition is not in `SCOPE.md`'s capability blocks, so no capability was +invented for the second. + Owner: `flex-auth`. - `SCOPE.md` says §13.1's register *"now has **two rows** rather than the one the diff --git a/workplans/FLEX-WP-0031-decision-record-emission.md b/workplans/FLEX-WP-0031-decision-record-emission.md index fd70a27..5bcff1d 100644 --- a/workplans/FLEX-WP-0031-decision-record-emission.md +++ b/workplans/FLEX-WP-0031-decision-record-emission.md @@ -4,7 +4,7 @@ type: workplan title: "The decision record has a declared emission guarantee and nothing that delivers it" domain: infotech repo: flex-auth -status: active +status: blocked flavor: implementation owner: claude topic_slug: netkingdom @@ -14,7 +14,7 @@ related_workplans: - FLEX-WP-0030 - FLEX-WP-0019 created: "2026-09-21" -updated: "2026-09-23" +updated: "2026-09-27" state_hub_workstream_id: "84f5d9fe-b4c9-584a-b964-efe3e48af095" --- @@ -82,6 +82,25 @@ which `senders.py` forbids for a load-bearing source. warden route has no catalog lane for audit-core sender tokens, and audit-core was asked to name one. Waiting on audit-core. +2026-09-27: audit-core replied (`AUDIT-IN-0006`, thread `6044ed35`), accepted +with corrections: `may_read` must be `false`, not `true` (a writer already +counts its own sources without it); `tenants: ["*"]` accepted on the stated +justification; per-class heartbeats and the `cadence.yaml` correction accepted +(done, see T05); each registration must also carry an `emission_cadence` +declaration (info-tech-canon wire schema 0.1, contract digest +`b08b4d95fc4b0bd3`) with `allow` as expected-rate, or its rate is only +declared and not evaluated. Two envelope corrections land before submission: +the heartbeat event needs its own `correlation_id` (no decision id exists for +it), and `occurred_at` needs an explicit offset. The atomicity ruling +(whether the `FLEX-DEC-2026-018` failure-path release is a `completeness_trade`) +is referred to `gate-house` by audit-core, not decided here — flex-auth agreed +in reply to wait for that referral. The token lane is an attended OpenBao mint +in the founder's terminal (same path `tenant-engine` used, +`AUDIT-WP-0010-T02`); `ops-warden` has no catalog lane for it yet and will add +one when registration opens. flex-auth acknowledged all of this in reply. +Still `wait`: the mint needs the founder, and `ops-warden`'s catalog entry is +not yet in place. + ## 3. Durable outbox and the release rule ```task @@ -118,11 +137,19 @@ Done 2026-09-23: ```task id: FLEX-WP-0031-T05 -status: todo +status: wait priority: high state_hub_task_id: "14bd6648-11da-53d7-a512-027005bd4772" ``` +2026-09-27: `cadence.yaml`'s heartbeat declaration corrected — it now names one +class per rare load-bearing event (`deny`, `redact`, `not_applicable`, +`audit_only`) instead of a single combined `flex-auth.decision.heartbeat` +class. `go build`/`go test ./...` pass unchanged. The remaining scope — the +actual per-class heartbeat sender and the drain to `POST /v1/events` — is +correctly blocked on T02's sender registration with `audit-core`; the task +stays `wait` rather than in progress here. + - Emit a daily `audit-core.heartbeat` event per rare class, with `data.class` set to the class, following audit-core's `stream_findings` shape. - Correct `cadence.yaml`, which still names a single @@ -136,11 +163,21 @@ state_hub_task_id: "14bd6648-11da-53d7-a512-027005bd4772" ```task id: FLEX-WP-0031-T06 -status: todo +status: wait priority: high state_hub_task_id: "bf92a951-3b69-59dd-8907-f6748c91a264" ``` +2026-09-27: reconciliation compare needs `audit-core`'s `GET /v1/reconciliation`, +which does not exist until T02's sender is registered. The profile checker +(`net-kingdom/tools/emission-cadence-profile`) is runnable locally but needs a +`--contract-schema` for the decision-record cadence contract that is not yet +published in this repo or referenced by any sibling's invocation found — +inventing one here would be exactly the kind of schema flex-auth should +publish deliberately, not improvise for a validation run. The PVC chart change +is explicitly a production change needing the founder's go-ahead and was left +undone. Stays `wait`. + - Compare committed counts per class and window with audit-core's `GET /v1/reconciliation`. Divergence is a finding, and undrained events count as lag, not divergence. @@ -154,11 +191,15 @@ state_hub_task_id: "bf92a951-3b69-59dd-8907-f6748c91a264" ```task id: FLEX-WP-0031-T04 -status: todo +status: wait priority: medium state_hub_task_id: "423b3090-1b72-58fd-9353-5c907c7683bb" ``` +2026-09-27: closing G2 requires the silence-finding gate, which requires T05's +sender and T06's reconciliation to actually exist. Both are blocked on T02 +(external, `audit-core`). Stays `wait`. + Change `cadence.yaml` `state` to emitting, move G2 out of the gap table with the evidence, and tell `gate-house`, `audit-core` and `kings-guard`. Gate: a silence finding is observed on a deliberately withheld heartbeat in a non-production