Widen A12 enforcement from the key name to the declaration's content (GH-DEC-2026-020).
internal/layer/conformance.go enforced A12 as "no key named standard_version", and so could not see the same pin as a versioned standard: path or as companion_version. It now detects a version of the standard or its companion in any key or value of the declaration (INTENT.md frontmatter, layer.yaml), including a version in a path, and excludes comments and schema_version. It refuses to be applied to pep-stance.yaml, pip-claims.yaml or evidence-classification.yaml, which A12 r2 does not reach (§3). Every run of check_layer_conformance and of the estate survey now prints the standard version it checks against (layer.ValidatedAgainst, kings-guard's pattern) and its scope (§4). The survey applies the same detection to peers' declarations; the receipt is refreshed because the survey's output changed (no peer declaration currently carries a version). Tests fail if a versioned standard: path or a companion_version comes back. flex-auth's own INTENT.md needed no change. 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
This commit is contained in:
parent
2be655703d
commit
e0c6c4389d
7 changed files with 334 additions and 17 deletions
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"checks_only": "the closed four-token §3 vocabulary, ASCII case folded per GH-DEC-2026-017 §2; no flex-auth house rules applied to peers",
|
"checks_only": "the closed four-token §3 vocabulary, ASCII case folded per GH-DEC-2026-017 §2; and A12 r2 (GH-DEC-2026-020): no standard or companion version in any key or value of INTENT.md frontmatter or layer.yaml; stance, claims and classification maps not read; no flex-auth house rules applied to peers",
|
||||||
"derived_at": "run time",
|
"derived_at": "run time",
|
||||||
"off_vocab": null,
|
"off_vocab": null,
|
||||||
"root": "/home/worsch",
|
"root": "/home/worsch",
|
||||||
|
|
@ -522,5 +522,7 @@
|
||||||
"net-kingdom",
|
"net-kingdom",
|
||||||
"ops-mason"
|
"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)",
|
||||||
|
"version_pinned": null,
|
||||||
"volunteers": null
|
"volunteers": null
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,15 @@ func CanonicalLayer(declared string) (string, bool) {
|
||||||
// Vocabulary returns the four §3 tokens in their canonical spelling.
|
// Vocabulary returns the four §3 tokens in their canonical spelling.
|
||||||
func Vocabulary() []string { return append([]string(nil), canonicalLayers...) }
|
func Vocabulary() []string { return append([]string(nil), canonicalLayers...) }
|
||||||
|
|
||||||
|
// ValidatedAgainst names the text this package checks against. A12 r2
|
||||||
|
// (GH-DEC-2026-020 §4) moves version-scoped state out of the declaration and
|
||||||
|
// into the run: every run MUST print the version or commit of the standard it
|
||||||
|
// checked against, and its scope. This constant lives in the checker, which is
|
||||||
|
// 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)"
|
||||||
|
|
||||||
// Engine roles from §3.3. An Engine declaration must state one.
|
// Engine roles from §3.3. An Engine declaration must state one.
|
||||||
var validEngineRoles = map[string]bool{
|
var validEngineRoles = map[string]bool{
|
||||||
"PDP": true,
|
"PDP": true,
|
||||||
|
|
@ -116,6 +125,11 @@ func Check(root string) error {
|
||||||
return fmt.Errorf("%s names %q, which is not on disk: §11's derived-artifact rule needs the artifact, not the pointer", named.field, named.path)
|
return fmt.Errorf("%s names %q, which is not on disk: §11's derived-artifact rule needs the artifact, not the pointer", named.field, named.path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if pins, err := DeclarationVersionPins(filepath.Join(root, "INTENT.md")); err != nil {
|
||||||
|
return err
|
||||||
|
} else if len(pins) > 0 {
|
||||||
|
return fmt.Errorf("layer declaration carries a standard or companion version (A12 r2, GH-DEC-2026-020 §1-§2): %s", strings.Join(pins, "; "))
|
||||||
|
}
|
||||||
hits, err := ScanToolingClients(root)
|
hits, err := ScanToolingClients(root)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -185,6 +199,123 @@ func ValidateDeclaration(decl Declaration) error {
|
||||||
return nil
|
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.
|
||||||
|
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.
|
||||||
|
versionToken = regexp.MustCompile(`(?i)(?:^|[^a-z0-9.])v?\d+\.\d+`)
|
||||||
|
)
|
||||||
|
|
||||||
|
// notReached are keys A12 r2 states it does not reach: the version of a
|
||||||
|
// declaration file's own schema is not a version of the standard. Comments are
|
||||||
|
// not reached either, and a YAML parse never surfaces them.
|
||||||
|
var notReached = map[string]bool{"schema_version": true}
|
||||||
|
|
||||||
|
// DeclarationVersionPins reads a declaration — INTENT.md frontmatter, or a bare
|
||||||
|
// derived declaration file such as layer.yaml — and returns every key or value
|
||||||
|
// carrying a version of the standard or its companion.
|
||||||
|
//
|
||||||
|
// It MUST be pointed at a declaration only. A stance map, claims map or
|
||||||
|
// evidence classification (pep-stance.yaml, pip-claims.yaml,
|
||||||
|
// evidence-classification.yaml) SHOULD carry the version of the clause text it
|
||||||
|
// answers, and A12 r2 forbids a run applying this rule there (GH-DEC-2026-020
|
||||||
|
// §3). Nothing in this package calls it on such a file.
|
||||||
|
func DeclarationVersionPins(path string) ([]string, error) {
|
||||||
|
if !isDeclarationPath(path) {
|
||||||
|
return nil, fmt.Errorf("%s is not a §11 declaration (INTENT.md or layer.yaml); A12 r2 does not reach it and a run MUST NOT apply it there", filepath.Base(path))
|
||||||
|
}
|
||||||
|
body, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("read layer declaration: %w", err)
|
||||||
|
}
|
||||||
|
doc := string(body)
|
||||||
|
if strings.HasSuffix(path, ".md") {
|
||||||
|
if doc, err = splitFrontmatter(doc); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return VersionPins(doc)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isDeclarationPath(path string) bool {
|
||||||
|
switch filepath.Base(path) {
|
||||||
|
case "INTENT.md", "layer.yaml", "layer.yml":
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// VersionPins scans a bare declaration YAML document. Findings are stable,
|
||||||
|
// human-readable "key: value" strings in document order.
|
||||||
|
func VersionPins(doc string) ([]string, error) {
|
||||||
|
var root yaml.Node
|
||||||
|
if err := yaml.Unmarshal([]byte(doc), &root); err != nil {
|
||||||
|
return nil, fmt.Errorf("parse layer declaration: %w", err)
|
||||||
|
}
|
||||||
|
var out []string
|
||||||
|
walkPins(&root, "", &out)
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func walkPins(n *yaml.Node, key string, out *[]string) {
|
||||||
|
switch n.Kind {
|
||||||
|
case yaml.DocumentNode, yaml.SequenceNode:
|
||||||
|
for _, c := range n.Content {
|
||||||
|
walkPins(c, key, out)
|
||||||
|
}
|
||||||
|
case yaml.MappingNode:
|
||||||
|
for i := 0; i+1 < len(n.Content); i += 2 {
|
||||||
|
k, v := n.Content[i], n.Content[i+1]
|
||||||
|
name := strings.ToLower(strings.TrimSpace(k.Value))
|
||||||
|
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)))
|
||||||
|
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)) {
|
||||||
|
*out = append(*out, fmt.Sprintf("%s: %s", dash(key), n.Value))
|
||||||
|
}
|
||||||
|
case yaml.AliasNode:
|
||||||
|
if n.Alias != nil {
|
||||||
|
walkPins(n.Alias, key, out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func isEmptyNode(n *yaml.Node) bool {
|
||||||
|
if n.Kind == yaml.ScalarNode {
|
||||||
|
return n.Tag == "!!null" || strings.TrimSpace(n.Value) == ""
|
||||||
|
}
|
||||||
|
return len(n.Content) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func nodeText(n *yaml.Node) string {
|
||||||
|
if n.Kind == yaml.ScalarNode {
|
||||||
|
return n.Value
|
||||||
|
}
|
||||||
|
b, _ := yaml.Marshal(n)
|
||||||
|
return strings.TrimSpace(string(b))
|
||||||
|
}
|
||||||
|
|
||||||
// ScanToolingClients returns production Go files that invoke OpenBao/Vault.
|
// ScanToolingClients returns production Go files that invoke OpenBao/Vault.
|
||||||
func ScanToolingClients(root string) ([]string, error) {
|
func ScanToolingClients(root string) ([]string, error) {
|
||||||
var hits []string
|
var hits []string
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/netkingdom/flex-auth/internal/layer"
|
"github.com/netkingdom/flex-auth/internal/layer"
|
||||||
|
|
@ -202,3 +203,99 @@ func repoRoot(t *testing.T) string {
|
||||||
}
|
}
|
||||||
return filepath.Clean(filepath.Join(filepath.Dir(file), "..", ".."))
|
return filepath.Clean(filepath.Join(filepath.Dir(file), "..", ".."))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A12 r2 reaches CONTENT, not a key name (GH-DEC-2026-020 §1, §2). Each of these
|
||||||
|
// is a version of the standard or its companion that a key-name check on
|
||||||
|
// `standard_version` could not see. If any of them comes back into a
|
||||||
|
// declaration, this test fails.
|
||||||
|
func TestVersionAnywhereInDeclarationIsFound(t *testing.T) {
|
||||||
|
for name, doc := range map[string]string{
|
||||||
|
"versioned standard path": "layer: Engine\nstandard: net-kingdom/canon/standards/security-layer-model_v0.7.md\n",
|
||||||
|
"versioned companion path": "layer: Engine\ncompanion: net-kingdom/SECURITY-COMPANION_v0.2.md\n",
|
||||||
|
"companion_version": "layer: Engine\ncompanion_version: \"0.2\"\n",
|
||||||
|
"standard_version": "layer: Engine\nstandard_version: \"0.8\"\n",
|
||||||
|
"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",
|
||||||
|
} {
|
||||||
|
pins, err := layer.VersionPins(doc)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%s: %v", name, err)
|
||||||
|
}
|
||||||
|
if len(pins) == 0 {
|
||||||
|
t.Errorf("%s: version was not detected in %q", name, doc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// What A12 r2 states it does NOT reach: comments, schema_version, and versions
|
||||||
|
// that are not versions of the standard or its companion. Unversioned standard
|
||||||
|
// and companion paths are the conforming form.
|
||||||
|
func TestVersionPinsLeavesWhatA12DoesNotReach(t *testing.T) {
|
||||||
|
doc := "# declared against security-layer-model_v0.7.md, kept as history\n" +
|
||||||
|
"schema_version: \"0.2\"\n" +
|
||||||
|
"intent_version: 0.1.0\n" +
|
||||||
|
"layer: engine # v0.8 comment\n" +
|
||||||
|
"standard: net-kingdom/canon/standards/security-layer-model\n" +
|
||||||
|
"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"
|
||||||
|
pins, err := layer.VersionPins(doc)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(pins) != 0 {
|
||||||
|
t.Fatalf("A12 r2 reached what it does not reach: %v", pins)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check must fail on a declaration whose only pin is in the standard: path.
|
||||||
|
// Stance, claims and classification maps SHOULD carry the version of the text
|
||||||
|
// they answer; a run MUST NOT apply A12 to them (GH-DEC-2026-020 §3).
|
||||||
|
func TestCheckRejectsVersionedStandardPathButNotStanceMaps(t *testing.T) {
|
||||||
|
good := "---\nlayer: Engine\nrole: PDP\nconformance_record: record.md\nsource_of_evidence: false\n" +
|
||||||
|
"standard: net-kingdom/canon/standards/security-layer-model\n---\n\n# x\n"
|
||||||
|
dir := t.TempDir()
|
||||||
|
for name, body := range map[string]string{
|
||||||
|
"INTENT.md": good,
|
||||||
|
"record.md": "x\n",
|
||||||
|
"pep-stance.yaml": "standard_version: \"0.8\"\n",
|
||||||
|
"pip-claims.yaml": "standard_version: \"0.8\"\nstandard: security-layer-model_v0.8.md\n",
|
||||||
|
"evidence-classification.yaml": "standard_version: \"0.8\"\n",
|
||||||
|
} {
|
||||||
|
if err := os.WriteFile(filepath.Join(dir, name), []byte(body), 0o644); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := layer.Check(dir); err != nil {
|
||||||
|
t.Fatalf("a version in a stance/claims/classification map failed the declaration check: %v", err)
|
||||||
|
}
|
||||||
|
for _, f := range []string{"pep-stance.yaml", "pip-claims.yaml", "evidence-classification.yaml"} {
|
||||||
|
if _, err := layer.DeclarationVersionPins(filepath.Join(dir, f)); err == nil {
|
||||||
|
t.Errorf("DeclarationVersionPins accepted %s; A12 r2 must not be applied to it", f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bad := strings.Replace(good, "security-layer-model\n", "security-layer-model_v0.7.md\n", 1)
|
||||||
|
if err := os.WriteFile(filepath.Join(dir, "INTENT.md"), []byte(bad), 0o644); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := layer.Check(dir); err == nil {
|
||||||
|
t.Fatal("a versioned standard: path in INTENT.md was accepted")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// flex-auth's own declaration carries no version under any key (A12 r2).
|
||||||
|
func TestOwnDeclarationCarriesNoVersionAnywhere(t *testing.T) {
|
||||||
|
pins, err := layer.DeclarationVersionPins(filepath.Join(repoRoot(t), "INTENT.md"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(pins) != 0 {
|
||||||
|
t.Fatalf("INTENT.md declaration carries a version: %v", pins)
|
||||||
|
}
|
||||||
|
if layer.ValidatedAgainst == "" {
|
||||||
|
t.Fatal("the checker must state the version it validates against on every run")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,11 @@ type Form struct {
|
||||||
InVocabulary bool
|
InVocabulary bool
|
||||||
// Canonical is the §4 column spelling of Layer, empty when out of vocabulary.
|
// Canonical is the §4 column spelling of Layer, empty when out of vocabulary.
|
||||||
Canonical string
|
Canonical string
|
||||||
|
// VersionPins lists every key or value of this declaration form carrying a
|
||||||
|
// version of the standard or its companion (A12 r2, GH-DEC-2026-020 §1-§2).
|
||||||
|
// Read from the declaration only — never from a stance, claims or
|
||||||
|
// classification map, which A12 r2 does not reach (§3).
|
||||||
|
VersionPins []string `json:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// SurveyRow is one repository's declaration as observed from outside. It holds
|
// SurveyRow is one repository's declaration as observed from outside. It holds
|
||||||
|
|
@ -144,8 +149,11 @@ func EstateScope(repos []string) Scope {
|
||||||
|
|
||||||
// SurveyDeclarations reads both §11 forms for every repository under root.
|
// SurveyDeclarations reads both §11 forms for every repository under root.
|
||||||
//
|
//
|
||||||
// It checks ONE property: whether each layer: value sits in the §3 vocabulary
|
// It checks TWO properties, both §11 statute rather than house rule: whether
|
||||||
// as written. It deliberately does not apply flex-auth's own declaration rules
|
// each layer: value sits in the §3 vocabulary (case folded), and whether the
|
||||||
|
// declaration carries a standard or companion version anywhere (A12 r2). It
|
||||||
|
// reads INTENT.md frontmatter and layer.yaml only, so a peer's pep-stance.yaml,
|
||||||
|
// pip-claims.yaml or evidence-classification.yaml is never graded. It deliberately does not apply flex-auth's own declaration rules
|
||||||
// — pep_stance, tooling_contacts, conformance_record — to any other repository.
|
// — pep_stance, tooling_contacts, conformance_record — to any other repository.
|
||||||
// Those are flex-auth's invariants for flex-auth, and §11 is explicit that a
|
// Those are flex-auth's invariants for flex-auth, and §11 is explicit that a
|
||||||
// layer stated about a repository by another repository is not a declaration.
|
// layer stated about a repository by another repository is not a declaration.
|
||||||
|
|
@ -179,7 +187,9 @@ func readForm(root, rel string) Form {
|
||||||
return Form{}
|
return Form{}
|
||||||
}
|
}
|
||||||
canon, ok := CanonicalLayer(decl.Layer)
|
canon, ok := CanonicalLayer(decl.Layer)
|
||||||
|
pins, _ := DeclarationVersionPins(path)
|
||||||
return Form{
|
return Form{
|
||||||
|
VersionPins: pins,
|
||||||
Source: rel,
|
Source: rel,
|
||||||
Layer: decl.Layer,
|
Layer: decl.Layer,
|
||||||
Role: decl.Role,
|
Role: decl.Role,
|
||||||
|
|
@ -261,6 +271,20 @@ func SelfDisagreeing(rows []SurveyRow) []SurveyRow {
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// VersionPinned lists each declaration form carrying a standard or companion
|
||||||
|
// version, as "source: finding".
|
||||||
|
func VersionPinned(rows []SurveyRow) []string {
|
||||||
|
var out []string
|
||||||
|
for _, r := range rows {
|
||||||
|
for _, f := range []Form{r.Intent, r.File} {
|
||||||
|
for _, p := range f.VersionPins {
|
||||||
|
out = append(out, f.Source+": "+p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
// VolunteerDeclarations lists repositories outside §4 that declared anyway. The
|
// VolunteerDeclarations lists repositories outside §4 that declared anyway. The
|
||||||
// correct report for them is "declared voluntarily, outside catalog scope"
|
// correct report for them is "declared voluntarily, outside catalog scope"
|
||||||
// (GH-DEC-2026-017 §4): treating a volunteer's declaration as a non-conformance
|
// (GH-DEC-2026-017 §4): treating a volunteer's declaration as a non-conformance
|
||||||
|
|
@ -280,7 +304,9 @@ func VolunteerDeclarations(rows []SurveyRow) []string {
|
||||||
// the scope because §11 requires a run to state what it ranged over.
|
// the scope because §11 requires a run to state what it ranged over.
|
||||||
func FormatSurvey(scope Scope, rows []SurveyRow) string {
|
func FormatSurvey(scope Scope, rows []SurveyRow) string {
|
||||||
var b strings.Builder
|
var b strings.Builder
|
||||||
fmt.Fprintf(&b, "Scope: %s — %s\n\n", scope.Name, scope.Statement)
|
fmt.Fprintf(&b, "Validated against: %s\n", ValidatedAgainst)
|
||||||
|
fmt.Fprintf(&b, "Scope: %s — %s\n", scope.Name, scope.Statement)
|
||||||
|
fmt.Fprintf(&b, "Reads: INTENT.md frontmatter and layer.yaml/layer.yml per repository; no stance, claims or classification map is read.\n\n")
|
||||||
fmt.Fprintf(&b, "%-18s %-16s %-16s %s\n", "REPO", "INTENT.md", "DECL FILE", "NOTE")
|
fmt.Fprintf(&b, "%-18s %-16s %-16s %s\n", "REPO", "INTENT.md", "DECL FILE", "NOTE")
|
||||||
for _, r := range rows {
|
for _, r := range rows {
|
||||||
note := ""
|
note := ""
|
||||||
|
|
@ -303,6 +329,11 @@ func FormatSurvey(scope Scope, rows []SurveyRow) string {
|
||||||
note += "; " + f.Layer + " is outside the closed §3 vocabulary"
|
note += "; " + f.Layer + " is outside the closed §3 vocabulary"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for _, f := range []Form{r.Intent, r.File} {
|
||||||
|
if len(f.VersionPins) > 0 {
|
||||||
|
note += "; " + f.Source + " carries a version (A12 r2)"
|
||||||
|
}
|
||||||
|
}
|
||||||
if r.Declared() && !r.InCatalog {
|
if r.Declared() && !r.InCatalog {
|
||||||
note += "; declared voluntarily, outside §4 catalog scope"
|
note += "; declared voluntarily, outside §4 catalog scope"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package layer_test
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/netkingdom/flex-auth/internal/layer"
|
"github.com/netkingdom/flex-auth/internal/layer"
|
||||||
|
|
@ -172,3 +173,37 @@ func TestPeerWithForeignFieldShapesIsStillSurveyed(t *testing.T) {
|
||||||
t.Fatalf("audit-core's layer.yaml was dropped: %+v", rows[0].File)
|
t.Fatalf("audit-core's layer.yaml was dropped: %+v", rows[0].File)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The survey applies A12 r2 to peers' declarations — both forms — and states
|
||||||
|
// the version it checks against on every run.
|
||||||
|
func TestSurveyFindsVersionInPeerDeclaration(t *testing.T) {
|
||||||
|
root := t.TempDir()
|
||||||
|
writeRepo(t, root, "audit-core", "Engine", "")
|
||||||
|
writeRepo(t, root, "approval-engine", "Engine", "")
|
||||||
|
writeRepo(t, root, "user-engine", "Engine", "")
|
||||||
|
must := func(err error) {
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
must(os.WriteFile(filepath.Join(root, "audit-core", "INTENT.md"),
|
||||||
|
[]byte("---\nlayer: Engine\nstandard: canon/standards/security-layer-model_v0.7.md\n---\n"), 0o644))
|
||||||
|
must(os.WriteFile(filepath.Join(root, "approval-engine", "layer.yaml"),
|
||||||
|
[]byte("schema_version: \"0.1\"\nlayer: engine\ncompanion_version: \"0.2\"\n"), 0o644))
|
||||||
|
// A stance map is not a declaration and must not be graded.
|
||||||
|
must(os.WriteFile(filepath.Join(root, "user-engine", "pep-stance.yaml"),
|
||||||
|
[]byte("standard_version: \"0.8\"\n"), 0o644))
|
||||||
|
|
||||||
|
rows, err := layer.SurveyDeclarations(root, []string{"approval-engine", "audit-core", "user-engine"})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
got := layer.VersionPinned(rows)
|
||||||
|
if len(got) != 2 {
|
||||||
|
t.Fatalf("VersionPinned = %v; want the audit-core standard: path and approval-engine companion_version only", got)
|
||||||
|
}
|
||||||
|
out := layer.FormatSurvey(layer.EstateScope([]string{"approval-engine", "audit-core", "user-engine"}), rows)
|
||||||
|
if !strings.Contains(out, layer.ValidatedAgainst) || !strings.Contains(out, "Scope:") {
|
||||||
|
t.Fatal("every survey run must print the version it checks against and its scope")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
// Command check_layer_conformance asserts the INTENT.md layer declaration
|
// Command check_layer_conformance asserts the INTENT.md layer declaration
|
||||||
// and that no Tooling client exists in production Go sources.
|
// and that no Tooling client exists in production Go sources.
|
||||||
|
//
|
||||||
|
// Every run — pass or fail — prints the standard version it checks against and
|
||||||
|
// the scope it ranged over, as A12 r2 requires (GH-DEC-2026-020 §4).
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
@ -16,6 +19,8 @@ func main() {
|
||||||
fmt.Fprintln(os.Stderr, err)
|
fmt.Fprintln(os.Stderr, err)
|
||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
}
|
}
|
||||||
|
fmt.Printf("Validated against: %s\n", layer.ValidatedAgainst)
|
||||||
|
fmt.Printf("Scope: this repository only — %s/INTENT.md frontmatter (the §11 declaration), the files it names, and production Go sources under cmd/, internal/, pkg/. Stance, claims and classification maps are not read.\n", filepath.Base(root))
|
||||||
if _, err := os.Stat(filepath.Join(root, "INTENT.md")); err != nil {
|
if _, err := os.Stat(filepath.Join(root, "INTENT.md")); err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "INTENT.md not found in %s\n", root)
|
fmt.Fprintf(os.Stderr, "INTENT.md not found in %s\n", root)
|
||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
|
|
@ -24,5 +29,5 @@ func main() {
|
||||||
fmt.Fprintln(os.Stderr, err)
|
fmt.Fprintln(os.Stderr, err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
fmt.Println("PASS — Engine/PDP declaration parses; no Tooling client in the tree.")
|
fmt.Println("PASS — Engine/PDP declaration parses; no standard or companion version anywhere in it; no Tooling client in the tree.")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,12 @@
|
||||||
// shell survey, which is the same defect in a different costume — a finding
|
// shell survey, which is the same defect in a different costume — a finding
|
||||||
// nobody can reproduce is an assertion. This makes it a command.
|
// nobody can reproduce is an assertion. This makes it a command.
|
||||||
//
|
//
|
||||||
// It checks ONE property: whether each layer: value sits in the §3 vocabulary
|
// It checks TWO properties: whether each layer: value sits in the §3
|
||||||
// as written, case-sensitively. It does not apply flex-auth's own declaration
|
// vocabulary (ASCII case folded, GH-DEC-2026-017 §2), and whether a
|
||||||
|
// declaration carries a standard or companion version in any key or value
|
||||||
|
// (A12 r2, GH-DEC-2026-020). It reads declarations only — INTENT.md frontmatter
|
||||||
|
// and layer.yaml — never stance, claims or classification maps. Every run prints
|
||||||
|
// the version it checks against and its scope. It does not apply flex-auth's own declaration
|
||||||
// rules to any other repository, and it does not grade anyone: §11 is explicit
|
// rules to any other repository, and it does not grade anyone: §11 is explicit
|
||||||
// that a layer stated about a repository by another repository is not a
|
// that a layer stated about a repository by another repository is not a
|
||||||
// declaration.
|
// declaration.
|
||||||
|
|
@ -131,22 +135,34 @@ func main() {
|
||||||
fmt.Printf("\nOutside the closed §3 vocabulary (four tokens, case folded): %s\n", strings.Join(offVocab, ", "))
|
fmt.Printf("\nOutside the closed §3 vocabulary (four tokens, case folded): %s\n", strings.Join(offVocab, ", "))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pinned := layer.VersionPinned(rows)
|
||||||
|
if len(pinned) > 0 {
|
||||||
|
fmt.Printf("\nDeclarations carrying a standard or companion version (A12 r2): %d\n", len(pinned))
|
||||||
|
for _, p := range pinned {
|
||||||
|
fmt.Printf(" %s\n", p)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Println("\nNo declaration carries a standard or companion version (A12 r2).")
|
||||||
|
}
|
||||||
|
|
||||||
if vol := layer.VolunteerDeclarations(rows); len(vol) > 0 {
|
if vol := layer.VolunteerDeclarations(rows); len(vol) > 0 {
|
||||||
fmt.Printf("\nDeclared voluntarily, outside §4 catalog scope — welcome, and NOT a §11\nnon-conformance: %s\n", strings.Join(vol, ", "))
|
fmt.Printf("\nDeclared voluntarily, outside §4 catalog scope — welcome, and NOT a §11\nnon-conformance: %s\n", strings.Join(vol, ", "))
|
||||||
}
|
}
|
||||||
|
|
||||||
if *jsonOut != "" {
|
if *jsonOut != "" {
|
||||||
receipt := map[string]any{
|
receipt := map[string]any{
|
||||||
"derived_at": "run time",
|
"derived_at": "run time",
|
||||||
"scope": scope,
|
"validated_against": layer.ValidatedAgainst,
|
||||||
"root": dir,
|
"version_pinned": pinned,
|
||||||
"rows": rows,
|
"scope": scope,
|
||||||
"spellings": spellings,
|
"root": dir,
|
||||||
"undeclared": undeclared,
|
"rows": rows,
|
||||||
"off_vocab": offVocab,
|
"spellings": spellings,
|
||||||
"self_disagreeing": disagree,
|
"undeclared": undeclared,
|
||||||
"volunteers": layer.VolunteerDeclarations(rows),
|
"off_vocab": offVocab,
|
||||||
"checks_only": "the closed four-token §3 vocabulary, ASCII case folded per GH-DEC-2026-017 §2; no flex-auth house rules applied to peers",
|
"self_disagreeing": disagree,
|
||||||
|
"volunteers": layer.VolunteerDeclarations(rows),
|
||||||
|
"checks_only": "the closed four-token §3 vocabulary, ASCII case folded per GH-DEC-2026-017 §2; and A12 r2 (GH-DEC-2026-020): no standard or companion version in any key or value of INTENT.md frontmatter or layer.yaml; stance, claims and classification maps not read; no flex-auth house rules applied to peers",
|
||||||
}
|
}
|
||||||
b, err := json.MarshalIndent(receipt, "", " ")
|
b, err := json.MarshalIndent(receipt, "", " ")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue