Widen A12 enforcement from the key name to the declaration's content (GH-DEC-2026-020).
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s
Build and Publish Container Image / build-and-push (push) Successful in 1m11s

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:
tegwick 2026-09-21 09:39:46 +02:00
parent 2be655703d
commit e0c6c4389d
7 changed files with 334 additions and 17 deletions

View file

@ -25,6 +25,11 @@ type Form struct {
InVocabulary bool
// Canonical is the §4 column spelling of Layer, empty when out of vocabulary.
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
@ -144,8 +149,11 @@ func EstateScope(repos []string) Scope {
// SurveyDeclarations reads both §11 forms for every repository under root.
//
// It checks ONE property: whether each layer: value sits in the §3 vocabulary
// as written. It deliberately does not apply flex-auth's own declaration rules
// It checks TWO properties, both §11 statute rather than house rule: whether
// 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.
// 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.
@ -179,7 +187,9 @@ func readForm(root, rel string) Form {
return Form{}
}
canon, ok := CanonicalLayer(decl.Layer)
pins, _ := DeclarationVersionPins(path)
return Form{
VersionPins: pins,
Source: rel,
Layer: decl.Layer,
Role: decl.Role,
@ -261,6 +271,20 @@ func SelfDisagreeing(rows []SurveyRow) []SurveyRow {
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
// correct report for them is "declared voluntarily, outside catalog scope"
// (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.
func FormatSurvey(scope Scope, rows []SurveyRow) string {
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")
for _, r := range rows {
note := ""
@ -303,6 +329,11 @@ func FormatSurvey(scope Scope, rows []SurveyRow) string {
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 {
note += "; declared voluntarily, outside §4 catalog scope"
}