secrets-engine/workplans/SECRETS-WP-0008-layer-model-lifecycle-conformance.md

489 lines
24 KiB
Markdown
Raw Normal View History

---
id: SECRETS-WP-0008
type: workplan
title: "Evolve the Lifecycle engine to the accepted security layer model"
domain: infotech
repo: secrets-engine
status: active
flavor: implementation
owner: grok
topic_slug: custodian
created: "2026-08-29"
Apply GH-DEC-2026-017: INTENT.md governs, layer.yaml is derived, no version Verified against gate-house's committed ruling (decisions/decisions.md, GH-DEC-2026-017) and amendments A9-A13, then ops-warden's reference change set (a70f559, wiki/playbooks/netkingdom-layer-declaration.md). They agree. layer.yaml: standard_version removed; derived: true and derived_from: INTENT.md added; declared_by kept. INTENT.md frontmatter never carried standard_version, but its standard: value was a version-pinned path; it is de-versioned as the reference instance did. No layer value is re-spelled: INTENT.md still says Engine and layer.yaml still says engine. The checker changes in the same commit because it listed standard_version as a required key: removing the field alone would have made a conforming declaration exit 2 MALFORMED. It now reads INTENT.md as the governing form, requires the derived marking, rejects a returning standard_version in either form, checks both layer values against the closed four-token vocabulary (Taxonomy included) after an ASCII fold, and reports a post-fold disagreement between the forms as a finding rather than resolving it by precedence. Tests assert the fold, not per-file spelling, and cover fold agreement, a real disagreement, the closed vocabulary and a returning version. Full suite 430 passed. role:, pep-stance.yaml and schema_version are untouched (not ruled). Still open: where the removed version lives in a derived conformance record; asked of gate-house by ops-warden (4220413a), followed rather than chosen here. Closes the SECRETS-WP-0008 note that waited on the reference form. 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
2026-09-21 07:37:53 +02:00
updated: "2026-09-21"
state_hub_workstream_id: "9c9e5164-b2f5-5ea2-a557-5368d65e9fe0"
---
# SECRETS-WP-0008 - Evolve the Lifecycle engine to the accepted security layer model
## Goal
Close the gaps between the 2026-08-29 layer declaration (Engine / Lifecycle)
and the obligations the accepted NetKingdom Security Layer Model v0.7 places on
a PEP-shaped Lifecycle engine.
Source review: `history/2026-08-29-layer-model-scope-intent-review.md`.
Declaration surface already landed this session: `INTENT.md` frontmatter,
`layer.yaml`, `pep-stance.yaml`, and `tests/test_layer_conformance.py`.
This workplan does **not** restart CLI hardening owned by `SECRETS-WP-0007`.
It adds the layer-model remainder: stance binding, decision-record consumption,
`audit-core` evidence, and the two proposed engine surfaces.
## Non-goals
- Taking ops-warden's SSH access lane or becoming a credential broker.
- Rendering or caching authorization decisions.
- Observation in production or automatic containment (companion §10: both are
at zero estate-wide).
- Service mode, runtime tenancy, or a second custody backend.
- Reopening live production on State Hub status or local fixtures.
## Coordination
- `SECRETS-WP-0007-T04` owns the consumer validator and the fail-closed
production gate until a durable ActionAuthorization object is served.
- `SECRETS-WP-0007-T05` owns delivery-session self-revocation and the KeyCape
scaffold; this workplan owns the layer-model "no standing credential" bar.
- railiance-platform owns production OpenBao and the JWT role.
- `access-engine` / State Hub own serving the decision record.
- `audit-core` owns evidence custody.
- ops-warden owns the SSH Staff PEP and must assent any SSH-CA API contract.
- kings-guard owns secret-abuse posture and must assent the evidence surface.
- gate-house owns statute §13.1 inventory of PEP stance maps.
## Tasks
## Publish stance application as shipped behaviour
```task
id: SECRETS-WP-0008-T01
status: done
priority: high
state_hub_task_id: "945735c6-2d3d-5f72-baf0-692e6b210889"
```
Completed 2026-08-29. `_require_lane_approval` loads `pep-stance.yaml`.
`SHIPPED_STANCE` is pinned equal to the published map. Fail-closed production
evidence records `stance_stage`, `stance_failure_mode`, and omits
`stance_decision_id` unless a decision was rendered.
`pep-stance.yaml` is published and tests pin `prod: fail_closed` to
`_require_lane_approval`. The gate is still hardcoded, and a refusal is
recorded as a `DecisionError` rather than named stance fields.
Load the published map as the source of shipped defaults. When the
unreachable-engine residue is applied, record at least stage, failure mode, and
decision id present only where a decision was rendered. Keep the three-factor
unsafe-demo exception out of the stance table. Assert map equality in
`tests/test_layer_conformance.py` the way ops-warden's stance test does.
Acceptance:
- `_require_lane_approval` (or its successor) reads `pep-stance.yaml`.
- Changing the YAML without changing the code fails the test, and the reverse.
- Fail-closed production evidence includes named stance fields, never a secret.
- Build/test remain fail-open *relative to access-engine* and still require
the existing lane-approval check; that residue stays documented as a gap
until T02.
## Consume access-engine decision records
```task
id: SECRETS-WP-0008-T02
status: wait
priority: high
state_hub_task_id: "3eb9cff8-1441-5437-9e92-a2b655c82d04"
```
Progress 2026-09-02. Gate House notice `632bdad9` (`GH-DEC-2026-003`): this
engine is the PEP for FLEX-WP-0017-T05 / OpenBao writes. The shared consume
function now lives in `src/secrets_engine/approval_consume.py` and every live
privileged production handler reaches it through `_require_lane_approval`
before `OpenBaoClient.resolve`. Conflict, unavailability, or a missing
binding fail closed with no OpenBao call. Production still also fail-closes
on the unpublished durable ActionAuthorization / consume-binding serving
path, so this task remains `wait`.
feat: implement the PIP claim + validate authorization join resolve_consume_binding was a `return None` stub, so protocol step 1 of docs/approval-consumption.md (GET /v1/approvals/{id}/claim) and the validation join never existed. validate_action_authorization had no caller in src/ at all - it was reachable only from tests. Production fail-closed was correct, but for an undocumented second reason, and WP-0007-T04's "what remains is not local engine work" was wrong. The join now reproduces the exact CheckRequest via build_action_request, fetches the durable ActionAuthorization, and validates request binding, digest, validity, authority, policy pin, and distinct-approver threshold before offering a consume binding. _require_lane_approval threads the exact field set for provision/rotate/verify/exec so the digest covers the real proposed action. Deliberate choices: - The approval-engine object id is never inferred from a State Hub decision UUID; flex-auth stated GET /decisions/{uuid} is not the durable object. - No default policy pin. flex-auth stated secrets-engine.lifecycle/v1 is example vocabulary, not a published package. - A half-configured join raises rather than returning None, so a partial deployment cannot be mistaken for an unconfigured one. Behavior is unchanged today: every new input is absent by default, so production still fails closed and plan/--dry-run still work. 234 tests pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M65ovP3eiiPHubibvWs9mD Assistant: claude-code Assistant-Model: opus Assistant-Process: 393550@bnt-lap001 Assistant-Session: 4bb359f9-1f12-4410-9e76-079cf23c82e4
2026-09-06 01:01:55 +02:00
Correction 2026-09-06. See `SECRETS-WP-0007-T04` for the full note. Summary: the
consume-before-OpenBao gate was real, but the PIP claim + validate join was a
`return None` stub and `validate_action_authorization` had no production caller,
so this task's "blocked on the durable serving path" framing hid an unimplemented
local seam. The join is now implemented and tested; the remaining blocker is
genuinely external (approval-engine claim endpoint and access-engine Check, both
unreachable as of 2026-09-06).
docs: correct the stale step-2 blocker, and ask custody about the npm lane path SECRETS-WP-0008-T02 still said access-engine Check was unreachable. That is no longer true and the note read as current, so it is corrected rather than left to mislead: Check is reachable through the owner-documented access path and step 2 is proved against decision:0f9c98f14545c42d, a real v2 allow. Two defects that only a real request could expose -- the missing tenant and the unsatisfiable digest join -- are fixed, so the task's own acceptance line about failing closed on wrong digest and expired lifetime is now exercised against a genuine envelope rather than a fixture. What remains for that task is step 1 alone: approval-engine must serve the claim endpoint, and APPROVAL-WP-0002-T03 is still wait with no deployed base URL. One external dependency, not the two previously named. SECRETS-WP-0006-T06: asked railiance-platform which KV location backs the whynot-design npm publish lane (hub message 546403e4). The question is narrowed to the path, since the endpoint agrees and ops-warden's NPM_AUTH_TOKEN claim resolved here as a category error -- that is the resolved injection env var, not a KV field name. Catalog left unchanged: rewriting a proven production lane pointer from an inbox claim is the unverified custody mutation this task exists to prevent. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E4tNMAYcSQmZWUE4wqP4ij Assistant: claude-code Assistant-Model: opus Assistant-Process: 715726@bnt-lap001 Assistant-Session: 80a42b32-cba6-4b23-8be0-68819b1a6092
2026-09-07 13:44:46 +02:00
Progress 2026-09-07. **Step 2 is no longer blocked and is proved against a real
decision.** The 2026-09-06 note above says "approval-engine claim endpoint and
access-engine Check, both unreachable"; the second half of that is now out of
date and is corrected here rather than left to read as current.
`access-engine` Check is reachable through the owner-documented access path
(loopback `kubectl port-forward` to the named pod plus a bounded `TokenRequest`
token; `docs/pdp-access-path.md`). A real CheckRequest for
`glas-claude-agent-dev-anthropic` returned `decision:0f9c98f14545c42d`, `allow`,
`catalog_lane_policy_matched`, under package `secrets-engine.catalog-lane.lifecycle`
v2 with `binding.tenant: tenant:platform`.
Two defects surfaced only because the request was real, both now fixed:
- Our CheckRequest carried no `tenant`, which v2 treats as a `wrong_tenant`
denial rather than an omission (`80eafaf`).
- `validate_decision_envelope` required byte-equality between the binding and
our unenriched request, which no real decision can satisfy because the
evaluator normalizes and enriches from its registry before hashing. Corrected
to the published consumer rule in `10baad9`.
So the acceptance line "wrong digest, expired lifetime, superseded status, or
missing record fail closed before OpenBao" is now exercised against a genuine
envelope: the unrefreshed live decision is refused on lifetime, and a restated
`resource.attributes.stage`, a foreign `subject.tenant`, or an unexpected
enrichment field are each refused.
**What remains is step 1 only.** approval-engine must serve the claim endpoint;
`APPROVAL-WP-0002-T03` is still `wait` with no deployed base URL, so
`resolve_consume_binding` returns `None` and production stays fail-closed. That
is one external dependency rather than the two this task previously named.
Also recorded, and not a blocker for this task: flex-auth's response channel is
unauthenticated (`FLEX-DEC-2026-010`). Fail-closed protects against a PDP that is
absent, not one that lies. Until `FLEX-WP-0024` ships detached signatures, the
enforced loopback address shape is what stands in for responder authenticity.
Blocked on the durable ActionAuthorization serving path owned with
`SECRETS-WP-0007-T04` / State Hub / `access-engine`.
Once that object is served, a protected side effect holds a decision record
identifying the request it was rendered for. Replay is permitted iff the
canonical request digest matches and the lifetime holds. Do not cache the
verdict. Do not treat State Hub status, a local fixture, or catalog admission
as the decision.
Acceptance:
- Every live privileged production handler passes one shared consume function.
- Wrong digest, expired lifetime, superseded status, or missing record fail
closed before OpenBao.
- Evidence carries the decision id; a fail-closed residue still records the
stance instead.
- Local fixtures cannot authorize steady-state production.
### 2026-09-09 published replay-identity contract received
Owner message `83db7e47-ba93-4d3e-ac15-2bcb2bb16a34` supplies
FLEX-DEC-2026-012, flex-auth source `0bc624b`, and updated
`docs/request-enrichment.md`, `docs/canonical-request-digest.md` and replay
examples. This supersedes the earlier "only external dependency" reading.
T02 is progress: the next work is an executable local consumer correction,
with no new owner interpretation or approval needed.
Implement `binding.submitted_request_digest` comparison against the exact
submitted CheckRequest. Stop treating enriched `request_digest` or the consumer's
chosen list of benign enrichment fields as the replay identity. Preserve policy
pin, lifetime and deny checks. Refresh the owner's replay fixtures without
changing their existing request_digest/approval_binding_digest pins.
For the approval join, carry the validated approval claim to the PDP and compare
its recorded pdp_digest to the evaluator's approval_binding_digest. Do not
recompute either side from unenriched material. Prove changed request, wrong
claim/digest, absent new binding field and registry-overridden caller attributes
are handled under the published contract before any live native lane activates.
The separate approval identity exchange is implemented in SECRETS-WP-0009-T03
and passes 350 tests plus a real KeyCape/Approval Engine component exercise.
That exercise uses a PDP double and does not prove this newer replay contract.
The current source has not yet implemented the changes in this return; live
PDP rollout/adoption evidence remains explicit too. No synthetic fixture or Hub
decision substitutes for a runtime allow and consume.
### 2026-09-09 replay consumer implementation and real component acceptance
The returned correction is implemented. The PEP now checks the exact submitted
request digest including the full carried approval, permits registry-owned
attribute replacement, joins the evaluator-origin approval digests and rechecks
claim freshness after Check. The consume CAS uses the validated evaluator's
request_digest and decision id; an observation alone is not a consume binding.
334 regression tests pass. The disposable KeyCape + actual Approval Engine +
compiled Flex Auth exercise passes 13 checks, including real dual-control denial
then claim/check/consume, registry override, changed submitted request, wrong
action, consumed claim and same/different CAS digest. Independent producer
inputs replace response-reconstructed replay fixtures; existing digest pins
remain unchanged. Receipt: docs/evidence/2026-09-09-replay-contract-exercise.json.
No OpenBao side effect, live credential read or model request occurred.
T02 is wait for the live return: deployed PDP contract adoption and the admitted
Approval Engine claim/consume path (APPROVAL-WP-0002-T03 and RPF-WP-0035-T06 /
AUDIT-WP-0009-T09). SECRETS-WP-0009-T03 owns native delivery acceptance. The old
live decision lacks submitted_request_digest and is explicitly refused; its
historical evidence was not rewritten. Local conformance does not close the
remaining live task. This supersedes earlier additive-only and claim-free
transitive correspondence descriptions above.
## Emit evidence under the §9.6 bound
```task
id: SECRETS-WP-0008-T03
status: done
priority: high
state_hub_task_id: "0b54cedf-9335-5fa0-bdcb-1efa9a9cd98d"
```
Completed 2026-08-29. `evidence-classification.yaml` is the declaration.
Load-bearing records are queued in `evidence_dir/outbox/` before JSONL.
`secrets-engine evidence heartbeat` emits a positive claim. Drain never sits
on a mutation path and an audit-core outage leaves the queue in place.
Classify each privileged action as load-bearing or attributive. Today's trail
is attributive local JSONL plus best-effort State Hub notes; completeness is
already not claimed and must stay that way until the destination is
`audit-core`.
Load-bearing classes (at least live destroy, production revoke/suspend of a
control, and dual-control provision once those are enabled) require atomic
emission via a **local** outbox and a declared cadence or heartbeat. Do not
place a synchronous `audit-core` dependency inside the state-change
transaction. Attributive classes may keep the current non-blocking trade if
that trade stays declared.
Acceptance:
- A machine-readable classification exists next to the declaration.
- No control in this repository branches on presence or absence of a local
evidence record.
- Load-bearing emission is queued locally; an `audit-core` outage does not
block a revocation.
- Documentation uses the sound claim: the archive proves records it holds were
not altered or truncated after arrival.
## Accept the SSH-CA signing engine surface
```task
id: SECRETS-WP-0008-T04
status: done
priority: medium
state_hub_task_id: "581aeee3-3d99-5034-992b-7d42e6542a19"
```
Completed 2026-08-29. Contract published at `docs/ssh-ca-signing-contract.md`.
No OpenBao SSH-CA write was added. `layer.yaml` owner_status remains proposed.
ops-warden's `ssh-ca-signing-write` gap names this engine as intended owner.
Accept the **engine API**, decline the Staff lane. Publish a contract
ops-warden can call after an `access-engine` decision: sign a short-lived
certificate for an `adm`/`agt`/`atm` actor under the caller's request, present
no additional authority, reconstruct as the caller's action.
Do not implement a replacement client until ops-warden assents the contract.
Do not unpark the standalone `warden-sign` AppRole as break-glass.
Acceptance:
- A non-secret contract document in `docs/` states request, decision binding,
lifetime, evidence, and non-responsibilities.
- ops-warden has a recorded chance to assent or contest.
- No OpenBao SSH-CA write is added to this CLI ahead of that assent.
- `layer.yaml` owner_status remains `proposed` until the surface ships.
## Accept the secret-use evidence surface
```task
id: SECRETS-WP-0008-T05
status: done
priority: medium
state_hub_task_id: "3100d28c-9494-5b21-9d5a-af583cd8aa55"
```
Completed 2026-08-29. Contract published at
`docs/secret-use-evidence-contract.md`. `layer.yaml` owner_status remains
proposed. `route`/`audit` are still not this surface.
**2026-09-02:** kings-guard assented in its own voice (message
`75ebd2cc-a166-4676-94aa-deef2791c0c9`). It will not consume the surface as
an observation input until the surface ships and publishes its cadence
declaration. No Tooling contact will be opened to fill that wait.
**2026-09-02 later:** shipped `secrets-engine secret-use snapshot`. Cadence is
the declared 1d heartbeat on the snapshot envelope. `owner_status` stays
`proposed` until kings-guard admits the snapshot as an observation input.
`route`/`audit` remain distinct operator summaries.
kings-guard's secret-abuse posture is fixture-driven because no engine exposes
lease, revocation, mount, rotation, and delivery-session metadata. `route` and
`audit` are operator summaries over local JSONL and are not that surface.
Publish a read-only engine contract of non-secret metadata, bounded by §9.6:
it cannot prove omission at source. Coordinate destination with `audit-core`.
Do not give kings-guard a vault client, and do not return values.
Acceptance:
- Contract lists fields, freshness, and the evidence bound.
- kings-guard has a recorded chance to assent or contest.
- No secret material, token, or accessor is in the surface.
- `layer.yaml` owner_status remains `proposed` until the surface ships.
## No standing engine credential
```task
id: SECRETS-WP-0008-T06
status: wait
priority: medium
state_hub_task_id: "d7bc8bdc-a0f8-5058-a640-374ef9859148"
```
Progress 2026-09-02. Named engine auth is shipped: `service-jwt`, `bootstrap`,
and `env` are exclusive providers. `--auth service-jwt` exchanges the KeyCape
service JWT and logs in through `OpenBaoClient.login_jwt` (JSON file, never
argv), then self-revokes. A JWT selection refuses `--bootstrap-token-file` and
does not read `BAO_TOKEN` on failure. `--bootstrap-token-file` is a named
break-glass provider with `auth_break_glass` evidence.
The platform-owned OpenBao JWT mount/role is still unpublished, so auto keeps
bootstrap/env and this task remains `wait`.
Inbox triage 2026-09-21. railiance-platform `29cccf8a` (2026-09-09): status,
not a contract. Their side is RPF-WP-0035-T02 (design
`docs/credential-lane-designs/secrets-engine-service-jwt.md`), still `wait`.
Designed: role/audience `secrets-engine-openbao`, subject
`service:secrets-engine`, token policy `secrets-engine-login-self`, five-minute
budget, login-only. KeyCape issuer `https://kc.coulomb.social` and its JWKS are
now confirmed live, so the remaining blocker is this registration's issued
claims, consumer readiness, an approved source and attended apply authority.
**Open question for secrets-engine, not answered in the triage session:** the
JWT design says `tenant:coulomb`, while the approval chain resolved to
`tenant:platform` (decision `5ed3fb35`) and approval-engine compares tenant by
exact string. Which tenant the OpenBao service identity carries is a design
decision for an owner session; platform will correct its design before the
role exists once told. A service login grants no lane mutation authority. Companion §7 / statute §3.4: an
agent holds no long-lived credential of its own. Authority is per task,
time-bounded, and attributable to the principal it acts for.
Bootstrap token files remain break-glass, heavily evidenced, never an implicit
fallback from service identity.
Acceptance:
- Steady-state CLI/API auth is the reviewed service identity, not
`--bootstrap-token-file`.
- Bootstrap is a named break-glass provider with distinct evidence.
- No fallback from JWT/service-auth failure into bootstrap or AppRole.
- Issued delivery sessions continue to self-revoke as in WP-0007-T05.
### 2026-09-21 GH-DEC-2026-017 — section 11 layer declaration ruled
Read from `gate-house/decisions/decisions.md`, not from message text.
`secrets-engine` is a named affected repository and carries both permitted
forms, so all four parts apply here. State Hub decision id
`82dd0fea-fabb-4940-92d5-d6e2ea9985f5`.
What the ruling settles for this repository:
1. **`INTENT.md` governs.** Our declaration is the `layer: Engine` key in
`INTENT.md` frontmatter. `layer.yaml` is a **derived artifact**: it must be
marked as derived, must name `INTENT.md` as what it derives from, and must
agree with it. Our `layer.yaml` already carries `declared_by: INTENT.md` and
a `Voice: INTENT.md (this repository's own declaration, §11)` header comment,
so we are among the repositories already treating the sidecar as derived in
substance. What is missing is the explicit derived marking the ruling now
requires as a field rather than a comment.
2. **The §3 vocabulary is case-insensitive and comparison must ASCII case-fold.**
Our `INTENT.md` says `Engine` and our `layer.yaml` says `engine`. Under §2 of
the ruling that is **conforming**, it was never a disagreement about a layer,
and **nothing here is re-spelled**. flex-auth withdrew the casing finding
itself (message `962bf395`) and its validator is the thing that changes. We
do not touch either file's spelling, and we do not "align" them.
3. **The vocabulary is closed with four tokens**`Taxonomy`, `Tooling`,
`Engine`, `Staff`. `Engine` is in it; no action.
4. **`standard_version` comes out of the declaration.** Our `layer.yaml` carries
`standard_version: "0.7"`. A declaration asserts a standing property and must
not carry a standard version; version-scoped state belongs in the derived
conformance record, which must name what it derives from and the version or
commit it was derived at.
**Deliberately not done in this session.** The ruling states that
`standard_version` is not one repository's habit but **ops-warden's reference
form**, which this repository copied along with seven others, and that ops-warden
is asked to make the change there rather than each adopter deciding independently.
ops-warden is updating that reference form in a parallel session. Inventing our
own sidecar shape ahead of it would produce exactly the divergence the ruling
exists to close, so we record the requirement and wait for the reference form.
What `secrets-engine` will need, once the reference form lands:
- Adopt the reference form's derived-artifact marking in `layer.yaml`, keeping
`declared_by: INTENT.md` or whatever field the reference form names for it.
- Remove `standard_version: "0.7"` from `layer.yaml`.
- Move the version the conformance run was derived at into our derived
conformance record, per the reference form's field for it. Our conformance
checker is `scripts/check_layer_conformance.py`; it reads `layer.yaml` today
and will need to read `INTENT.md` as the governing form, read the sidecar as
well, and **report a disagreement between them as a finding** rather than
resolve it by precedence. A casing-only difference is not such a disagreement.
- Keep `framework:`, `layer:`, `role:` and the substantive §3/§5/§6/§9.6
content unchanged. `role:` is explicitly not ruled and stays as it is.
- The `Framework:` header comment in `layer.yaml` cites
`security-layer-model_v0.7.md` while the ruling is issued against `v0.8`.
That is a citation in a comment, not a declaration version, so it is out of
scope for part 4 — but it should be refreshed in the same commit so the two
are not confused for each other.
No task is opened here by hand. This is a note against the workplan that already
owns `layer.yaml` / `pep-stance.yaml` / INTENT frontmatter staying in one voice;
the work lands under that exit criterion when ops-warden's form is published.
Apply GH-DEC-2026-017: INTENT.md governs, layer.yaml is derived, no version Verified against gate-house's committed ruling (decisions/decisions.md, GH-DEC-2026-017) and amendments A9-A13, then ops-warden's reference change set (a70f559, wiki/playbooks/netkingdom-layer-declaration.md). They agree. layer.yaml: standard_version removed; derived: true and derived_from: INTENT.md added; declared_by kept. INTENT.md frontmatter never carried standard_version, but its standard: value was a version-pinned path; it is de-versioned as the reference instance did. No layer value is re-spelled: INTENT.md still says Engine and layer.yaml still says engine. The checker changes in the same commit because it listed standard_version as a required key: removing the field alone would have made a conforming declaration exit 2 MALFORMED. It now reads INTENT.md as the governing form, requires the derived marking, rejects a returning standard_version in either form, checks both layer values against the closed four-token vocabulary (Taxonomy included) after an ASCII fold, and reports a post-fold disagreement between the forms as a finding rather than resolving it by precedence. Tests assert the fold, not per-file spelling, and cover fold agreement, a real disagreement, the closed vocabulary and a returning version. Full suite 430 passed. role:, pep-stance.yaml and schema_version are untouched (not ruled). Still open: where the removed version lives in a derived conformance record; asked of gate-house by ops-warden (4220413a), followed rather than chosen here. Closes the SECRETS-WP-0008 note that waited on the reference form. 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
2026-09-21 07:37:53 +02:00
### 2026-09-21 GH-DEC-2026-017 applied — reference form published, note closed
ops-warden published the reference form (commit `a70f559`,
`wiki/playbooks/netkingdom-layer-declaration.md` "Reference-form change set").
Re-read against the ruling in `gate-house/decisions/decisions.md` and
amendments A9A13 before editing; the ruling and the playbook agree.
Applied in one commit, field and checker together:
- `layer.yaml`: `standard_version: "0.7"` removed; `derived: true` and
`derived_from: INTENT.md` added (`declared_by: INTENT.md` kept, as the note
above required). Header comment marks the file derived and drops the version
from the framework citation.
- `INTENT.md` frontmatter: never carried `standard_version`, but its
`standard:` value was a version-pinned path (`..._v0.7.md`). De-versioned to
`net-kingdom/canon/standards/security-layer-model`, as the reference instance
did, because a pinned path in the governing form is a standard version by
another name. The field is kept; only the pin is gone.
- **No layer value re-spelled.** INTENT says `Engine`, layer.yaml says
`engine`; both unchanged.
- `scripts/check_layer_conformance.py`: `standard_version` was a required key,
so removing the field alone would have exited 2 MALFORMED. It now reads
INTENT.md as the governing form, requires the derived marking, rejects a
returning `standard_version` in either form, validates both layer values
against the closed four-token vocabulary (Taxonomy included) after an ASCII
fold, and reports a post-fold disagreement between the forms as a finding.
- `tests/test_layer_conformance.py`: per-file equality on the layer spelling
replaced by fold assertions; new tests for fold agreement, a real
disagreement (finding, exit 1), the closed vocabulary (`surface` exit 2,
`Taxonomy` admitted), and a returning `standard_version` (exit 2).
Full suite: 430 passed.
`role:` untouched (not ruled). `pep-stance.yaml` untouched (a stance map, not
a declaration). `schema_version` left at `0.1` — ops-warden bumped its own, but
the ruling does not call for it.
**Still open, not ours to answer:** where the removed version lives. A12 says
the derived conformance record already MUST carry it; our checker, like
ops-warden's, is re-runnable and emits nothing durable. ops-warden asked
gate-house (message `4220413a`); we follow that answer rather than choose.
## Exit criteria
- The published PEP stance is the shipped default and stance application is
recorded by name.
- Production live side effects require an access-engine decision record, or
remain fail-closed with a recorded stance.
- Evidence classification and destination match §9.6; completeness is not
claimed.
- SSH-CA and secret-use surfaces are either contracted with the declaring
repositories or explicitly declined in this repository's own voice.
- Bootstrap is not the steady-state engine credential.
- `layer.yaml` / `pep-stance.yaml` / INTENT frontmatter stay in one voice.
- No raw secret values in Git, State Hub, chat, prompts, workplans, evidence,
or argv.