Separate filesystem context from access
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
# Consolidation Implementation Plan
|
# Consolidation Implementation Plan
|
||||||
|
|
||||||
Status: in progress. Stages 1 through 6 are complete; follow Stage 7 through Stage 20 in order.
|
Status: in progress. Stages 1 through 7 are complete; follow Stage 8 through Stage 20 in order.
|
||||||
The target executor is a Luna coding agent working through one end-to-end sprint.
|
The target executor is a Luna coding agent working through one end-to-end sprint.
|
||||||
|
|
||||||
## Authority, Baseline, And Execution
|
## Authority, Baseline, And Execution
|
||||||
@@ -347,7 +347,7 @@ direct tests to construct named requests. A search-source regression was caught
|
|||||||
and fixed during verification. The focused engine selection, facade selection,
|
and fixed during verification. The focused engine selection, facade selection,
|
||||||
consumer example tests, and `GOTOOLCHAIN=local GOPROXY=off go test ./...` passed.
|
consumer example tests, and `GOTOOLCHAIN=local GOPROXY=off go test ./...` passed.
|
||||||
|
|
||||||
## Stage 7 — Separate Resolved Filesystem Facts From Access
|
## Stage 7 — Separate Resolved Filesystem Facts From Access (Complete)
|
||||||
|
|
||||||
**Owners:** new engine `filesystem_context.go`, `filesystem.go`,
|
**Owners:** new engine `filesystem_context.go`, `filesystem.go`,
|
||||||
`filesystem_access.go`.
|
`filesystem_access.go`.
|
||||||
@@ -374,6 +374,13 @@ consumer example tests, and `GOTOOLCHAIN=local GOPROXY=off go test ./...` passed
|
|||||||
**Finish:** common context facts have one private representation while validation
|
**Finish:** common context facts have one private representation while validation
|
||||||
timing and live resource ownership remain explicit.
|
timing and live resource ownership remain explicit.
|
||||||
|
|
||||||
|
Progress notes (2026-10-25): added shared host/native filesystem descriptions,
|
||||||
|
separated directory validation and access creation, and migrated import
|
||||||
|
acquisition to pass resolved facts alongside its owned access. The prior
|
||||||
|
derivation adapters remain for the validator and overlay migration. Focused
|
||||||
|
acquisition tests, `GOTOOLCHAIN=local GOPROXY=off go test ./...`, and
|
||||||
|
`git diff --check` passed.
|
||||||
|
|
||||||
## Stage 8 — Carry Context Facts Through Input And Passive Views
|
## Stage 8 — Carry Context Facts Through Input And Passive Views
|
||||||
|
|
||||||
**Owners:** engine `load_input.go`, `source_context.go`, `load_request.go`, and
|
**Owners:** engine `load_input.go`, `source_context.go`, `load_request.go`, and
|
||||||
|
|||||||
@@ -33,14 +33,6 @@ type importAcquisitionRequest struct {
|
|||||||
processing *sourceVersionProcessingResult
|
processing *sourceVersionProcessingResult
|
||||||
}
|
}
|
||||||
|
|
||||||
type resolvedFilesystemContext struct {
|
|
||||||
native *nativeNamespace
|
|
||||||
hostToken *namespaceToken
|
|
||||||
rootPath string
|
|
||||||
basePath string
|
|
||||||
mainPath string
|
|
||||||
}
|
|
||||||
|
|
||||||
func acquireInput(request inputAcquisitionRequest, policy acquisitionPolicy, state *acquisitionState, tier *tierAcquisitionState) (assembledInput, []Diagnostic) {
|
func acquireInput(request inputAcquisitionRequest, policy acquisitionPolicy, state *acquisitionState, tier *tierAcquisitionState) (assembledInput, []Diagnostic) {
|
||||||
source := request.input.source
|
source := request.input.source
|
||||||
destination := request.input.destination
|
destination := request.input.destination
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package engine
|
|||||||
import (
|
import (
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@@ -137,32 +136,17 @@ func nativeContext(source Source, context *FilesystemContext) bool {
|
|||||||
// nativeFilesystemAccess resolves namespace context without opening caller
|
// nativeFilesystemAccess resolves namespace context without opening caller
|
||||||
// resources. File inputs always derive their base from the selected file.
|
// resources. File inputs always derive their base from the selected file.
|
||||||
func nativeFilesystemAccess(source Source, context *FilesystemContext) (*filesystemAccess, string, string, *Diagnostic) {
|
func nativeFilesystemAccess(source Source, context *FilesystemContext) (*filesystemAccess, string, string, *Diagnostic) {
|
||||||
var namespace *nativeNamespace
|
resolved, diagnostic := resolveNativeFilesystemContext(source, context)
|
||||||
rootPath := "."
|
if diagnostic != nil {
|
||||||
basePath := "."
|
return nil, "", "", diagnostic
|
||||||
mainPath := ""
|
|
||||||
if source.kind == sourceFilesystem {
|
|
||||||
namespace = source.namespace
|
|
||||||
mainPath = source.path
|
|
||||||
basePath = path.Dir(source.path)
|
|
||||||
if context != nil && context.Root != "" {
|
|
||||||
rootPath = context.Root
|
|
||||||
}
|
|
||||||
} else if context != nil && context.FS != nil {
|
|
||||||
namespace = context.FS.namespace
|
|
||||||
rootPath, basePath = context.Root, context.BaseDir
|
|
||||||
}
|
}
|
||||||
if namespace == nil || nilFilesystem(namespace.filesystem) || !fs.ValidPath(rootPath) || !fs.ValidPath(basePath) {
|
access, err := openResolvedFilesystemAccess(resolved)
|
||||||
value := optionDiagnostic(CodeOptionsInvalid, "filesystem context is invalid")
|
if err != nil {
|
||||||
|
value := globFilesystemDiagnostic(err, "configuration root could not be opened", Location{})
|
||||||
|
value.Primary = nil
|
||||||
return nil, "", "", &value
|
return nil, "", "", &value
|
||||||
}
|
}
|
||||||
access := newNativeAccess(namespace.filesystem, rootPath, namespace.token)
|
return access, resolved.basePath, resolved.mainPath, nil
|
||||||
access.label = namespace.label
|
|
||||||
if !access.contains(basePath) || mainPath != "" && !access.contains(mainPath) {
|
|
||||||
value := importDiagnostic(CodeImportOutsideRoot, "selected source and import base must stay inside the configuration root", nil)
|
|
||||||
return nil, "", "", &value
|
|
||||||
}
|
|
||||||
return access, basePath, mainPath, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateNativeDirectories(access *filesystemAccess, basePath, mainPath string) *Diagnostic {
|
func validateNativeDirectories(access *filesystemAccess, basePath, mainPath string) *Diagnostic {
|
||||||
@@ -181,64 +165,14 @@ func validateNativeDirectories(access *filesystemAccess, basePath, mainPath stri
|
|||||||
}
|
}
|
||||||
|
|
||||||
func importFilesystem(source Source, context *FilesystemContext, main parsedSource) (rootPath, basePath, mainPath string, diagnostic *Diagnostic) {
|
func importFilesystem(source Source, context *FilesystemContext, main parsedSource) (rootPath, basePath, mainPath string, diagnostic *Diagnostic) {
|
||||||
if main.source == nil || main.document == nil {
|
resolved, diagnostic := resolveHostFilesystemContext(source, context, main, nil)
|
||||||
value := importDiagnostic(CodeImportInvalid, "imports require a selected configuration source", nil)
|
if diagnostic != nil {
|
||||||
return "", "", "", &value
|
return "", "", "", diagnostic
|
||||||
}
|
}
|
||||||
|
if diagnostic := validateResolvedFilesystemDirectories(resolved); diagnostic != nil {
|
||||||
if source.kind == sourceFile {
|
return "", "", "", diagnostic
|
||||||
if context != nil && context.BaseDir != "" {
|
|
||||||
value := optionDiagnostic(CodeOptionsInvalid, "file sources determine their own import base directory")
|
|
||||||
return "", "", "", &value
|
|
||||||
}
|
|
||||||
canonical, err := canonicalExistingPath(source.path)
|
|
||||||
if err != nil {
|
|
||||||
value := importDiagnostic(CodeImportInvalid, "selected source path could not be canonicalized", nil)
|
|
||||||
return "", "", "", &value
|
|
||||||
}
|
|
||||||
mainPath = canonical
|
|
||||||
basePath = filepath.Dir(canonical)
|
|
||||||
rootPath = basePath
|
|
||||||
if context != nil && context.Root != "" {
|
|
||||||
rootPath, err = canonicalExistingPath(context.Root)
|
|
||||||
if err != nil {
|
|
||||||
value := importDiagnostic(CodeImportInvalid, "configuration root could not be canonicalized", nil)
|
|
||||||
return "", "", "", &value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if context == nil || context.BaseDir == "" || context.Root == "" {
|
|
||||||
value := optionDiagnostic(CodeOptionsInvalid, "byte and reader imports require absolute base and root directories")
|
|
||||||
return "", "", "", &value
|
|
||||||
}
|
|
||||||
if !filepath.IsAbs(context.BaseDir) || !filepath.IsAbs(context.Root) {
|
|
||||||
value := optionDiagnostic(CodeOptionsInvalid, "byte and reader import base and root directories must be absolute")
|
|
||||||
return "", "", "", &value
|
|
||||||
}
|
|
||||||
var err error
|
|
||||||
basePath, err = canonicalExistingPath(context.BaseDir)
|
|
||||||
if err != nil {
|
|
||||||
value := importDiagnostic(CodeImportInvalid, "import base directory could not be canonicalized", nil)
|
|
||||||
return "", "", "", &value
|
|
||||||
}
|
|
||||||
rootPath, err = canonicalExistingPath(context.Root)
|
|
||||||
if err != nil {
|
|
||||||
value := importDiagnostic(CodeImportInvalid, "configuration root could not be canonicalized", nil)
|
|
||||||
return "", "", "", &value
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if !containedPath(rootPath, basePath) || (mainPath != "" && !containedPath(rootPath, mainPath)) {
|
return resolved.rootPath, resolved.basePath, resolved.mainPath, nil
|
||||||
value := importDiagnostic(CodeImportOutsideRoot, "selected source and 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return rootPath, basePath, mainPath, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func canonicalExistingPath(name string) (string, error) {
|
func canonicalExistingPath(name string) (string, error) {
|
||||||
|
|||||||
149
internal/engine/filesystem_context.go
Normal file
149
internal/engine/filesystem_context.go
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
package engine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/fs"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type resolvedFilesystemContext struct {
|
||||||
|
native *nativeNamespace
|
||||||
|
hostToken *namespaceToken
|
||||||
|
rootPath string
|
||||||
|
basePath string
|
||||||
|
mainPath string
|
||||||
|
}
|
||||||
|
|
||||||
|
func resolveFilesystemContext(source Source, context *FilesystemContext, main parsedSource, hostToken *namespaceToken) (resolvedFilesystemContext, *Diagnostic) {
|
||||||
|
if nativeContext(source, context) {
|
||||||
|
return resolveNativeFilesystemContext(source, context)
|
||||||
|
}
|
||||||
|
return resolveHostFilesystemContext(source, context, main, hostToken)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resolveNativeFilesystemContext(source Source, context *FilesystemContext) (resolvedFilesystemContext, *Diagnostic) {
|
||||||
|
var namespace *nativeNamespace
|
||||||
|
resolved := resolvedFilesystemContext{rootPath: ".", basePath: "."}
|
||||||
|
if source.kind == sourceFilesystem {
|
||||||
|
namespace = source.namespace
|
||||||
|
resolved.mainPath = source.path
|
||||||
|
resolved.basePath = path.Dir(source.path)
|
||||||
|
if context != nil && context.Root != "" {
|
||||||
|
resolved.rootPath = context.Root
|
||||||
|
}
|
||||||
|
} else if context != nil && context.FS != nil {
|
||||||
|
namespace = context.FS.namespace
|
||||||
|
resolved.rootPath, resolved.basePath = context.Root, context.BaseDir
|
||||||
|
}
|
||||||
|
if namespace == nil || nilFilesystem(namespace.filesystem) || !fs.ValidPath(resolved.rootPath) || !fs.ValidPath(resolved.basePath) {
|
||||||
|
value := optionDiagnostic(CodeOptionsInvalid, "filesystem context is invalid")
|
||||||
|
return resolvedFilesystemContext{}, &value
|
||||||
|
}
|
||||||
|
resolved.native = namespace
|
||||||
|
if !containsNativePath(resolved.rootPath, resolved.basePath) || resolved.mainPath != "" && !containsNativePath(resolved.rootPath, resolved.mainPath) {
|
||||||
|
value := importDiagnostic(CodeImportOutsideRoot, "selected source and import base must stay inside the configuration root", nil)
|
||||||
|
return resolvedFilesystemContext{}, &value
|
||||||
|
}
|
||||||
|
return resolved, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resolveHostFilesystemContext(source Source, context *FilesystemContext, main parsedSource, hostToken *namespaceToken) (resolvedFilesystemContext, *Diagnostic) {
|
||||||
|
if main.source == nil || main.document == nil {
|
||||||
|
value := importDiagnostic(CodeImportInvalid, "imports require a selected configuration source", nil)
|
||||||
|
return resolvedFilesystemContext{}, &value
|
||||||
|
}
|
||||||
|
resolved := resolvedFilesystemContext{hostToken: hostToken}
|
||||||
|
if source.kind == sourceFile {
|
||||||
|
if context != nil && context.BaseDir != "" {
|
||||||
|
value := optionDiagnostic(CodeOptionsInvalid, "file sources determine their own import base directory")
|
||||||
|
return resolvedFilesystemContext{}, &value
|
||||||
|
}
|
||||||
|
canonical := main.source.Path
|
||||||
|
if canonical == "" {
|
||||||
|
var err error
|
||||||
|
canonical, err = canonicalExistingPath(source.path)
|
||||||
|
if err != nil {
|
||||||
|
value := importDiagnostic(CodeImportInvalid, "selected source path could not be canonicalized", nil)
|
||||||
|
return resolvedFilesystemContext{}, &value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resolved.mainPath = canonical
|
||||||
|
resolved.basePath = filepath.Dir(canonical)
|
||||||
|
resolved.rootPath = resolved.basePath
|
||||||
|
if context != nil && context.Root != "" {
|
||||||
|
rootPath, err := canonicalExistingPath(context.Root)
|
||||||
|
if err != nil {
|
||||||
|
value := importDiagnostic(CodeImportInvalid, "configuration root could not be canonicalized", nil)
|
||||||
|
return resolvedFilesystemContext{}, &value
|
||||||
|
}
|
||||||
|
resolved.rootPath = rootPath
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if context == nil || context.BaseDir == "" || context.Root == "" {
|
||||||
|
value := optionDiagnostic(CodeOptionsInvalid, "byte and reader imports require absolute base and root directories")
|
||||||
|
return resolvedFilesystemContext{}, &value
|
||||||
|
}
|
||||||
|
if !filepath.IsAbs(context.BaseDir) || !filepath.IsAbs(context.Root) {
|
||||||
|
value := optionDiagnostic(CodeOptionsInvalid, "byte and reader import base and root directories must be absolute")
|
||||||
|
return resolvedFilesystemContext{}, &value
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
resolved.basePath, err = canonicalExistingPath(context.BaseDir)
|
||||||
|
if err != nil {
|
||||||
|
value := importDiagnostic(CodeImportInvalid, "import base directory could not be canonicalized", nil)
|
||||||
|
return resolvedFilesystemContext{}, &value
|
||||||
|
}
|
||||||
|
resolved.rootPath, err = canonicalExistingPath(context.Root)
|
||||||
|
if err != nil {
|
||||||
|
value := importDiagnostic(CodeImportInvalid, "configuration root could not be canonicalized", nil)
|
||||||
|
return resolvedFilesystemContext{}, &value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !containedPath(resolved.rootPath, resolved.basePath) || resolved.mainPath != "" && !containedPath(resolved.rootPath, resolved.mainPath) {
|
||||||
|
value := importDiagnostic(CodeImportOutsideRoot, "selected source and import base must stay inside the configuration root", nil)
|
||||||
|
return resolvedFilesystemContext{}, &value
|
||||||
|
}
|
||||||
|
return resolved, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func containsNativePath(root, name string) bool {
|
||||||
|
if !fs.ValidPath(name) || !fs.ValidPath(root) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return root == "." || name == root || strings.HasPrefix(name, root+"/")
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateResolvedFilesystemDirectories(resolved resolvedFilesystemContext) *Diagnostic {
|
||||||
|
for _, directory := range []string{resolved.rootPath, resolved.basePath} {
|
||||||
|
var info fs.FileInfo
|
||||||
|
var err error
|
||||||
|
if resolved.native != nil {
|
||||||
|
info, err = fs.Stat(resolved.native.filesystem, directory)
|
||||||
|
} else {
|
||||||
|
info, err = os.Stat(directory)
|
||||||
|
}
|
||||||
|
if err != nil || !info.IsDir() {
|
||||||
|
value := importDiagnostic(CodeImportInvalid, "configuration root and import base must be directories", nil)
|
||||||
|
return &value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func openResolvedFilesystemAccess(resolved resolvedFilesystemContext) (*filesystemAccess, error) {
|
||||||
|
if resolved.native != nil {
|
||||||
|
access := newNativeAccess(resolved.native.filesystem, resolved.rootPath, resolved.native.token)
|
||||||
|
access.label = resolved.native.label
|
||||||
|
return access, nil
|
||||||
|
}
|
||||||
|
access, err := openHostAccess(resolved.rootPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if resolved.hostToken != nil {
|
||||||
|
access.token = resolved.hostToken
|
||||||
|
}
|
||||||
|
return access, nil
|
||||||
|
}
|
||||||
@@ -76,36 +76,22 @@ func acquireImportedInput(request importAcquisitionRequest, policy *acquisitionP
|
|||||||
source := request.input.input.source
|
source := request.input.input.source
|
||||||
context := request.input.input.filesystem
|
context := request.input.input.filesystem
|
||||||
main := request.main
|
main := request.main
|
||||||
if nativeContext(source, context) {
|
resolved, diagnostic := resolveFilesystemContext(source, context, main, tier.hostToken)
|
||||||
access, basePath, mainPath, diagnostic := nativeFilesystemAccess(source, context)
|
|
||||||
if diagnostic != nil {
|
|
||||||
return nil, sourceInfos(main.source), []Diagnostic{*diagnostic}
|
|
||||||
}
|
|
||||||
native := source.namespace
|
|
||||||
if native == nil && context != nil && context.FS != nil {
|
|
||||||
native = context.FS.namespace
|
|
||||||
}
|
|
||||||
return acquireImportedWithAccess(request, policy, state, tier, access, resolvedFilesystemContext{
|
|
||||||
native: native, hostToken: tier.hostToken, rootPath: access.rootPath,
|
|
||||||
basePath: basePath, mainPath: mainPath,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
rootPath, basePath, mainPath, diagnostic := importFilesystem(source, context, main)
|
|
||||||
if diagnostic != nil {
|
if diagnostic != nil {
|
||||||
return nil, sourceInfos(main.source), []Diagnostic{*diagnostic}
|
return nil, sourceInfos(main.source), []Diagnostic{*diagnostic}
|
||||||
}
|
}
|
||||||
access, err := openHostAccess(rootPath)
|
if resolved.native == nil {
|
||||||
|
if diagnostic := validateResolvedFilesystemDirectories(resolved); diagnostic != nil {
|
||||||
|
return nil, sourceInfos(main.source), []Diagnostic{*diagnostic}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
access, err := openResolvedFilesystemAccess(resolved)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
diagnostic := globFilesystemDiagnostic(err, "configuration root could not be opened", Location{})
|
diagnostic := globFilesystemDiagnostic(err, "configuration root could not be opened", Location{})
|
||||||
diagnostic.Primary = nil
|
diagnostic.Primary = nil
|
||||||
return nil, sourceInfos(main.source), []Diagnostic{diagnostic}
|
return nil, sourceInfos(main.source), []Diagnostic{diagnostic}
|
||||||
}
|
}
|
||||||
if tier.hostToken != nil {
|
return acquireImportedWithAccess(request, policy, state, tier, access, resolved)
|
||||||
access.token = tier.hostToken
|
|
||||||
}
|
|
||||||
return acquireImportedWithAccess(request, policy, state, tier, access, resolvedFilesystemContext{
|
|
||||||
hostToken: tier.hostToken, rootPath: rootPath, basePath: basePath, mainPath: mainPath,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func acquireImportedWithAccess(request importAcquisitionRequest, policy *acquisitionPolicy, loadState *acquisitionState, tier *tierAcquisitionState, access *filesystemAccess, resolved resolvedFilesystemContext) (*composedDocument, []SourceInfo, []Diagnostic) {
|
func acquireImportedWithAccess(request importAcquisitionRequest, policy *acquisitionPolicy, loadState *acquisitionState, tier *tierAcquisitionState, access *filesystemAccess, resolved resolvedFilesystemContext) (*composedDocument, []SourceInfo, []Diagnostic) {
|
||||||
|
|||||||
Reference in New Issue
Block a user