2026-11-09 18:39:25 +00:00
|
|
|
|
# Implement main-relative companion inputs
|
|
|
|
|
|
|
|
|
|
|
|
The [feature roadmap](main-relative-inputs.md) owns the target behavior and the
|
|
|
|
|
|
four settled decisions. The [Audita request](audita-request.md) supplies the
|
|
|
|
|
|
consumer workflow. Implement in order as one sprint. Intermediate stages may
|
|
|
|
|
|
leave code or tests incomplete; run focused checks when their dependencies are
|
|
|
|
|
|
in place, and require a coherent, validated tree at the end. Stage boundaries
|
|
|
|
|
|
are handoffs for Luna, not required commit boundaries.
|
|
|
|
|
|
|
|
|
|
|
|
Start with the [development guide](../development.md) and its applicable
|
|
|
|
|
|
agent, architecture, testing, and documentation policies. Keep the public
|
|
|
|
|
|
facade root-owned. Keep ordinary loading, file source semantics, and existing
|
|
|
|
|
|
explicit layered overlays compatible. No dependency change, new YAML syntax,
|
|
|
|
|
|
second discovery pass, application callback for context selection, or new
|
|
|
|
|
|
release gate is part of this work.
|
|
|
|
|
|
|
|
|
|
|
|
## Stage 1 — Define the Go API and preflight rules
|
|
|
|
|
|
|
2026-11-09 18:42:09 +00:00
|
|
|
|
**Status:** Complete. Main-relative descriptions and layered preflight are in place.
|
|
|
|
|
|
|
2026-11-09 18:39:25 +00:00
|
|
|
|
In root and engine `layered.go`, add matching descriptions and carry them into
|
|
|
|
|
|
`loadInput` and `loadRequest` in the engine. Use these public names:
|
|
|
|
|
|
|
|
|
|
|
|
```go
|
|
|
|
|
|
type MainRelativeContext struct {
|
|
|
|
|
|
Fallback *FilesystemContext
|
|
|
|
|
|
RequireBase bool
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func MainRelative() *MainRelativeContext
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
`MainRelative()` returns a fresh pointer to a zero-valued description. Add
|
|
|
|
|
|
`Input.MainRelative *MainRelativeContext` and
|
|
|
|
|
|
`LayeredOptions[T].OverlayFallback *FilesystemContext`. The latter only needs
|
|
|
|
|
|
a concrete context: a selected overlay already requires a base. Use keyed
|
|
|
|
|
|
literals in new examples. Copy the description and its concrete fallback into
|
|
|
|
|
|
the private request using the existing filesystem-context conversion in
|
|
|
|
|
|
`lifecycle.go`; preserve the caller's native namespace and reader ownership
|
|
|
|
|
|
rules. Store resolved state only in the current load.
|
|
|
|
|
|
|
|
|
|
|
|
In layered preflight, apply the concrete-context override before checking
|
|
|
|
|
|
binding scope. Reject an active main binding and active bindings on `File`,
|
|
|
|
|
|
`Filesystem.File`, and `SearchConfig` underlays with `CodeOptionsInvalid`.
|
|
|
|
|
|
Keep ordinary source validation, including zero-source rejection. A valid
|
|
|
|
|
|
`NoSource` underlay remains absent; binding does not make it contribute content
|
|
|
|
|
|
or evidence, and `RequireBase` still applies to an active binding. Suppress the
|
|
|
|
|
|
synthetic-source missing-context preflight error only for a valid active
|
|
|
|
|
|
underlay binding. Binding does not enable imports.
|
|
|
|
|
|
|
|
|
|
|
|
Check the static shape of a fallback that can participate, using the existing
|
|
|
|
|
|
non-file context rules (both base and root, host absolute paths or native
|
|
|
|
|
|
namespace paths). Defer existence, directory, access, and resolved containment
|
|
|
|
|
|
checks until selection. Ignore the shadowed binding entirely when
|
|
|
|
|
|
`Input.Filesystem` is supplied; likewise ignore the overlay fallback when the
|
|
|
|
|
|
overlay is inactive or an explicit `OverlayFilesystem` wins. Keep overlay
|
|
|
|
|
|
name and explicit-context preflight checks. Move the missing-context error for
|
|
|
|
|
|
active layered overlays to Stage 5, where selection can see the main. Do not
|
|
|
|
|
|
change preflight for ordinary `Load`.
|
|
|
|
|
|
|
|
|
|
|
|
Cover active binding scope, explicit overrides (including invalid explicit
|
|
|
|
|
|
contexts), and fallback shape through public option tests. Reuse existing
|
|
|
|
|
|
option diagnostic codes; avoid promising order among unrelated errors.
|
|
|
|
|
|
|
|
|
|
|
|
## Stage 2 — Bind companion inputs with a usable context
|
|
|
|
|
|
|
2026-11-09 18:45:00 +00:00
|
|
|
|
**Status:** Complete. Focused host, native, symlink, fallback, and grouped-load checks passed.
|
|
|
|
|
|
|
2026-11-09 18:39:25 +00:00
|
|
|
|
In `assembleLayeredInput`, pass the main's `assembledInput.resolvedFilesystem`
|
|
|
|
|
|
to `acquireUnderlayGroupScopes`. Reuse the acquisition handoff in
|
|
|
|
|
|
`acquisition.go` and resolution helpers in `filesystem_context.go`; do not
|
|
|
|
|
|
select or open the main again. Verify the handoff covers a file or selected
|
|
|
|
|
|
search without imports, a native file, explicit non-file contexts, and
|
|
|
|
|
|
`NoSource` or exhausted optional search with explicit context. Preserve the
|
|
|
|
|
|
absent host root-only rule and the native context requirements. A main
|
|
|
|
|
|
acquisition/context error is terminal, never an absent-base signal.
|
|
|
|
|
|
|
|
|
|
|
|
For each underlay, use its explicit filesystem first; otherwise, for active
|
|
|
|
|
|
bindings, use the resolved main context, then its fallback. For main-derived
|
|
|
|
|
|
contexts copy the resolved base and root and keep the native namespace object
|
|
|
|
|
|
intact. Clear `mainPath` in the copy: the companion is still a synthetic
|
|
|
|
|
|
source, not a second copy of the main. Use the receiving tier's existing host
|
|
|
|
|
|
identity token and visited state; the main's host location and scope carry
|
|
|
|
|
|
over, not its tier bookkeeping. A selected fallback uses the same
|
|
|
|
|
|
resolution/validation rules as a concrete non-file context; pass it through
|
|
|
|
|
|
the normal acquisition path where possible. Validate selected contexts even
|
|
|
|
|
|
when imports are inactive as required by the existing context contract;
|
|
|
|
|
|
do not probe the fallback when main derivation wins.
|
|
|
|
|
|
|
|
|
|
|
|
Carry a pre-resolved derived context through the private acquisition request
|
|
|
|
|
|
to `acquireImportedInput` and the standalone/context completion path. Use
|
|
|
|
|
|
existing import access, traversal and closure; keep root/base checks, parse
|
|
|
|
|
|
and version processing, budget accounting, and source evidence. Bound
|
|
|
|
|
|
synthetic sources can expose the same passive context as explicitly located
|
|
|
|
|
|
synthetic sources but keep their original kind, label and empty physical path.
|
|
|
|
|
|
The imported files use their own paths and directory bases.
|
|
|
|
|
|
|
|
|
|
|
|
Make the existing native namespace anchor selection in
|
|
|
|
|
|
`acquireUnderlayGroupScopes` use the **selected** namespace, including
|
|
|
|
|
|
derived or fallback contexts. Determine anchors from selected descriptors
|
|
|
|
|
|
without opening directories or validating later inputs out of turn. Keep the
|
|
|
|
|
|
first input position anchor for each namespace and the existing host and
|
|
|
|
|
|
synthetic anchoring and tier boundaries. Preserve independent sources and
|
|
|
|
|
|
repetition semantics, including two different native namespaces with the
|
|
|
|
|
|
same label and paths.
|
|
|
|
|
|
|
|
|
|
|
|
Cover successful bindings for host and native contexts, an unrelated working
|
|
|
|
|
|
directory, canonical host symlink target directory, a wider root that permits
|
|
|
|
|
|
contained parent-relative imports, and reuse of one description across loads.
|
|
|
|
|
|
Test selected and unused fallbacks. Defer the no-base path to Stages 3–4.
|
|
|
|
|
|
|
|
|
|
|
|
## Stage 3 — Share import declaration preparation
|
|
|
|
|
|
|
|
|
|
|
|
In `import_acquisition.go`, extract the context-independent first pass of
|
|
|
|
|
|
`walkDeclarations` so both normal traversal and the contextless path can
|
|
|
|
|
|
use it. Prepare from the already parsed declaration specs, apply
|
|
|
|
|
|
`resolveImportPolicy` with the load-wide defaults, and retain existing
|
|
|
|
|
|
placement checks and diagnostic sites. For an input placed as a sequence
|
|
|
|
|
|
item, prepare against its local mapping scope and keep the existing diagnostic
|
|
|
|
|
|
path projection. Use the already processed root directives from
|
|
|
|
|
|
`processAcquiredInputVersion`; do not reparse YAML, repeat version checks,
|
|
|
|
|
|
or infer a filesystem path from a source label.
|
|
|
|
|
|
|
|
|
|
|
|
Keep selection and admission in the existing normal walk. This stage changes
|
|
|
|
|
|
no bound import behavior. Reuse import specification, defaults, placement,
|
|
|
|
|
|
selection, and diagnostic tests for verification.
|
|
|
|
|
|
|
|
|
|
|
|
## Stage 4 — Finish companion handling when no base exists
|
|
|
|
|
|
|
|
|
|
|
|
Handle the no-base outcome only for an active underlay binding with no
|
|
|
|
|
|
override, usable main context, or fallback. `RequireBase` fails with
|
|
|
|
|
|
`CodeOptionsInvalid` regardless of import enablement or declaration count.
|
|
|
|
|
|
Otherwise retain normal source parsing, version handling, disabled-import
|
|
|
|
|
|
validation, and contribution assembly. When imports are enabled, use Stage 3
|
|
|
|
|
|
to validate their declarations even if none will be expanded.
|
|
|
|
|
|
|
|
|
|
|
|
For required declarations use `CodeImportMissing` at the first named target
|
|
|
|
|
|
site for `path`, or the selector field site for `dir` and `glob`. Say that no
|
|
|
|
|
|
base is available; do not report a file search that did not happen. A required named-path declaration with nonempty
|
|
|
|
|
|
targets fails, as do required directory or glob selections. Empty
|
|
|
|
|
|
`path: []` has no targets even if required. Explicit per-declaration
|
|
|
|
|
|
requiredness continues to override the application default. Skip optional
|
|
|
|
|
|
expansion without calling a filesystem selector.
|
|
|
|
|
|
|
|
|
|
|
|
Assemble the declaring payload once, with its original source record,
|
|
|
|
|
|
placement, sequence declarations, and version evidence. Share the existing
|
|
|
|
|
|
root contribution assembly and accounting where needed for imports-enabled
|
|
|
|
|
|
payloads, including the pending local scope for sequence-item placement.
|
|
|
|
|
|
Do not turn imports off to bypass validation or introduce a dummy access
|
|
|
|
|
|
object, dummy filesystem, acquired child, resource context, or
|
|
|
|
|
|
`EmptyImportSelections` entry. Charge the declaring payload as usual; charge
|
|
|
|
|
|
no traversal or child work for skipped imports. Normal searches with a
|
|
|
|
|
|
selected context still produce their normal empty-selection evidence.
|
|
|
|
|
|
|
|
|
|
|
|
Test absent optional discovery and explicit absence, mixed payload with
|
|
|
|
|
|
optional and required declarations, application defaults and explicit
|
|
|
|
|
|
overrides, empty paths under both requiredness values, require-base with and
|
|
|
|
|
|
without enabled imports, malformed declarations and invalid placement,
|
|
|
|
|
|
source version policy, mounted payloads, and absence of synthetic search
|
|
|
|
|
|
evidence. Required discovery and failed selected main processing must not
|
|
|
|
|
|
activate this path.
|
|
|
|
|
|
|
|
|
|
|
|
## Stage 5 — Derive layered overlay context and lookup base
|
|
|
|
|
|
|
|
|
|
|
|
In the existing overlay branch of `assembleLayeredInput`, select in order:
|
|
|
|
|
|
explicit `OverlayFilesystem`, usable main context, `OverlayFallback`.
|
|
|
|
|
|
Skip selection entirely for an inactive name. For an active name without a
|
|
|
|
|
|
base, return `CodeOptionsInvalid`; if context validation fails, propagate
|
|
|
|
|
|
its normal diagnostics without trying another context. Keep overlay selection
|
|
|
|
|
|
after source assembly and main version handling, and preserve existing
|
|
|
|
|
|
failures for missing files, invalid overlays, and repeated sources.
|
|
|
|
|
|
|
|
|
|
|
|
In `selected_overlay.go`, pass a lookup directory separately to the existing
|
|
|
|
|
|
selected overlay loader (for example on `selectedOverlayRequest`). Ordinary
|
|
|
|
|
|
`Load` and explicit layered contexts use the resolved root as before.
|
|
|
|
|
|
Derived and fallback contexts use the resolved base. Open access at the
|
|
|
|
|
|
containment root; join the lookup directory with `overlays/<name>.yml`
|
|
|
|
|
|
using the selected namespace's path operations, then use normal
|
|
|
|
|
|
canonicalization and containment. Keep the namespace, source identities,
|
|
|
|
|
|
version processing, limits, source contexts, and handle ownership on the
|
|
|
|
|
|
existing path. Do not use the lookup directory as a replacement root or
|
|
|
|
|
|
construct a new namespace.
|
|
|
|
|
|
|
|
|
|
|
|
Cover canonical host symlink targets, wider roots with distinct overlay
|
|
|
|
|
|
files at base and root, native filesystems, context on a non-file or absent
|
|
|
|
|
|
main, inactive overlay with an invalid fallback, explicit override with no
|
|
|
|
|
|
fallback on error, base-relative fallback, and missing selected overlays
|
|
|
|
|
|
without recovery. Retain ordinary and explicit root-relative regression
|
|
|
|
|
|
coverage, including existing namespace and repeated-source cases.
|
|
|
|
|
|
|
|
|
|
|
|
## Stage 6 — Exercise the combined workflow
|
|
|
|
|
|
|
|
|
|
|
|
Add a maintained executable example in `examples/consumers` for a single
|
|
|
|
|
|
layered load using `SearchConfig`, a mapping companion with optional named
|
|
|
|
|
|
files and recursive directory imports, an independently located embedded
|
|
|
|
|
|
tier, and one selected overlay. Use actual fixture files and the existing
|
|
|
|
|
|
`*.yml` and `*.yaml` pattern list; show destination placement, transitive
|
|
|
|
|
|
imports, and strict peers. Demonstrate precedence from embedded catalog
|
|
|
|
|
|
through operator underlay, main, and overlay. Include a registered identity
|
|
|
|
|
|
case for whole-record upsert across tiers and a same-tier duplicate failure.
|
|
|
|
|
|
No change to Audita or PromptKit is needed.
|
|
|
|
|
|
|
|
|
|
|
|
## Stage 7 — Close remaining integration and evidence gaps
|
|
|
|
|
|
|
|
|
|
|
|
Fill remaining public integration gaps without repeating every engine test:
|
|
|
|
|
|
first and later search candidates, explicit file and mapping/bytes/reader
|
|
|
|
|
|
sources, only the file, only the directory, both, neither and an empty
|
|
|
|
|
|
directory, invalid or inaccessible selected fragments (use a deterministic
|
|
|
|
|
|
filesystem error where permissions are unreliable), and a selected main
|
|
|
|
|
|
failure that cannot resume search or use the fallback. Use focused fixtures
|
|
|
|
|
|
to check layer/index, physical paths, passive resource contexts, normal
|
|
|
|
|
|
source-version evidence, shared acquisition budgets and cleanup on failure.
|
|
|
|
|
|
Check that callbacks execute only in the existing lifecycle, and that a
|
|
|
|
|
|
reused description binds anew. Preserve existing ownership and detached
|
|
|
|
|
|
inspection checks. Do not require new global diagnostic ordering or test
|
|
|
|
|
|
incidental call counts.
|
|
|
|
|
|
|
|
|
|
|
|
## Stage 8 — Document and verify the completed sprint
|
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
containment, canonical host symlink behavior, override and fallback
|
|
|
|
|
|
precedence, and absent-base behavior. Link the maintained examples from
|
|
|
|
|
|
consumer guidance. Update the [internal overview](../internal/overview.md)
|
|
|
|
|
|
and [import internals](../internal/imports.md) for the actual handoff,
|
|
|
|
|
|
contextless path, and ownership.
|
|
|
|
|
|
|
|
|
|
|
|
Record the deferred context decision in the next numbered ADR, extending
|
|
|
|
|
|
[ADR 0019](../adr/0019-explicit-configuration-discovery.md) without rewriting
|
|
|
|
|
|
it. Clarify in the architecture policy that the layered loader binds
|
|
|
|
|
|
application-selected dependent locations after main selection; applications
|
|
|
|
|
|
still own search conventions, and an explicit overlay selection is required.
|
|
|
|
|
|
Preserve existing scope boundaries. Use the documentation policy's canonical
|
|
|
|
|
|
owners rather than restating the full contract in every document.
|
|
|
|
|
|
|
|
|
|
|
|
Run the focused engine, facade, and consumer tests added or affected above,
|
|
|
|
|
|
then the everyday `./scripts/check.sh`. Run the race suite for the
|
|
|
|
|
|
load-local ownership and description reuse changes. Check changed Markdown
|
|
|
|
|
|
links, public facade boundaries and examples. Use the declared Go toolchain
|
|
|
|
|
|
and existing dependencies. No release or benchmark is part of completion.
|
|
|
|
|
|
Report validation and any remaining limitations; commit the completed sprint
|
|
|
|
|
|
under the agent policy.
|