Resolve Windows rooted search paths with Go

This commit is contained in:
2026-10-24 19:24:30 +00:00
parent 2fedb0307f
commit 0369111d30
3 changed files with 84 additions and 5 deletions

View File

@@ -1,6 +1,6 @@
# Configuration Discovery Review Findings # Configuration Discovery Review Findings
Status: findings 1–4 complete; finding 5 remains open. Status: complete. All five findings have been addressed in separate commits.
Reviewed commit: `e50fee2`, including the discovery implementation commits after Reviewed commit: `e50fee2`, including the discovery implementation commits after
`424c897`. The review compared the implementation with the `424c897`. The review compared the implementation with the
@@ -185,7 +185,15 @@ code, not a new Windows support promise or native-platform release gate.
## 5. Preserve Drive-Root-Relative Windows Path Meaning ## 5. Preserve Drive-Root-Relative Windows Path Meaning
Priority: P3. Status: open. Algorithmic audit: correctness finding. Priority: P3. Status: complete. Algorithmic audit: correctness finding.
**Completed:** drive-root-relative Windows paths now use `filepath.Abs` alongside
drive-relative paths, preserving native host interpretation. Windows regression
tests compare candidates and loaded files with explicit `File` for relative,
drive-root-relative (both separators), drive-relative, and absolute paths, through
both `Paths` and `SearchDirectory`. Existing Unix path/short-circuit tests and
`./scripts/check.sh` passed. The Windows engine test binary cross-compiled; no
native Windows execution or new release gate was introduced.
**Location:** [discovery.go](../../internal/engine/discovery.go), **Location:** [discovery.go](../../internal/engine/discovery.go),
`absoluteCandidate`, lines 380–391; also used for literal directory searches. `absoluteCandidate`, lines 380–391; also used for literal directory searches.
@@ -257,8 +265,8 @@ ordinary file acquisition. It did not benchmark or reopen those choices.
No unplanned quadratic algorithm or other actionable asymptotic regression was No unplanned quadratic algorithm or other actionable asymptotic regression was
identified. Findings 4 and 5 record the additional ordinary correctness issues identified. Findings 4 and 5 record the additional ordinary correctness issues
found while examining those algorithms; findings 1–3 were carried forward from found while examining those algorithms; findings 1–3 were carried forward from
the earlier review. Existing configuration-tree algorithms outside the discovery changes the earlier review. Existing configuration-tree algorithms outside the discovery
were not re-audited as new feature work. changes were not re-audited as new feature work.
## Review Validation ## Review Validation
@@ -278,3 +286,13 @@ were not re-audited as new feature work.
helper regression overlay described in finding 4. It collected no timing or helper regression overlay described in finding 4. It collected no timing or
allocation measurements. Findings 4 and 5 are platform-specific correctness allocation measurements. Findings 4 and 5 are platform-specific correctness
findings, not claims of a native Windows validation run. findings, not claims of a native Windows validation run.
## Completed Remediation Validation
All five fixes include focused regression coverage and per-issue completion
markers above. The final `./scripts/check.sh` and `go test -race ./...` passed
with Go 1.26.10 on Linux. The Windows engine tests also cross-compiled with
`GOOS=windows GOARCH=amd64 CGO_ENABLED=0 go test -c`, with the binary kept outside
the repository. No native Windows or macOS run was performed or made a gate.
The implementation preserves the public API, candidate ordering, optional
absence, and existing composition/version/ownership contracts.

View File

@@ -370,7 +370,9 @@ func (iterator *searchCandidateIterator) absoluteCandidate(path string) (string,
if filepath.IsAbs(path) { if filepath.IsAbs(path) {
return filepath.Clean(path), nil return filepath.Clean(path), nil
} }
if filepath.VolumeName(path) != "" { // Drive-relative and drive-root-relative Windows paths need native
// resolution; joining a rooted path to the whole working directory changes it.
if filepath.VolumeName(path) != "" || runtime.GOOS == "windows" && len(path) > 0 && os.IsPathSeparator(path[0]) {
return filepath.Abs(path) return filepath.Abs(path)
} }
working, err := iterator.helpers.workingDir() working, err := iterator.helpers.workingDir()

View File

@@ -4,6 +4,8 @@ package engine
import ( import (
"errors" "errors"
"path/filepath"
"strings"
"testing" "testing"
) )
@@ -30,3 +32,60 @@ func TestWindowsSearchRejectsReservedComponentsBeforeAcquisition(t *testing.T) {
} }
} }
} }
func TestWindowsSearchPathsMatchHostAbsoluteResolution(t *testing.T) {
root := t.TempDir()
file := writeImportFile(t, root, "settings/config.yml", "value: selected\n")
working := t.TempDir()
t.Chdir(working)
relative, err := filepath.Rel(working, file)
if err != nil {
t.Fatal(err)
}
volume := filepath.VolumeName(file)
rooted := strings.TrimPrefix(file, volume)
for _, entry := range []struct {
name string
path string
}{
{"relative", relative},
{"drive root relative", rooted},
{"slash root relative", filepath.ToSlash(rooted)},
{"drive relative", volume + relative},
{"absolute", file},
} {
t.Run(entry.name, func(t *testing.T) {
want, err := filepath.Abs(entry.path)
if err != nil {
t.Fatal(err)
}
explicit, err := testLoad[discoveryLoadConfig](File(entry.path))
if err != nil {
t.Fatal(err)
}
for _, search := range []Search{
Paths(entry.path),
SearchDirectory(filepath.Dir(entry.path), DirectorySearchOptions{
Filenames: []FilenameGroup{CustomFilenames(filepath.Base(entry.path))},
}),
} {
iterator, diagnostics := newSearchCandidateIterator(search, "", discoveryDirectoryHelpers{})
if len(diagnostics) != 0 {
t.Fatal(diagnostics)
}
candidate, ok, err := iterator.next()
if err != nil || !ok || candidate != want {
t.Fatalf("candidate = %q, %v, %v; want %q", candidate, ok, err, want)
}
result, err := testLoad[discoveryLoadConfig](SearchConfig("", search))
if err != nil || result.Config.Value != explicit.Config.Value {
t.Fatalf("searched path = %#v, %v; explicit value %q", result, err, explicit.Config.Value)
}
sources := result.Sources()
if len(sources) != 1 || sources[0].Path != explicit.Sources()[0].Path {
t.Fatalf("searched source = %#v; explicit source %#v", sources, explicit.Sources())
}
}
})
}
}