diff --git a/docs/internal/imports.md b/docs/internal/imports.md index 5f105f8..e4fe464 100644 --- a/docs/internal/imports.md +++ b/docs/internal/imports.md @@ -8,8 +8,9 @@ This note records implementation flow and private identities for maintainers. Each source's root directive is parsed into presence-aware declarations and resolved against load-wide defaults. `acquisition.go` owns shared acquisition -policy, load accounting, and per-tier accounting; `import_acquisition.go` owns -reading candidate bytes, declaration traversal, and import admission. Candidate +policy, load-wide accounting, and tier-scoped repetition/namespace state; +`import_acquisition.go` owns reading candidate bytes, declaration traversal, +and import admission. Candidate selection in `import_selection.go` preserves a normalized namespace path separately from canonical access identity: overlapping roots coalesce the same candidate spelling in that namespace, while aliases reached diff --git a/docs/internal/overview.md b/docs/internal/overview.md index 70395b6..f19c644 100644 --- a/docs/internal/overview.md +++ b/docs/internal/overview.md @@ -98,12 +98,14 @@ version, default-import, and override policy stays distinct from each input's source, filesystem, destination, and import settings. [`acquisition.go`](../../internal/engine/acquisition.go) defines acquisition -policy and load-wide and per-tier accounting state. Each input is acquired under -that policy; grouped requests retain acquired underlay scopes until their tier is -composed in [`load_request.go`](../../internal/engine/load_request.go). Live +policy, load-wide accounting, and tier-scoped repetition/namespace state. Each +input is acquired under that policy; grouped requests retain acquired underlay +scopes until their tier is composed in +[`load_request.go`](../../internal/engine/load_request.go). Live filesystem access handles are confined to acquisition and closed there. Resolved filesystem facts used to authorize access are separate from passively retained -resource contexts: see +resource contexts. Non-import validation retains those facts on `assembledInput` +for passive-context construction and ordinary-overlay handling: see [`filesystem_context.go`](../../internal/engine/filesystem_context.go) and [`source_context.go`](../../internal/engine/source_context.go). diff --git a/docs/roadmap/remediation.md b/docs/roadmap/remediation.md index 3b56b7d..864c999 100644 --- a/docs/roadmap/remediation.md +++ b/docs/roadmap/remediation.md @@ -12,7 +12,7 @@ and full race suite (`go test -race ./...`) passed with Go 1.26.10 on Linux. ## 1. Complete The Non-Import Resolved-Context Handoff -Status: open. Priority: medium. Category: implementation gap and maintainability. +Status: complete (2026-10-25). Priority: medium. Category: implementation gap and maintainability. No user-facing correctness regression has been established for this finding. **Target.** Roadmap internal target 2 and implementation Stages 7–9 select one @@ -76,6 +76,17 @@ only for a consequential gap exposed by the change; do not assert incidental canonicalization/stat call counts. Run the development check and applicable ownership race checks after remediation. +**Completion.** Non-import validation now returns its resolved filesystem +description and acquisition retains it for passive contexts and ordinary +overlays, including absent inputs. Absent-host derivation uses the common +resolver; the duplicate validator was removed. Validation timing, directory and +root-open/close checks, and the existing absent-input containment diagnostic +are retained. Internal documentation now distinguishes load-wide accounting +from tier-scoped repetition/namespace state. Existing filesystem, discovery, +overlay, and resource tests, the development check, and `go test -race ./...` +passed with Go 1.26.10 on Linux. No new incidental call-count assertions were +added. + ## Algorithmic Audit Scope Audited worktree: `d898cb1`, with production code at the reviewed implementation @@ -88,7 +99,8 @@ schema construction. Findings 2–3 are retained inefficiencies in paths carried through the refactor, not algorithmic regressions introduced by the consolidation. They warrant focused follow-up without changing the selected configuration model. The audit -did not identify an additional correctness defect. Finding 1 remains open. +did not identify an additional correctness defect. Finding 1 was open at the +time of that audit; completion is recorded with the finding above. The selected stable Kahn scheduler, deterministic sorting, composition rules, scope boundaries, and required ownership copies are not reconsidered here. diff --git a/internal/engine/acquisition.go b/internal/engine/acquisition.go index db44890..ef738f5 100644 --- a/internal/engine/acquisition.go +++ b/internal/engine/acquisition.go @@ -178,9 +178,11 @@ func acquireInput(request inputAcquisitionRequest, policy acquisitionPolicy, sta input.document = versionProcessing.payload } if request.input.filesystem != nil && (selectedSource.kind == sourceNone || !activeOverlay(request.ordinaryOverlay)) { - if diagnostic := validateFilesystemContext(selectedSource, request.input.filesystem, parsed); diagnostic != nil { + resolved, diagnostic := validateFilesystemContext(selectedSource, request.input.filesystem, parsed, tier.hostToken) + if diagnostic != nil { return input, []Diagnostic{*diagnostic} } + input.resolvedFilesystem = &resolved } } if !request.input.imports && input.document != nil { diff --git a/internal/engine/filesystem.go b/internal/engine/filesystem.go index 6692173..1fe0aa6 100644 --- a/internal/engine/filesystem.go +++ b/internal/engine/filesystem.go @@ -7,73 +7,33 @@ import ( "strings" ) -func validateFilesystemContext(source Source, context *FilesystemContext, main parsedSource) *Diagnostic { - if source.kind == sourceNone && context.FS == nil { - return validateAbsentHostFilesystemContext(context) - } - if nativeContext(source, context) { - resolved, diagnostic := resolveNativeFilesystemContext(source, context) - if diagnostic != nil { - return diagnostic - } - return validateResolvedFilesystemDirectories(resolved) - } - resolved, diagnostic := resolveHostFilesystemContext(source, context, main, nil) +func validateFilesystemContext(source Source, context *FilesystemContext, main parsedSource, hostToken *namespaceToken) (resolvedFilesystemContext, *Diagnostic) { + resolved, diagnostic := resolveFilesystemContext(source, context, main, hostToken) if diagnostic != nil { - return diagnostic + // Keep absent-input validation's existing diagnostic while sharing + // root/base derivation with overlay and resource-context handling. + if source.kind == sourceNone && context.FS == nil && diagnostic.Code == CodeImportOutsideRoot { + diagnostic.Message = "import base must stay inside the configuration root" + } + return resolvedFilesystemContext{}, diagnostic } if diagnostic := validateResolvedFilesystemDirectories(resolved); diagnostic != nil { - return diagnostic + return resolvedFilesystemContext{}, diagnostic + } + if resolved.native != nil { + return resolved, nil } root, err := os.OpenRoot(resolved.rootPath) if err != nil { value := globFilesystemDiagnostic(err, "configuration root could not be opened", Location{}) value.Primary = nil - return &value + return resolvedFilesystemContext{}, &value } if err := root.Close(); err != nil { value := importDiagnostic(CodeImportRead, "configuration root could not be closed", nil) - return &value + return resolvedFilesystemContext{}, &value } - return nil -} - -func validateAbsentHostFilesystemContext(context *FilesystemContext) *Diagnostic { - rootPath, err := canonicalExistingPath(context.Root) - if err != nil { - value := importDiagnostic(CodeImportInvalid, "configuration root could not be canonicalized", nil) - return &value - } - basePath := rootPath - if context.BaseDir != "" { - basePath, err = canonicalExistingPath(context.BaseDir) - if err != nil { - value := importDiagnostic(CodeImportInvalid, "import base directory could not be canonicalized", nil) - return &value - } - } - if !containedPath(rootPath, basePath) { - value := importDiagnostic(CodeImportOutsideRoot, "import base must stay inside the configuration root", nil) - return &value - } - for _, candidate := range []string{rootPath, basePath} { - info, err := os.Stat(candidate) - if err != nil || !info.IsDir() { - value := importDiagnostic(CodeImportInvalid, "configuration root and import base must be directories", nil) - return &value - } - } - root, err := os.OpenRoot(rootPath) - if err != nil { - value := globFilesystemDiagnostic(err, "configuration root could not be opened", Location{}) - value.Primary = nil - return &value - } - if err := root.Close(); err != nil { - value := importDiagnostic(CodeImportRead, "configuration root could not be closed", nil) - return &value - } - return nil + return resolved, nil } func filesystemOptionDiagnostics(source Source, context *FilesystemContext) []Diagnostic { diff --git a/internal/engine/selected_overlay.go b/internal/engine/selected_overlay.go index caff47b..860fdf3 100644 --- a/internal/engine/selected_overlay.go +++ b/internal/engine/selected_overlay.go @@ -43,7 +43,6 @@ func resolveOrdinaryOverlayFilesystem(source Source, context *FilesystemContext, value := optionDiagnostic(CodeOptionsInvalid, "NoSource overlays require explicit filesystem context") return resolvedFilesystemContext{}, &value } - return resolveAbsentFilesystemContext(context) } if existing != nil { return *existing, nil