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
|
each fragment conforms to a complete application schema. Final assembled
|
||||||
decoding and application validation still run once.
|
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
|
Accepted non-main declarations are consumed from their owned payload before
|
||||||
composition; their authored ancestors remain, even if empty. The main
|
composition; their authored ancestors remain, even if empty. The main
|
||||||
declaration remains in the application. Ordinary release metadata should use a
|
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
|
`GOTOOLCHAIN=local GOPROXY=off go test -race ./...` passed on
|
||||||
`go1.26.9 linux/amd64`. Existing historical/dependency propagation and neighboring
|
`go1.26.9 linux/amd64`. Existing historical/dependency propagation and neighboring
|
||||||
public-value coverage remain green; no new sensitivity policy was introduced.
|
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
|
before placement/composition, and the destination-prefixed main rule reaches
|
||||||
payload protection and typed conversion. Warnings, raw accounting, source
|
payload protection and typed conversion. Warnings, raw accounting, source
|
||||||
contexts, and final loading reuse the existing pipeline. Four must-address
|
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)
|
### 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)
|
### 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
|
Affected: `processSourceVersion` in
|
||||||
[source_version.go](../../internal/engine/source_version.go), lines 197–219 at
|
[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
|
No production fixes, repository regression tests, dependency updates, or release
|
||||||
changes were made. Only this findings document was revised.
|
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) {
|
func (reader *sourceVersionModeReader) Read(data []byte) (int, error) {
|
||||||
reader.reads++
|
reader.reads++
|
||||||
return reader.Reader.Read(data)
|
return reader.Reader.Read(data)
|
||||||
|
|||||||
@@ -196,7 +196,7 @@ func processSourceVersion(root *document.Node, rule *VersionRule, role sourceVer
|
|||||||
|
|
||||||
interpreted := interpretVersion(rule, root)
|
interpreted := interpretVersion(rule, root)
|
||||||
evidence.version = interpreted.version
|
evidence.version = interpreted.version
|
||||||
if sourceVersionControlOverlap(root, evidence.resolvedPath) {
|
if resolved.present && sourceVersionControlOverlap(root, evidence.resolvedPath) {
|
||||||
evidence.redactText()
|
evidence.redactText()
|
||||||
result.payload = nil
|
result.payload = nil
|
||||||
result.diagnostics = []Diagnostic{sourceVersionOverlapDiagnostic(rule, evidence)}
|
result.diagnostics = []Diagnostic{sourceVersionOverlapDiagnostic(rule, evidence)}
|
||||||
|
|||||||
Reference in New Issue
Block a user