Allow absent version headers below structural controls
This commit is contained in:
@@ -334,6 +334,10 @@ declaration is accepted by the consumer's checker; it does not establish that
|
||||
each fragment conforms to a complete application schema. Final assembled
|
||||
decoding and application validation still run once.
|
||||
|
||||
A header that is absent below a structural control follows the same omission
|
||||
policy. Actual header/control overlap is rejected, and ordinary structural
|
||||
validation and main-version ownership protection continue independently.
|
||||
|
||||
Accepted non-main declarations are consumed from their owned payload before
|
||||
composition; their authored ancestors remain, even if empty. The main
|
||||
declaration remains in the application. Ordinary release metadata should use a
|
||||
|
||||
@@ -707,3 +707,14 @@ classification. Focused source-version/sensitivity/history tests,
|
||||
`GOTOOLCHAIN=local GOPROXY=off go test -race ./...` passed on
|
||||
`go1.26.9 linux/amd64`. Existing historical/dependency propagation and neighboring
|
||||
public-value coverage remain green; no new sensitivity policy was introduced.
|
||||
|
||||
Additional remediation regressions protect import opt-in
|
||||
(`TestFacadeSourceVersionModesPreserveImportOptIn`), complete absence paths
|
||||
(`TestSourceVersionAbsentPathKeepsUnresolvedSegmentIntent` and
|
||||
`TestFacadeSourceVersionAbsenceKeepsCompleteEvidencePath`), and optional missing
|
||||
headers below controls
|
||||
(`TestFacadeSourceVersionMissingHeaderDoesNotOverlapControl`). Each failed
|
||||
before its production correction and passes afterward. All four independent
|
||||
review probes, the final everyday check, and the full race suite now pass on
|
||||
`go1.26.9 linux/amd64`. The remediation record contains completion evidence;
|
||||
the agreed source-version policy and release gates are unchanged.
|
||||
|
||||
@@ -158,7 +158,8 @@ state. Main's local header is retained, accepted non-main headers are removed
|
||||
before placement/composition, and the destination-prefixed main rule reaches
|
||||
payload protection and typed conversion. Warnings, raw accounting, source
|
||||
contexts, and final loading reuse the existing pipeline. Four must-address
|
||||
issues remain. Findings 1 and 2 above remain completed historical findings.
|
||||
issues were identified and are now resolved below. Findings 1 and 2 above remain
|
||||
completed historical findings.
|
||||
|
||||
### 3. Preserve distinct semantic shapes during evidence classification (P1)
|
||||
|
||||
@@ -304,7 +305,15 @@ Required remediation:
|
||||
|
||||
### 6. Apply structural-overlap rejection only to an actual declaration (P2)
|
||||
|
||||
Status: open. This is an unintended strengthening of the agreed restriction.
|
||||
Status: complete. Overlap rejection now requires an actually present selected
|
||||
declaration. Optional absence proceeds without a non-main checker invocation;
|
||||
required absence and malformed controls still fail under their existing rules.
|
||||
The public regression failed before the fix and now covers valid inheritance
|
||||
in separate mounted scopes, required omission with NoSource main, and invalid
|
||||
inheritance controls. Existing present-overlap tests remain green. Focused
|
||||
source-version/directive/inheritance tests, the everyday check, and the full
|
||||
race suite passed on `go1.26.9 linux/amd64`. The configuration contract clarifies
|
||||
the omission boundary. This removes the unintended strengthened restriction.
|
||||
|
||||
Affected: `processSourceVersion` in
|
||||
[source_version.go](../../internal/engine/source_version.go), lines 197–219 at
|
||||
@@ -368,3 +377,16 @@ temporary directory.
|
||||
|
||||
No production fixes, repository regression tests, dependency updates, or release
|
||||
changes were made. Only this findings document was revised.
|
||||
|
||||
### Remediation completion validation
|
||||
|
||||
All four external review probes now pass against the corrected checkout,
|
||||
including both shape-collision acquisition orders. The final everyday check and
|
||||
`GOTOOLCHAIN=local GOPROXY=off go test -race ./...` passed on
|
||||
`go1.26.9 linux/amd64`. Each new regression was observed failing before its
|
||||
corresponding production fix.
|
||||
|
||||
The earlier completed Mapping diagnostic regression was rerun successfully, and
|
||||
ADR 0011 from `## Date` onward still matches its accepted text at `7248a8b`.
|
||||
No findings remain open. No dependencies, source-version policies, or release
|
||||
gates changed, and no release was published.
|
||||
|
||||
@@ -170,6 +170,68 @@ func TestFacadeSourceVersionAbsenceKeepsCompleteEvidencePath(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestFacadeSourceVersionMissingHeaderDoesNotOverlapControl(t *testing.T) {
|
||||
t.Parallel()
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
mode configkit.SourceVersionMode
|
||||
control string
|
||||
wantCode configkit.Code
|
||||
}{
|
||||
{"optional omission", configkit.SourceVersionModeIfPresent, "base", ""},
|
||||
{"required omission", configkit.SourceVersionModeRequired, "base", configkit.CodeVersionError},
|
||||
{"invalid control", configkit.SourceVersionModeIfPresent, "true", configkit.CodeInheritanceInvalid},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
main := configkit.Bytes("main", []byte("{}\n"))
|
||||
if test.mode == configkit.SourceVersionModeRequired {
|
||||
main = configkit.NoSource()
|
||||
}
|
||||
checks := 0
|
||||
path := configkit.Keys("entries", "child", "extends", "version")
|
||||
loaded, err := configkit.LoadLayered(configkit.Input{Source: main, Destination: configkit.Keys("main")}, configkit.LayeredOptions[map[string]any]{
|
||||
SourceVersionMode: test.mode,
|
||||
Definition: configkit.Definition[map[string]any]{Version: &configkit.VersionRule{
|
||||
Path: path, Check: func(version configkit.Version, _ *configkit.Validation) error {
|
||||
checks++
|
||||
if version.Present {
|
||||
t.Error("checker received a nonexistent declaration")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}},
|
||||
Underlays: []configkit.Underlay{{Inputs: []configkit.Input{{
|
||||
Source: configkit.Bytes("underlay", []byte("entries:\n base: {value: good}\n child: {extends: "+test.control+"}\n")), Destination: configkit.Keys("under"),
|
||||
}}}},
|
||||
})
|
||||
if checks != 1 {
|
||||
t.Errorf("checker ran %d times, want absent main only", checks)
|
||||
}
|
||||
if test.wantCode != "" {
|
||||
var failure *configkit.LoadError
|
||||
if loaded != nil || !errors.As(err, &failure) {
|
||||
t.Fatal("invalid input did not fail safely")
|
||||
}
|
||||
if diagnostics := failure.Diagnostics(); len(diagnostics) != 1 || diagnostics[0].Code != test.wantCode {
|
||||
t.Fatalf("diagnostics=%#v", diagnostics)
|
||||
}
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
child := loaded.Config["under"].(map[string]any)["entries"].(map[string]any)["child"].(map[string]any)
|
||||
if child["value"] != "good" {
|
||||
t.Fatal("valid sibling inheritance was not applied")
|
||||
}
|
||||
check := loaded.Sources()[1].VersionCheck
|
||||
if check == nil || check.Version.Present || check.Path.String() != path.String() {
|
||||
t.Fatal("optional omission evidence was not retained")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (reader *sourceVersionModeReader) Read(data []byte) (int, error) {
|
||||
reader.reads++
|
||||
return reader.Reader.Read(data)
|
||||
|
||||
@@ -196,7 +196,7 @@ func processSourceVersion(root *document.Node, rule *VersionRule, role sourceVer
|
||||
|
||||
interpreted := interpretVersion(rule, root)
|
||||
evidence.version = interpreted.version
|
||||
if sourceVersionControlOverlap(root, evidence.resolvedPath) {
|
||||
if resolved.present && sourceVersionControlOverlap(root, evidence.resolvedPath) {
|
||||
evidence.redactText()
|
||||
result.payload = nil
|
||||
result.diagnostics = []Diagnostic{sourceVersionOverlapDiagnostic(rule, evidence)}
|
||||
|
||||
Reference in New Issue
Block a user