tenant-engine named the relation in TEN-DEC-2026-002: `tenant` is the target tenant record and always equals `resource.id`; the write API is cross-tenant by design and `tenant.guardrail.read` does not differ. v2 carried no tenant rule and a constant fixture tenant, so the deliberate scope and an omitted rule were indistinguishable. tenant-engine.write-api.mutate v3 (FLEX-DEC-2026-016): - allowed requires tenant_is_target; a mismatch or absent tenant is denied tenant_not_target (object.get, so an absent key names the right cause). - the cross-tenant scope is stated in the package and quantified by test_tenant_never_changes_effect over every action, three subjects and four tenants, with guards against passing by denying everything. - fixtures rotate tenant across four tenants; five cross-tenant allows and two tenant_not_target denies added (42 fixtures, 33 tests, all pass). - user-engine's tenant:platform exclusion is named as a fixed-record rule, not a subject/tenant relation, and tested separately. Closes FLEX-WP-0022 (T01, T02 done). Also records TEN-IN-0004 and SECRETS-IN-0002 on FLEX-WP-0020 and acknowledges the GH-DEC-2026-017 replies on FLEX-WP-0030-T04. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 63291@bnt-lap001 Assistant-Session: 8bd77868-ca68-4f49-bb1e-d539ecc0d703
18 KiB
| id | type | title | domain | repo | status | flavor | owner | topic_slug | planning_priority | planning_order | related_workplans | created | updated | state_hub_workstream_id | ||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| FLEX-WP-0030 | workplan | The layer declaration pins a version it should not, and four security-relevant peers do not declare at all | infotech | flex-auth | active | review | claude | netkingdom | P1 | 300 |
|
2026-09-21 | 2026-09-21 | abe60f5f-79cc-5857-b9e8-a46bed704279 |
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_byrecords 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
id: FLEX-WP-0030-T01
status: done
priority: high
state_hub_task_id: "016ab184-e814-591b-938b-24ff9ace5ede"
Owner: flex-auth.
- Drop
standard_versionfrom theINTENT.mdfrontmatter. §11 requires alayer: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.mdis 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.mdinto 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/layergreen, and decide whetherStandardVersionstays 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.
2026-09-21: done. standard_version removed; conformance_record added. The
test that pinned 0.7 was inverted rather than deleted — internal/layer
now rejects a version pin in the declaration and requires conformance_record to
name a file that exists on disk. StandardVersion is kept on the struct
deliberately, documented as must-stay-empty, so a reintroduced pin fails loudly
instead of being silently ignored. Version-scoped state is in
docs/conformance/security-layer-conformance.md. Full suite green.
2. Update SCOPE.md and assess the gaps
id: FLEX-WP-0030-T02
status: done
priority: high
state_hub_task_id: "224cd214-7226-5286-9794-a8a6d36c25e5"
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.
2026-09-21: done. Three gaps, each with owner and route. G2 (emission guarantee)
is newly assessed and recorded as a gap rather than as conformance, because the
flattering reading has been asserted by nobody but flex-auth. Register corrected
from two rows to five. All v0.7 references removed from SCOPE.md.
3. Publish the boundaries review
id: FLEX-WP-0030-T03
status: done
priority: high
state_hub_task_id: "7d45a3d6-d4a7-556a-8607-58086f74a998"
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".
2026-09-21: published at docs/conformance/boundaries-review.md, marked derived
and version-stamped. Twelve counterparts. Seven agreed, one agreed-in-substance
but undeclared (gate-house), one asserted from one side only (key-cape), one
held unilaterally and unconfirmed (zone-engine), two unclear (tenant-engine
on CheckRequest.tenant, audit-core on evidence source), one undeclared twice
over (ops-mason).
4. Raise the conflicting and unclear boundaries for resolution
id: FLEX-WP-0030-T04
status: progress
priority: high
state_hub_task_id: "70786e6d-02f2-5a90-8640-247ba377d9a4"
Owner: flex-auth to raise; the named owner resolves each.
B1 — nine of nine repositories disagree with themselves across the two §11 forms
Corrected 2026-09-21. The first version of this finding said the estate
spelled layer: three ways across repositories and named eight as non-conformant
on casing. That was wrong, from an ad-hoc shell survey that took the first
^layer: match per repository without recording which file it came from.
Re-run mechanically (tools/survey_layer_declarations.go, receipt at
docs/evidence/2026-09-21-layer-declaration-survey.json):
| Repositories carrying both §11 forms | 9 |
|---|---|
| Of those, forms that disagree | 9 of 9 |
| Shape | INTENT.md: Engine / Staff. layer.yaml: engine / staff |
| Only one form | flex-auth — no layer.yaml, which is why it cannot exhibit this |
The disagreement is within each repository, not across the estate, and it is universal rather than careless: two generators, two conventions, no repository mixing them. Nobody is inconsistent with anybody else.
§11 accepts "a layer: key in the INTENT.md frontmatter, or an equivalent
declaration file" and does not say which governs when both exist and disagree. A
conformance run reading INTENT.md and one reading layer.yaml reach different
answers for nine repositories, and both follow §11. That is the finding: not
casing, but an unruled precedence between two permitted forms.
Owner: gate-house. Two things would resolve it — which form governs, and
whether the §3 vocabulary is case-sensitive. If it is case-insensitive, the
casing half evaporates and flex-auth's validator is what changes; the precedence
half does not.
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-capeis 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-houseauthors 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.
2026-09-21: eleven messages sent. gate-house (all five findings), key-cape
(B2, one-sided identity boundary), audit-core (B3, evidence source),
ops-mason (B2), net-kingdom (B5 plus the vendored-vs-authored question),
zone-engine (membership/stance confirmation), tenant-engine (the live unclear
boundary, restated with its standing), and B1 as a heads-up to the five affected
engine repos — sent as an estate property, not as a finding against any of them,
since no repository could have discovered it from inside its own checker.
Task stays progress: every finding is open until its owner answers. None is
closed by silence.
2026-09-21 (later): B1 was sent wrong and corrected the same day. See T05.
Corrections went to gate-house and the five engine repositories that received
the original, plus ops-warden, kings-guard and audit-core, who are affected
by the corrected finding and had not been told of the first.
2026-09-21 (rulings): gate-house ruled B1, B3 and B4 (GH-DEC-2026-017,
-018, -019; amendments A9–A13). B1: INTENT.md governs, case folds. B3:
against flex-auth — a source of evidence, non-conforming on §11 today, and
audit-core confirmed independently (AUDIT-IN-0005). B4: ruled as asked. B5:
A13 notes it. B2: gate-house declared; key-cape, ops-mason and
net-kingdom have not answered. Task stays progress on B2 alone — not closed
by silence. Consequences are T06–T08.
2026-09-21 (acknowledgements): kings-guard's reply to the B1 correction
(4c328bf6) predates GH-DEC-2026-017; it held its two forms rather than
guessing, recorded KG-IN-0007, and has since applied the ruling and closed
that intake (kings-guard 5120adf, value unchanged, standard_version
removed). Its observation — that off-vocabulary values are a third class a
precedence ruling alone leaves untouched — is answered by the post-ruling
survey (docs/evidence/2026-09-21-layer-declaration-survey-after-ghdec017.json):
off_vocab is empty once Taxonomy is admitted. approval-engine,
maturity-engine, zone-engine, secrets-engine and railiance-master also
confirmed the ruling applied. None of these bears on B2.
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.
5. Make the survey reproducible
id: FLEX-WP-0030-T05
status: done
priority: high
state_hub_task_id: "a695c139-b831-55a2-b015-445742ee043c"
Owner: flex-auth.
B1 was found with a shell pipeline and published as a finding. The review it appeared in had already argued that a mechanical check nobody can re-run is an assertion — and §11's whole claim is mechanical checkability. Asserting B1 unmechanically was that defect committed by the repository reporting it.
tools/survey_layer_declarations.go reads both §11 forms for every
security-relevant counterpart, reports intra-repository disagreement, and writes
a JSON receipt. internal/layer carries four tests covering the disagreement
case, the refusal to fold case, a missing declaration, and the single-form shape
that must not be reported as disagreeing with itself.
It checks exactly one property — the layer: value against the §3 vocabulary as
written — and deliberately does not apply flex-auth's own declaration rules
(pep_stance, tooling_contacts, conformance_record) to any other repository.
Grading peers by the surveyor's house rules would be §11's own defect wearing a
tool for a hat.
Running it immediately falsified the finding it was built to reproduce, which is the argument for building it.
Gate: B1 is reproducible by command, with a receipt, and the correction is recorded rather than edited away.
2026-09-21: done. Receipt at
docs/evidence/2026-09-21-layer-declaration-survey.json.
6. Fix the validator: four tokens, case folded, scope stated
id: FLEX-WP-0030-T06
status: done
priority: high
state_hub_task_id: "23af18ea-93da-5bd2-9511-e4fce7475dc3"
Owner: flex-auth. Authority: GH-DEC-2026-017 §2–§4, A9, A11.
The validator admitted {Staff, Engine, Tooling} — three tokens taken from §4's
catalog rows, omitting §3.1's Taxonomy. railiance-master's Taxonomy was
conforming; the validator was the divergent artifact.
Gate: the four tokens are admitted; comparison folds ASCII case; §4's column
spelling is canonical; INTENT.md governs while form disagreements are still
reported; a run states its scope and reports non-catalogued declarers as
volunteers; the review records the correction rather than editing it away.
2026-09-21: done. internal/layer (CanonicalLayer, Vocabulary, Scope,
CatalogScope, EstateScope, VolunteerDeclarations), survey --catalog-only,
eight new or inverted tests. Also fixed a survey defect found on the way: peers
were decoded into flex-auth's struct, and audit-core's layer.yaml silently
dropped out once field shapes diverged. Correction in
docs/conformance/boundaries-review.md; receipt re-run.
7. Publish the per-class emission inventory and register G2 as a dated gap
id: FLEX-WP-0030-T07
status: done
priority: high
state_hub_task_id: "55af504c-482d-5c1a-97e3-7034912308f5"
Owner: flex-auth. Authority: GH-DEC-2026-018.
Gate: G2 closes as a question with the ruling as its reason and reopens as a
gap with owner, blocker and review date; the classification is published per
event class by flex-auth, not inferred; INTENT.md states
source_of_evidence and names the declaration; the test suite asserts both.
2026-09-21: done. cadence.yaml classifies five decision classes. Rare
load-bearing (deny, redact, not_applicable, audit_only): heartbeat and
reconciliation, rate monitoring forbidden. Volume load-bearing (allow):
expected-rate and reconciliation, because rate cannot see a targeted subset
removed. G2 is dated 2026-10-19; delivery is FLEX-WP-0031.
8. Rule whether resource.system follows the repository or the runtime
id: FLEX-WP-0030-T08
status: done
priority: high
state_hub_task_id: "cbf8531e-1aa9-5bc4-9f63-ecbbcf987083"
Owner: flex-auth as PDP. Asked by ops-warden (WARDEN-IN-0003).
2026-09-21: done. FLEX-DEC-2026-015: it follows the runtime. It is policy
vocabulary, retained by FLEX-DEC-2026-013, and both places the PDP matches on
it fail closed on an unknown value. Any future flip is PDP-first,
consumer-second, and is not a repository-rename step.