Settle main-source eligibility for relative context binding

This commit is contained in:
2026-11-09 18:00:31 +00:00
parent daa0cf9e88
commit c0bc5e2b73

View File

@@ -1,8 +1,7 @@
# Main-relative companion imports and overlays # Main-relative companion imports and overlays
Status: accepted direction, public API structure, absent-main behavior, and Status: all four design decisions settled; not implemented. Final API spelling
containment/symlink convention; main-source eligibility remains open. and implementation planning remain. Baseline: `191015e`.
Not implemented. Baseline: `191015e`.
## Purpose ## Purpose
@@ -14,7 +13,8 @@ boilerplate to the main file or creating a manifest on disk.
The central addition is deferred filesystem-context binding for application-owned The central addition is deferred filesystem-context binding for application-owned
underlay inputs, including `Mapping` and `Bytes` sources containing native import underlay inputs, including `Mapping` and `Bytes` sources containing native import
declarations. Bind their imports to the selected main directory during loading. declarations. Bind their imports to the main input's resolved filesystem context
during loading: for file mains, lookup starts beside the selected main file.
Reuse existing optional-file, directory, recursion, pattern, destination, and Reuse existing optional-file, directory, recursion, pattern, destination, and
composition behavior. A new file constructor alone would not cover this workflow, composition behavior. A new file constructor alone would not cover this workflow,
and a second fragment-discovery implementation is unnecessary. and a second fragment-discovery implementation is unnecessary.
@@ -32,6 +32,11 @@ root override, `/srv/campaign/audita.yml` supplies the location
lookup uses the configuration root, which can differ from the main directory. lookup uses the configuration root, which can differ from the main directory.
Optional discovery exhaustion provides no inferred context. Optional discovery exhaustion provides no inferred context.
Non-file mains can already carry an explicit filesystem context. Absent inputs,
including optional discovery exhaustion, can also retain a supplied context;
for an absent host input, an explicit root supplies the base when `BaseDir` is
omitted. Reusing these validated contexts requires no filename inference.
The [layered API](../config.md#layered-loading) has the coordination gap: The [layered API](../config.md#layered-loading) has the coordination gap:
- An active overlay requires `LayeredOptions.OverlayFilesystem`. Preflight - An active overlay requires `LayeredOptions.OverlayFilesystem`. Preflight
@@ -94,10 +99,11 @@ selects main-derived filesystem binding for that input. `Mapping` and `Bytes`
are first-class supported sources for this use. OD-1 settles the public API are first-class supported sources for this use. OD-1 settles the public API
structure below; no new operator-facing YAML syntax is needed. structure below; no new operator-facing YAML syntax is needed.
Once the main is selected, ConfigKit supplies the underlay import base from Once the main context is resolved, ConfigKit inherits its base, namespace, and
that file's resolved directory and inherits its namespace and containment root containment root under OD-3 and OD-4. File mains supply their resolved directory;
under OD-3. Existing import selection then acquires the companion files. Each non-file or absent mains may supply an explicit context. Existing import selection
imported file uses its own directory for further relative imports. An import of then acquires the companion files. Each imported file uses its own directory for
further relative imports. An import of
`extra.yml` from `glossary/terms.yml` resolves to `glossary/extra.yml`. `extra.yml` from `glossary/terms.yml` resolves to `glossary/extra.yml`.
Applications choose underlay groups and their order. A group composes strictly; Applications choose underlay groups and their order. A group composes strictly;
@@ -118,14 +124,15 @@ native imports. Ordinary `File` paths do not silently gain new relative semantic
### Layered overlays ### Layered overlays
For an active layered overlay, use an explicit `OverlayFilesystem` when supplied. For an active layered overlay, use an explicit `OverlayFilesystem` when supplied.
Otherwise derive the overlay context automatically from the available main-file Otherwise derive the overlay context automatically from the main input's usable
information; the common workflow requires no `OverlayFromMain` flag. An invalid resolved context; the common workflow requires no `OverlayFromMain` flag. An
explicit context fails rather than falling back. Inactive overlays need no invalid explicit context fails rather than falling back. Inactive overlays need no
context. Missing-main and fallback cases are addressed below and in OD-2. context. Missing-main and fallback cases are addressed below and in OD-2.
For this newly derived layered context, the lookup location is the main file's For this newly derived layered context, lookup uses the main context's resolved
directory, independently of a wider containment root. If the main is base, independently of a wider containment root. For file mains this is the
`/srv/campaign/audita.yml` and its permitted root is `/srv`, the selected file main file's directory. If the main is `/srv/campaign/audita.yml` and its permitted
root is `/srv`, the selected file
is `/srv/campaign/overlays/production.yml`, not `/srv/overlays/production.yml`. is `/srv/campaign/overlays/production.yml`, not `/srv/overlays/production.yml`.
Widening containment must not relocate the automatically selected companions. Widening containment must not relocate the automatically selected companions.
@@ -138,7 +145,8 @@ precedence, and validation retain their existing semantics.
### Absent main and optional companions ### Absent main and optional companions
Optional discovery may find no main file while defaults and CLI settings still Optional discovery may find no main file while defaults and CLI settings still
permit a successful application load. When no usable base is available, skip permit a successful application load. An explicit context on that absent main
can still supply a usable base under OD-4. When no usable base is available, skip
optional companion import expansion while retaining the declaring input's optional companion import expansion while retaining the declaring input's
ordinary payload and validation. Required imports still fail. Applications can ordinary payload and validation. Required imports still fail. Applications can
require a base or supply an explicit fallback context on each derived binding. require a base or supply an explicit fallback context on each derived binding.
@@ -188,21 +196,22 @@ No release version or additional release gate is selected here.
## Work from the baseline ## Work from the baseline
1. **Finalize context declarations and absence policy.** Resolve the remaining 1. **Finalize API spelling and examples.** Translate the settled decisions below
choices below and prepare Go examples for Audita's mapping/bytes workflow, into concrete declarations and prepare Go examples for Audita's mapping/bytes
explicit context overrides, and independently located embedded inputs. workflow, explicit overrides, non-file main contexts, and independently located
embedded inputs.
2. **Carry binding intent through the public facade and load request.** Update 2. **Carry binding intent through the public facade and load request.** Update
the relevant input/options declarations and conversions in `layered.go` and the relevant input/options declarations and conversions in `layered.go` and
`lifecycle.go`, retaining root-owned public types and normal ownership rules. `lifecycle.go`, retaining root-owned public types and normal ownership rules.
Distinguish a deferred relationship from a concrete filesystem context. Distinguish a deferred relationship from a concrete filesystem context.
3. **Bind underlay import contexts after main selection.** Extend preparation 3. **Bind underlay import contexts after main context resolution.** Extend
and acquisition in `internal/engine/load_request.go`, `load_input.go`, and preparation and acquisition in `internal/engine/load_request.go`, `load_input.go`, and
`acquisition.go` as needed. Reuse the acquired main's directory and namespace, `acquisition.go` as needed. Reuse the main's resolved base, root, and namespace,
apply the chosen missing-base policy, and delegate selection to existing import apply the settled missing-base policy, and delegate selection to existing import
machinery. Preserve independently supplied contexts. machinery. Preserve independently supplied contexts.
4. **Derive overlay context and separate lookup from containment.** Remove the 4. **Derive overlay context and separate lookup from containment.** Remove the
unconditional explicit-context requirement for the new layered default. unconditional explicit-context requirement for the new layered default.
Preserve explicit and ordinary lookup while enabling a main-directory lookup Preserve explicit and ordinary lookup while using the main context's resolved
base inside a potentially wider root. Reuse selected-overlay loading and its base inside a potentially wider root. Reuse selected-overlay loading and its
failure and resource-ownership behavior. failure and resource-ownership behavior.
5. **Verify the complete workflow and document it.** Add maintained consumer 5. **Verify the complete workflow and document it.** Add maintained consumer
@@ -219,6 +228,9 @@ Validate behavior through the public loading boundary, including:
- Explicit and discovered main files, early and later search candidates, and an - Explicit and discovered main files, early and later search candidates, and an
unrelated invocation directory. Companions follow the same selected main. unrelated invocation directory. Companions follow the same selected main.
- Native file mains, bytes/readers/mappings with explicit host or native contexts,
and absent mains with explicit contexts. Cover absent host root-only contexts,
missing and invalid contexts, and context reuse without enabling main imports.
- Mapping and bytes underlays containing optional named-file and recursive - Mapping and bytes underlays containing optional named-file and recursive
directory imports with `*.yml`/`*.yaml` filtering and mounted destinations. directory imports with `*.yml`/`*.yaml` filtering and mounted destinations.
- File only, directory only, both, neither, empty directories, and invalid or - File only, directory only, both, neither, empty directories, and invalid or
@@ -308,6 +320,9 @@ context in this order:
A fallback can satisfy require-base: the option requires a usable context, not A fallback can satisfy require-base: the option requires a usable context, not
an actual main file. Consumers requiring a main file can make discovery required. an actual main file. Consumers requiring a main file can make discovery required.
Under OD-4, an explicit context on a non-file or absent main can also satisfy it;
the absence policy applies when no usable base exists, not merely when no main
content exists.
An invalid selected context fails; it does not activate a lower-priority choice. An invalid selected context fails; it does not activate a lower-priority choice.
Fallback applies only when the main cannot provide a base, never after a failure Fallback applies only when the main cannot provide a base, never after a failure
reading or processing a selected main, or after missing or invalid companion reading or processing a selected main, or after missing or invalid companion
@@ -326,10 +341,11 @@ Companion absence settings do not make a selected overlay optional.
### OD-3: Containment inheritance and symlink convention — settled ### OD-3: Containment inheritance and symlink convention — settled
Derived companion imports and derived layered overlays inherit the selected Derived companion imports and derived layered overlays inherit the main input's
main's resolved filesystem namespace and containment root. Lookup starts at the resolved filesystem namespace and containment root. Lookup uses its resolved
main file's resolved directory, even when its configured root is wider. Keep base under OD-4, which for file mains is the file's resolved directory, even when
existing explicit overlay context and ordinary `Load` lookup behavior. its configured root is wider. Keep existing explicit overlay context and ordinary
`Load` lookup behavior.
For a host main at `/srv/campaign/audita.yml` with root `/srv`, companion For a host main at `/srv/campaign/audita.yml` with root `/srv`, companion
`glossary.yml` resolves to `/srv/campaign/glossary.yml`, while an import of `glossary.yml` resolves to `/srv/campaign/glossary.yml`, while an import of
@@ -352,27 +368,41 @@ under the normal rules when they choose another location or scope.
Each imported file continues to resolve further imports from its own directory, Each imported file continues to resolve further imports from its own directory,
within the inherited root. Existing import `follow` controls still govern within the inherited root. Existing import `follow` controls still govern
discovered symlink entries; they do not select the base of a symlinked main. discovered symlink entries; they do not select the base of a symlinked main.
Preserve existing containment checks and traversal controls. For any eligible Preserve existing containment checks and traversal controls. For native main
native main under OD-4, retain that filesystem's namespace and link-handling contexts under OD-4, retain that filesystem's namespace and link-handling
capabilities without imposing host-filesystem guarantees on every `fs.FS`. capabilities without imposing host-filesystem guarantees on every `fs.FS`.
## Open Decisions ### OD-4: Main-source eligibility beyond discovered host files — settled
### OD-4: Main-source eligibility beyond discovered host files Derive from any usable, validated main filesystem context, regardless of the
main source's format. Document `MainRelative` as using the main input's resolved
filesystem context. For the common file workflow, this means lookup beside the
selected main file. Reuse existing context resolution rather than adding another
acquisition mechanism or requiring main imports to be enabled.
**Settled:** both explicitly selected and discovered host mains support the - Explicit host files and successful discovery supply the canonical file directory
workflow. Non-file companion inputs, especially mappings and bytes, are in scope; and resolved containment root.
this decision concerns the main input that supplies their base. - `Filesystem.File` mains supply their directory and root within their existing
namespace. Dependent loading stays in that namespace.
- `Bytes`, `Reader`, and `Mapping` mains can supply an explicit context with a
usable base, root, and namespace under their existing validation rules.
- `NoSource` and optional discovery exhaustion can retain an explicit main
context. Reuse its resolved base: existing absent-host rules allow an explicit
root to serve as the base when `BaseDir` is omitted; native contexts retain
their existing requirements.
**Recommendation:** also support native `Filesystem.File` mains using their For example, a bytes main with host `BaseDir: /srv/campaign` and `Root: /srv`
namespace's path and ownership rules. For an initial file-based scope, non-file can supply the same companion lookup and containment as a file main in that
mains use independently supplied or fallback contexts rather than implicitly directory with the same root. An absent main with a usable explicit context
providing a main-file directory. This covers the requested workflow and avoids can likewise supply companion and overlay lookup without creating main content.
making discovery a prerequisite for equivalent file inputs.
**Alternative:** derive from any usable main context, including `BaseDir` on a Without a usable main context, apply OD-2's fallback, optional-import skipping,
byte, reader, or mapping main. This is more uniform with context-based loading or error behavior. Invalid supplied contexts fail rather than selecting a
and avoids excluding a known base, but requires explaining main-relative as fallback. Never infer filesystem locations from diagnostic labels or unsuccessful
main-context-relative and deciding how root-only or absent-main contexts behave. search candidates, manufacture a main source, or change source evidence to make
A host-only initial scope is smaller but leaves native-file consumers with the a non-file input appear file-backed.
same coordination gap.
This preserves one context-reuse rule when applications change how they supply
main content. OD-1's concrete overrides and OD-3's containment, namespace, and
link-handling rules continue to apply. Existing ordinary `Load` and explicit
overlay context lookup semantics remain unchanged.