Document main-relative companion loading
This commit is contained in:
47
docs/adr/0022-main-relative-context.md
Normal file
47
docs/adr/0022-main-relative-context.md
Normal file
@@ -0,0 +1,47 @@
|
||||
# ADR 0022: Bind Companion Contexts After Main Selection
|
||||
|
||||
## Status
|
||||
|
||||
Accepted.
|
||||
|
||||
## Date
|
||||
|
||||
2026-11-09
|
||||
|
||||
## Context
|
||||
|
||||
Applications choose a main-file search and may also declare companion imports
|
||||
and a selected overlay. The main directory is only known after discovery, so
|
||||
supplying every dependent context before loading duplicates selection or requires
|
||||
a preliminary load. ADR 0019 keeps main discovery explicit and inside loading.
|
||||
|
||||
## Decision
|
||||
|
||||
Extend layered loading to bind application-declared non-file underlays to the
|
||||
resolved main context after acquisition. An explicit underlay context overrides
|
||||
the binding, and an explicit fallback or require-base option controls absence.
|
||||
Selected layered overlays choose an explicit context, the resolved main context,
|
||||
or a fallback in that order. Derived and fallback lookup use the context base;
|
||||
the inherited root continues to bound containment. Ordinary and explicit
|
||||
overlay lookup retain their established root-relative behavior.
|
||||
|
||||
Applications remain responsible for choosing searches, import declarations,
|
||||
and overlay names. Selection failures are terminal; no second search or
|
||||
application context callback is introduced.
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
- Require applications to discover the main file separately. This repeats
|
||||
discovery and may run a complete load more than once.
|
||||
- Implicitly scan conventional companion directories. This would impose
|
||||
application-specific conventions without an explicit declaration.
|
||||
- Add only a relative file constructor. This would not cover mapping or bytes
|
||||
sources with existing import, placement, and composition rules.
|
||||
|
||||
## Consequences
|
||||
|
||||
The load owns the resolved binding and keeps namespace, containment, source
|
||||
identity, and handle lifetime in the existing acquisition lifecycle. A missing
|
||||
base skips optional imports while preserving the declaring payload; required
|
||||
imports and require-base retain explicit failures. The configuration contract
|
||||
and public declarations describe the observable rules.
|
||||
@@ -75,6 +75,8 @@ or the supported directory presets, then pass it to `SearchConfig`. Search
|
||||
descriptions are immutable and reusable; each load resolves dynamic directories
|
||||
and checks current candidate files again. `Searches` is ordered concatenation.
|
||||
It preserves the supplied description order and does not deduplicate candidates.
|
||||
Layered companion inputs can use the selected main's context without a second
|
||||
search, as described under [layered loading](#layered-loading).
|
||||
|
||||
`NamedFilenames` selects `<application>.yml`, then `<application>.yaml`.
|
||||
`GenericFilenames` selects `config.yml`, `config.yaml`, `main.yml`, then
|
||||
@@ -316,13 +318,33 @@ typed configuration before application resolution. Syntax, strict-composition,
|
||||
inheritance, and decoding errors cannot be masked by a CLI override. Resolution,
|
||||
secret lookup, validation, and publication run once on the resulting values.
|
||||
|
||||
An active layered overlay requires `LayeredOptions.OverlayFilesystem` explicitly,
|
||||
independent of the main source's namespace. Its name and root-relative lookup
|
||||
follow the selected-overlay rules below; inactive overlays ignore the context.
|
||||
An active layered overlay selects `OverlayFilesystem` first, then the resolved
|
||||
main context, then `OverlayFallback`. An invalid selected context fails without
|
||||
trying another. An inactive overlay ignores both supplied contexts.
|
||||
The same path in a different filesystem namespace is not a repeated file.
|
||||
Ordinary `Load` infers overlay context from sources that provide one; with
|
||||
`NoSource`, it requires the explicit `Options.Filesystem` context.
|
||||
|
||||
An underlay `Input.MainRelative` requests the resolved main context for a
|
||||
non-file source. An explicit `Input.Filesystem` overrides it entirely. File,
|
||||
native file, and search underlays cannot use an active binding, nor can the
|
||||
main input bind to itself. `MainRelative()` creates a fresh default description.
|
||||
The binding does not enable imports. If the main has no usable context, its
|
||||
`Fallback` is selected if supplied; otherwise optional imports are skipped and
|
||||
the input's own payload remains. Required named targets, directory and glob
|
||||
selections fail without a base, while an empty `path: []` remains a no-op.
|
||||
`RequireBase` requires a usable main or fallback context even without imports.
|
||||
Invalid declarations still fail, and skipped imports create no search evidence.
|
||||
Selected main failures never trigger fallback or a second discovery attempt.
|
||||
|
||||
Derived contexts preserve the main namespace and containment root, while lookup
|
||||
starts at its resolved base. A host file's base is its canonical target directory,
|
||||
including when the selected filename is a symlink. A wider root permits contained
|
||||
parent-relative imports without moving the lookup base. Explicit non-file and
|
||||
absent-main contexts can provide a base; an absent host input may use its root as
|
||||
base. An unused fallback is not opened or checked for existence. See the
|
||||
[consumer example](../examples/consumers/main_relative.go).
|
||||
|
||||
Source IDs follow acquisition order—main and its depth-first imports, then each
|
||||
underlay tier's inputs and depth-first imports, then overlay and CLI—even though
|
||||
history follows precedence order. Default main-version checking happens after
|
||||
@@ -567,7 +589,7 @@ one context for a composite origin or an application-derived value.
|
||||
`ResourceContext.FS` is rooted at the source's configured root, and `BaseDir`
|
||||
is its slash-relative directory inside that root. This includes imported
|
||||
sources, selected overlays, host files even without enabled imports, native
|
||||
files, and byte, reader, or mapping inputs with an explicit context. CLI and
|
||||
files, and byte, reader, or mapping inputs with an explicit or main-derived context. CLI and
|
||||
contextless synthetic inputs have no resource context. To open a relative
|
||||
resource, join `BaseDir` with the application resource name using slash-path
|
||||
operations and open that path through `FS`.
|
||||
@@ -1489,12 +1511,15 @@ claim records rather than mutating or aliasing an input document.
|
||||
`Options.Overlay` selects at most one overlay by name. Names must match
|
||||
`[a-zA-Z0-9_-]+`; they are not paths. The empty name and exact lowercase name
|
||||
`default` select no overlay, and `overlays/default.yml` is never loaded implicitly.
|
||||
Every other name resolves only to `<configuration-root>/overlays/<name>.yml`, with
|
||||
For ordinary loads and explicit layered overlay contexts, every other name resolves
|
||||
to `<configuration-root>/overlays/<name>.yml`, with
|
||||
no extension fallback, case normalization, inheritance between overlay files,
|
||||
or overlay stack. A selected missing overlay is an error.
|
||||
For a layered main-derived or fallback context, lookup instead starts at the
|
||||
resolved base directory; the configured root still bounds containment.
|
||||
|
||||
With `NoSource`, the main input stays absent and the explicit filesystem context
|
||||
supplies the overlay root. The overlay is recorded as the first acquired source
|
||||
With ordinary `Load` and `NoSource`, the main input stays absent and the explicit
|
||||
filesystem context supplies the overlay root. The overlay is recorded as the first acquired source
|
||||
when no other source exists. Host contexts may use the root-only form described
|
||||
under [single-source loading](#single-source-loading); native contexts retain
|
||||
their complete namespace, base, and root requirements.
|
||||
@@ -1629,6 +1654,8 @@ config:
|
||||
Each selected document follows the same strict combination rules. A missing
|
||||
optional input can be absent; invalid declarations, unreadable selected files,
|
||||
and conflicting settings still fail loading.
|
||||
Main-relative underlays follow the same import rules once a context is selected;
|
||||
their no-base behavior is described under [layered loading](#layered-loading).
|
||||
|
||||
| Option | `path` | `dir` | `glob` |
|
||||
| --- | --- | --- | --- |
|
||||
|
||||
@@ -38,8 +38,8 @@ Start with one file, then choose the tasks your application needs:
|
||||
- Use `LoadLayered` when several inputs should supply lower-priority defaults
|
||||
beneath the main source. Each underlay group combines its inputs strictly.
|
||||
Each [`Input`](../../layered.go) owns its source, filesystem, destination, and
|
||||
import choice. The overlay filesystem context is selected independently in
|
||||
[`LayeredOptions`](../../layered.go).
|
||||
import choice. Main-relative bindings and overlay context selection are
|
||||
available in [`LayeredOptions`](../../layered.go).
|
||||
- Load-wide rules belong to [`Options`](../../lifecycle.go) or
|
||||
[`LayeredOptions`](../../layered.go): the application definition (including
|
||||
version policy), import defaults, and explicit overrides. These do not turn
|
||||
@@ -53,6 +53,8 @@ file and layered cases. For import-focused adoption, see the
|
||||
[prompt catalog](../../examples/consumers/prompt_catalog.go); for explicit
|
||||
discovery composed with sequences, see the
|
||||
[sequence catalog](../../examples/consumers/sequence_catalog.go).
|
||||
For discovered main files with companion imports and an overlay, see the
|
||||
[main-relative example](../../examples/consumers/main_relative.go).
|
||||
|
||||
## Happy path
|
||||
|
||||
|
||||
@@ -66,6 +66,7 @@ Accepted decisions:
|
||||
- [Keep main-file discovery explicit and composable](adr/0019-explicit-configuration-discovery.md)
|
||||
- [Scope configuration controls to document roots](adr/0020-document-root-controls.md)
|
||||
- [Match registered records during resolution inspection](adr/0021-sequence-resolution-correspondence.md)
|
||||
- [Bind companion contexts after main selection](adr/0022-main-relative-context.md)
|
||||
|
||||
For cross-cutting changes, follow every applicable row. Completed implementation
|
||||
and remediation plans are preserved in Git history at `cb5b435`; use current
|
||||
|
||||
@@ -20,6 +20,11 @@ patterns to discovered names, and form a set union of matching candidates across
|
||||
overlapping roots. Exclusions are applied as set difference after discovery and
|
||||
candidate normalization. They do not suppress discovery or resource errors.
|
||||
|
||||
Layered assembly passes the resolved main context to underlay acquisition,
|
||||
replacing tier-local identity state. Contextless bindings prepare declaration
|
||||
policies and assemble the declaring payload without opening access or inventing
|
||||
selection evidence. Selected contexts use the normal acquisition and handle path.
|
||||
|
||||
Directory traversal tracks canonical directories on the active traversal path.
|
||||
It adds an identity when descending and removes it when the frame is popped, so
|
||||
only a back-edge to an active directory is a traversal cycle; a later alias is
|
||||
|
||||
@@ -11,7 +11,7 @@ Implemented component owners:
|
||||
explicitly selected configuration discovery. A search resolves once during
|
||||
shared acquisition; the selected concrete file or absent-input state then
|
||||
follows the existing composition, metadata, and resource-context lifecycle.
|
||||
It also owns grouped strict composition, recursive precedence assembly,
|
||||
It also owns main-relative context selection, grouped strict composition, recursive precedence assembly,
|
||||
per-input mapping and sequence-item placement, YAML collection declarations
|
||||
and operations for identified sequences, and application identity registrations for strict
|
||||
composition, inheritance, and final validation. It applies the selected
|
||||
|
||||
@@ -151,6 +151,11 @@ initialization, and resource checks. See the [configuration discovery
|
||||
contract](../config.md#configuration-discovery) and [ADR
|
||||
0019](../adr/0019-explicit-configuration-discovery.md).
|
||||
|
||||
The layered loader may bind application-selected dependent locations after main
|
||||
selection. Applications still choose search conventions and must explicitly
|
||||
select an overlay. [ADR 0022](../adr/0022-main-relative-context.md) records
|
||||
the deferred context decision.
|
||||
|
||||
The engine applies each input's mapping destination within the existing source
|
||||
assembly, applies immediate-parent-relative YAML destinations to imports, and
|
||||
retains each effective placement with source identity and inspection records;
|
||||
|
||||
@@ -227,6 +227,8 @@ incidental call counts.
|
||||
|
||||
## Stage 8 — Document and verify the completed sprint
|
||||
|
||||
**Status:** Complete. Focused integration and consumer checks, the everyday checker, the race suite, and changed local-link checks passed.
|
||||
|
||||
Update the public declaration comments and the discovery, layered loading,
|
||||
imports, resource context and selected overlay sections of
|
||||
[the configuration contract](../config.md). Document lookup versus
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Main-relative companion imports and overlays
|
||||
|
||||
Status: design settled; not implemented. The [implementation plan](implementation.md)
|
||||
Status: implemented. The [implementation plan](implementation.md)
|
||||
owns the concrete API spelling, execution order, and verification. Baseline: `191015e`.
|
||||
|
||||
## Purpose
|
||||
@@ -19,7 +19,7 @@ Reuse existing optional-file, directory, recursion, pattern, destination, and
|
||||
composition behavior. A new file constructor alone would not cover this workflow,
|
||||
and a second fragment-discovery implementation is unnecessary.
|
||||
|
||||
## Current baseline
|
||||
## Prior baseline
|
||||
|
||||
The [discovery contract](../config.md#configuration-discovery) resolves
|
||||
`SearchConfig` inside loading to one host file or absent input. Selected-file
|
||||
|
||||
Reference in New Issue
Block a user