diff --git a/docs/evidence/2026-09-21-layer-declaration-survey-after-ghdec017.json b/docs/evidence/2026-09-21-layer-declaration-survey-after-ghdec017.json index 97bca2b..2fc58c4 100644 --- a/docs/evidence/2026-09-21-layer-declaration-survey-after-ghdec017.json +++ b/docs/evidence/2026-09-21-layer-declaration-survey-after-ghdec017.json @@ -87,12 +87,12 @@ { "Repo": "key-cape", "Intent": { - "Source": "", - "Layer": "", - "Role": "", - "Found": false, - "InVocabulary": false, - "Canonical": "" + "Source": "key-cape/INTENT.md", + "Layer": "Tooling", + "Role": "—", + "Found": true, + "InVocabulary": true, + "Canonical": "Tooling" }, "File": { "Source": "", @@ -147,12 +147,12 @@ { "Repo": "net-kingdom", "Intent": { - "Source": "", - "Layer": "", + "Source": "net-kingdom/INTENT.md", + "Layer": "Taxonomy", "Role": "", - "Found": false, - "InVocabulary": false, - "Canonical": "" + "Found": true, + "InVocabulary": true, + "Canonical": "Taxonomy" }, "File": { "Source": "", @@ -167,12 +167,12 @@ { "Repo": "ops-mason", "Intent": { - "Source": "", - "Layer": "", - "Role": "", - "Found": false, - "InVocabulary": false, - "Canonical": "" + "Source": "ops-mason/INTENT.md", + "Layer": "Staff", + "Role": "pep-shaped", + "Found": true, + "InVocabulary": true, + "Canonical": "Staff" }, "File": { "Source": "", @@ -501,8 +501,15 @@ "Staff": [ "gate-house/INTENT.md", "kings-guard/INTENT.md", + "ops-mason/INTENT.md", "ops-warden/INTENT.md" ], + "Taxonomy": [ + "net-kingdom/INTENT.md" + ], + "Tooling": [ + "key-cape/INTENT.md" + ], "engine": [ "approval-engine/layer.yaml", "audit-core/layer.yaml", @@ -517,12 +524,8 @@ "ops-warden/layer.yaml" ] }, - "undeclared": [ - "key-cape", - "net-kingdom", - "ops-mason" - ], - "validated_against": "net-kingdom security-layer-model v0.8 with amendments A9-A13 (A12 r2), gate-house@104f3fc (GH-DEC-2026-017, GH-DEC-2026-020)", + "undeclared": null, + "validated_against": "net-kingdom/canon/standards/security-layer-model_v0.7.md (net-kingdom@66dc491) as amended by GH-DEC-2026-017, GH-DEC-2026-020, GH-DEC-2026-021 (gate-house@39d9287)", "version_pinned": null, "volunteers": null } diff --git a/internal/layer/conformance.go b/internal/layer/conformance.go index cd15bc7..485c79d 100644 --- a/internal/layer/conformance.go +++ b/internal/layer/conformance.go @@ -58,7 +58,12 @@ func Vocabulary() []string { return append([]string(nil), canonicalLayers...) } // not a declaration, so A12 does not reach it. When the canon moves it goes // visibly stale in every run's output — which is the point (kings-guard's // VALIDATED_AGAINST pattern, adopted as the reference by GH-DEC-2026-020). -const ValidatedAgainst = "net-kingdom security-layer-model v0.8 with amendments A9-A13 (A12 r2), gate-house@104f3fc (GH-DEC-2026-017, GH-DEC-2026-020)" +// +// It names the ACCEPTED v0.7 text plus the gate-house decisions this checker +// enforces beyond it, pinned by commit (GH-DEC-2026-021 §2). v0.8 is held under +// GH-DEC-2026-019, so a run must not claim a check against it; the re-point to +// v0.8 belongs to the post-flip commit (GH-WP-0004-T11). +const ValidatedAgainst = "net-kingdom/canon/standards/security-layer-model_v0.7.md (net-kingdom@66dc491) as amended by GH-DEC-2026-017, GH-DEC-2026-020, GH-DEC-2026-021 (gate-house@39d9287)" // Engine roles from §3.3. An Engine declaration must state one. var validEngineRoles = map[string]bool{ @@ -199,19 +204,29 @@ func ValidateDeclaration(decl Declaration) error { return nil } -// A12 r2 reaches CONTENT, not a key name (GH-DEC-2026-020 §1). Every checker in -// the estate, this one included, enforced A12 as "no key named standard_version" -// and so could not see the same pin as `standard: …security-layer-model_v0.7.md` -// or as `companion_version: "0.2"`. These detect a version of the standard or of -// its companion anywhere in the declaration. +// A12 reaches a PIN, not a citation (GH-DEC-2026-020 §1, narrowed by +// GH-DEC-2026-021 §1). This detector is converged on the estate reference in +// ops-warden's playbook (wiki/playbooks/netkingdom-layer-declaration.md; +// GH-DEC-2026-021 §3), with the one addition that ruling makes. A pin is: +// +// - a key naming a standard or companion version (standard_version, +// companion_version, standard_version_reviewed), with a value; +// - a version in a path or file-name token of any value (`_v0.7`, +// `-v0.8.md`, `@0.7`) — the reference errs wide here, deliberately; +// - any version token (v?N.N) in the value of a `standard:` or `companion:` +// key (the kings-guard addition: `standard: security-layer-model v0.7`). +// +// Not reached: a revision cited in prose ("the v0.5 scope rule"), which is +// provenance; `schema_version`; a key such as `intent_version` that names no +// standard or companion; and comments, which a YAML parse never surfaces. var ( - // pinKeys are keys whose presence with a value IS the pin (§1, §2). - pinKeys = map[string]bool{"standard_version": true, "companion_version": true} - // versionedRef matches a reference to the standard or its companion that - // carries a version in its path or name, whatever key holds it. - versionedRef = regexp.MustCompile(`(?i)(security-layer-model|security-companion|standard-companion)[^\s]*?[_@/-]v?\d+(\.\d+)+`) - // versionToken matches a bare version, e.g. 0.8 or v0.7, bounded so a date - // or a decision id does not count. Applied only under standard*/companion* keys. + // versionKey is the reference VERSION_KEY. + versionKey = regexp.MustCompile(`(?i)(standard|companion).*version|version.*(standard|companion)`) + // versionInValue is the reference VERSION_IN_VALUE. Go's RE2 has no \b, so + // the trailing word boundary is spelled out. + versionInValue = regexp.MustCompile(`(?i)(?:[_\-.]v\d+(?:\.\d+)*(?:\.md)?(?:[^a-z0-9_]|$))|@v?\d+\.\d+`) + // versionToken is the GH-DEC-2026-021 §3 addition, applied only to the + // value of a `standard:` or `companion:` key. versionToken = regexp.MustCompile(`(?i)(?:^|[^a-z0-9.])v?\d+\.\d+`) ) @@ -279,19 +294,20 @@ func walkPins(n *yaml.Node, key string, out *[]string) { if notReached[name] { continue } - if versionedRef.MatchString(k.Value) { - *out = append(*out, fmt.Sprintf("key %q names a versioned standard or companion", k.Value)) - } - if pinKeys[name] && !isEmptyNode(v) { - *out = append(*out, fmt.Sprintf("%s: %s", k.Value, nodeText(v))) + if versionKey.MatchString(name) { + // An empty key carries no version, so it is not a pin; the + // reference flags the key alone. flex-auth's own Declaration + // keeps an always-empty standard_version field. + if !isEmptyNode(v) { + *out = append(*out, fmt.Sprintf("%s: %s", k.Value, nodeText(v))) + } continue } walkPins(v, name, out) } case yaml.ScalarNode: - standardish := key == "standard" || key == "companion" || - strings.HasPrefix(key, "standard_") || strings.HasPrefix(key, "companion_") - if versionedRef.MatchString(n.Value) || (standardish && versionToken.MatchString(n.Value)) { + identity := key == "standard" || key == "companion" + if versionInValue.MatchString(n.Value) || (identity && versionToken.MatchString(n.Value)) { *out = append(*out, fmt.Sprintf("%s: %s", dash(key), n.Value)) } case yaml.AliasNode: diff --git a/internal/layer/conformance_test.go b/internal/layer/conformance_test.go index 7708022..f7ddba9 100644 --- a/internal/layer/conformance_test.go +++ b/internal/layer/conformance_test.go @@ -217,6 +217,10 @@ func TestVersionAnywhereInDeclarationIsFound(t *testing.T) { "bare version under standard": "layer: Engine\nstandard: \"v0.8\"\n", "nested versioned path": "layer: Engine\nassented_by:\n - ref: security-layer-model_v0.8.md\n", "version in a list of sources": "layer: Engine\nsources: [net-kingdom/canon/standards/security-layer-model_v0.6.md]\n", + "prose version under standard": "layer: Engine\nstandard: security-layer-model v0.7\n", + "version under companion": "layer: Engine\ncompanion: SECURITY-COMPANION 0.2\n", + "reviewed-version key": "layer: Engine\nstandard_version_reviewed: \"0.7\"\n", + "at-version reference": "layer: Engine\nsource: net-kingdom@0.7\n", } { pins, err := layer.VersionPins(doc) if err != nil { @@ -240,7 +244,11 @@ func TestVersionPinsLeavesWhatA12DoesNotReach(t *testing.T) { "companion: net-kingdom/SECURITY-COMPANION.md\n" + "declared_at: \"2026-08-29\"\n" + "declared_by: decisions/decisions.md FLEX-DEC-2026-001\n" + - "companion_version:\n" + "companion_version:\n" + + // GH-DEC-2026-021 §1: a revision cited in prose is provenance, not a + // pin, even under a standard_*-prefixed key that names no version. + "standard_note: the standard's v0.5 scope rule\n" + + "rationale: adopted under security-layer-model v0.7 and GH-DEC-2026-020\n" pins, err := layer.VersionPins(doc) if err != nil { t.Fatal(err)