Retire completed roadmaps before the next release

This commit is contained in:
2026-10-16 11:36:21 +00:00
parent e4270c9b14
commit e66c37161c
12 changed files with 48 additions and 2202 deletions

View File

@@ -39,6 +39,18 @@ Built-in sibling inheritance using `extends` is implemented as described in the
[configuration contract](../config.md#sibling-inheritance). It is distinct from
inheritance between selected overlay files, which remains excluded above.
## Possible source-version follow-ups
Independent collection or subtree specification policies were outside the
completed shared source-version feature and remain possible future work, with
no committed delivery plan. Current [source-version checks](../config.md#shared-source-version-checks)
use one application-owned policy across participating documents. Reconsider
independent policies if concrete consumer needs justify a separate design
decision; this is not a permanent prohibition. The original request and its
selected scope remain at `e4270c9:docs/roadmap/promptkit-feature-request.md` in
Git history. Existing sequence-composition and cross-scope inheritance deferrals
above also remain unchanged.
## Deferred performance experiments
The completed performance audit left these internal optimization candidates for

View File

@@ -1,720 +0,0 @@
# Implement shared source version checks
Status: complete. All 14 implementation stages are validated.
Audience: a Luna coding agent implementing one numbered stage per prompt, in
order. The [feature roadmap](source-version-checks.md) owns target behavior and
accepted policy; this plan owns concrete implementation decisions and execution.
Planning baseline: `d9f08ff7198ad5a8fb5c49a43e7ccdad2ad0e707`.
The completed relative-import plan and evidence replaced here remain at
`d9f08ff:docs/roadmap/implementation.md`. Its roadmap and remediation are
complete, not instructions for this sprint.
## Working instructions
- Read `AGENTS.md`, [development.md](../development.md), and applicable policies.
This work involves [architecture](../policy/architecture.md),
[testing](../policy/testing.md), and [documentation](../policy/documentation.md).
Use graph-first discovery, verify live sources, and preserve unrelated work.
- Implement one stage at a time. Record completion, actual commands, toolchain,
results, limitations, and implementation revisions here. Commit each validated
nonempty unit with a short plain-English message.
- Keep public types root-owned with explicit engine conversions. Reuse semantic
Paths, documents, composition, overlay, version validation, sensitivity, and
inspection. No second loader, public parser nodes, generic transformation
hooks, or consumer-specific policy.
- Default mode preserves current behavior, including effective-path lookup,
main checker timing, and protected-path failures. Enabled modes deliberately
select source-local declarations and non-main consumption. Do not broaden
these changes into unrelated default-mode fixes.
- Do not add equality/SemVer rules, non-null requirements, numeric coercion,
path-form restrictions, source uniqueness policies, limits, dependencies,
toolchain changes, or release gates. Declaration acceptance is not fragment
schema conformance. Exclusions describe this delivery, not new permanent bans.
- Internal stages may test modes before exposing them publicly. Keep Go/current
documentation accurate to each delivered boundary; the roadmap owns the target.
- Use focused offline tests while iterating and `./scripts/check.sh` for each
production-code unit. Documentation-only units need local-link and whitespace
checks. Reuse existing tests; avoid a full mode/source/tier/path cross-product,
helper-call assertions, allocation identity, or exact message snapshots.
No independent benchmarks or performance thresholds are required.
- Do not rewrite accepted ADR decisions, publish, select a release version,
upgrade consumers, or retire completed roadmap records. Public struct additions
and enabled-mode path interpretation need minor-release compatibility guidance
during separately authorized release preparation.
## Concrete implementation decisions
### Public options and evidence
1. Add root-owned `type SourceVersionMode uint8` with consecutive constants
`SourceVersionModeDefault` (zero), `SourceVersionModeIfPresent`, and
`SourceVersionModeRequired`. Add `SourceVersionMode SourceVersionMode`
to `Options[T]` and `LayeredOptions[T]`, not Definition, Input, or YAML.
Mirror privately and convert explicitly. Unknown modes, or enabled modes
without a usable VersionRule and checker, produce `CodeOptionsInvalid`
before source access or callbacks. Keep existing rule/path validation.
2. Add root-owned `SourceVersionCheck` with `Path Path`, `Version Version`,
`Location *Location`, and `Redacted bool`. Add
`VersionCheck *SourceVersionCheck` to SourceInfo. Nil means no enabled
declaration processing occurred. A non-nil record with Present false records
observed absence, including optional omissions whose checker was skipped; it
does not claim a successful callback or acceptance. On a malformed selected
value or overlap failure, retain only known presence/null, path, and location
with conservative redaction; never expose arbitrary scalar/container content.
Read/parse failures before this phase leave the record nil.
Reuse Version's absent/null/text model.
Redaction preserves presence/null, empties classified Text, and sets Redacted.
3. Evidence Path is source-local, with semantic segment intent resolved against
original source shape where possible. Present declarations retain their actual
authored location; absence uses the nearest available source location, not an
invented header coordinate. Destination and source identity already belong
to SourceInfo. Add no new query API, raw-node field, expected-version value,
or string/integer discriminator.
4. Clone the record, Path, and Location at all query/conversion/error boundaries.
Callback-time source queries and failed-load source records expose conservative
text-redacted evidence until final classification is available. Successful
final inspection may expose unclassified text; previous copies stay detached.
Keep raw evidence separate from public-safe source records. Do not extend a
failed load to finish classification or introduce a failed-load evidence API.
### Lookup, checking, and diagnostics
5. Carry the configured local rule and an effective main-owned rule separately.
Enabled modes derive the latter once by prefixing main's Destination using
semantic segments. Use it consistently for remaining-payload protection,
inheritance/CLI checks, decoding's exact-path integer exception, and application
metadata. Default mode uses the configured effective path unchanged. Never
mutate caller rules or repeatedly prefix a path.
6. Read each declaration from its original source document before mounting or
consumption. Keep contextual numeric-key/index resolution, empty/escaped keys,
parsed tokens, and explicit indices. No mapping-only/root-only restriction;
selecting a root mapping simply fails ordinary version-type validation.
7. Missing members, unavailable indices, and obstructing non-containers mean the
requested declaration is absent, as with current document lookup. Distinguish
causes privately for accurate nearest locations, not to invent rejection of
an ancestor's domain value. If-present non-main absence skips the checker and
records absence; required absence is `CodeVersionError` before callback.
A present unsupported selected value is `CodeVersionType`, not absence.
Actual main absence in if-present mode still reaches its checker once;
required mode rejects it first. NoSource is not a document and preserves the
existing single absent-main callback in both enabled modes.
8. Reuse strict decimal recognition and exact Version.Text. No machine-integer
conversion or comparison with main. Required null reaches the checker and can
be accepted. Warnings alone succeed and accepted non-main headers are consumed.
9. Enabled declaration checkers and their Validation paths use the current
source's local coordinates for every role, including mounted main. Resolve
primary/related locations against that original source. Generated header
diagnostics also use source-local paths; do not rebase them to phantom payload
fields. Ordinary payload diagnostics remain effective-path diagnostics.
Default version diagnostics are unchanged. Reuse `CodeVersionError` for
missing-required/structural-overlap failures, `CodeVersionType` for malformed
values, and existing checker error/warning/hook codes. No new code is needed.
10. Keep callback lifetime, safe generic returned-error handling, and synchronous
execution. Check main once from its own declaration, never a composed value.
Check other documents as acquired, before composition can hide rejection.
Do not promise a new inter-source callback order or exhaustive acquisition
after failure. Build schema successfully before callbacks; enabled mode may
prepare it before acquisition, without changing the default pipeline.
Defaults, decoding, resolution, secret lookup, and validation still run once
on the assembled application.
### Consumption and structural boundaries
11. Validate source-local controls before consumption can erase malformed
directives. Reuse import/overlay validation and inheritance-control
recognition, not per-fragment inheritance expansion. Reject actual overlap
with configkit directive structure or an extends control, including headers
inside import declarations or invalid control subtrees. Unrelated siblings
and ordinary keys containing those spellings remain allowed. Missing headers
follow their mode; they do not suppress independently invalid directives.
Add no error-order guarantee.
12. Retain main's node. Remove exactly an accepted non-main mapping member or
sequence element in an owned payload tree. Preserve authored ancestors,
newly empty mappings/sequences, sibling order, and physical node locations.
Sequence deletion shifts later payload indices; evidence keeps original
coordinates. Removing a member inside a sequence element keeps that element,
even when empty. No null placeholders, ancestor pruning, or Source mutation.
13. Extract source-root imports from original content; consume headers in its
application body before placement/composition. Underlay roots and all imports
are non-main; selected overlays are also non-main. Check remaining placed
payload against effective main ownership. Consumption does not permit
forbidden overlay controls, sequence alignment, or CLI ownership bypass.
14. Charge original acquired bytes/nodes once, including consumed headers. Apply
existing placed-body bounds and full-destination wrapper charges to remaining
payload. Keep source/depth/glob accounting, records, resource contexts, and
physical repetition unchanged. Acquired header-only mappings retain their
empty body/ancestors and placement; NoSource/optional absent sources add no
document or evidence. No new cap or full-source clone is required.
### Evidence and sensitivity
15. Carry private evidence per acquired SourceID in per-load acquisition/assembly
state, separate from application history. Retain an owned scalar/absence,
original local and mounted semantic paths, location, and the ancestor-shape
context needed for classification. Capture key/index intent before deletion.
Retain only needed context, not entire source-tree clones or loading handles.
16. At publication classify evidence at its original mounted path and ancestors
with existing schema tags, predicate, historical projected sensitivity,
inheritance, and recorded-dependency propagation. Include evidence-only
candidates in classification without adding them to Metadata paths, payload
applications, rendering, or Diff. Do not infer original sequence shape from
shifted payload or round-trip through pointer strings. Reuse dependency
closure rather than building a weaker independent redactor. Retained header
text never triggers credential lookup.
17. Sanitize source records before public conversion; early records remain
conservatively redacted. Final successful evidence uses completed sensitivity,
retaining historical classification despite replacement. Main's normal
payload/history remains intact. Consumed non-main headers are only source
evidence: equal payloads with different headers must not acquire effective
differences merely from that evidence.
## Implementation owner map
| Responsibility | Existing owners to inspect and extend |
| --- | --- |
| Public options, callbacks, records, conversions | `lifecycle.go`, `layered.go`, `records.go`, `conversion.go`, `diagnostic.go` |
| Options and per-load coordination | `internal/engine/options.go`, `load.go`, `load_request.go`, `layered.go` |
| Acquisition, directives, placement, overlay | `internal/engine/load_input.go`, `imports.go`, `directives.go`, `destination.go`, `selected_overlay.go` |
| Checking and semantic paths | `internal/engine/validation.go`, `document_walk.go`, `version_path_test.go` |
| Evidence copies, errors, publication | `internal/engine/records.go`, `diagnostic.go`, `load_result.go` |
| Classification and propagation | `internal/engine/secret.go`, schema secret-path helpers, `secret_propagation_test.go`, `render_test.go` |
| Compatibility tests | Engine `load_test.go`, `imports_test.go`, `selected_overlay_test.go`, `load_request_test.go`, `destination_*_test.go`; root `facade_version_test.go`, `facade_layered_test.go`, `facade_destination_test.go`, `records_test.go` |
Private helper names/file splits may follow the implementation while preserving
these decisions. A new internal package solely for headers is unnecessary.
## Validation commands
Use the applicable subset during each stage; add new tests under the
SourceVersion name family so focused checks include them. Existing test owners
remain authoritative. Commands run from the repository root with dependencies
already available; these are development checks, not new release gates.
```sh
GOTOOLCHAIN=local GOPROXY=off go test ./internal/engine -run 'SourceVersion|Version|Import|Underlay|Overlay|Destination'
GOTOOLCHAIN=local GOPROXY=off go test ./internal/engine -run 'SourceVersion|Secret|Sensitivity|History|Inheritance|Resolution'
GOTOOLCHAIN=local GOPROXY=off go test . -run 'SourceVersion|Version|Layered|SourceInfo|Destination|PublicFacadeBoundary|DependencyImportBoundary'
GOTOOLCHAIN=local GOPROXY=off go test ./examples/consumers
GOTOOLCHAIN=local GOPROXY=off go test . -run '^TestLocalDocumentationLinks$'
GOTOOLCHAIN=local ./scripts/check.sh
GOTOOLCHAIN=local GOPROXY=off go test -race ./...
git diff --check
```
## Stage 1 — Capture the baseline and record the extension
1. Confirm the worktree/baseline; read the roadmap, plan, applicable policies,
contract, and relevant ADRs. Run the everyday check and record revision,
toolchain, results, and environmental limitations.
2. Write Accepted ADR `0013-shared-source-version-checks.md` after checking the
number is unused. Record one policy, modes, source-local checking, consumption,
evidence, and retained main protection. Focus on ConfigKit's selected choice;
rejected choices belong only in Alternatives Considered. Keep sequencing here.
3. Preserve prior accepted decision text. Add status/navigation links where
useful. Note struct additions and enabled mounted-main interpretation as
minor-release considerations; do not select a version or write release notes.
Validate: local-link test and whitespace. Done: baseline and accepted decision
recorded, with no runtime changes.
Completion evidence: reviewed baseline `d9f08ff7198ad5a8fb5c49a43e7ccdad2ad0e707`
and the clean implementation worktree at `70c95bf660794809e58182d736c67c9085238705`;
confirmed ADR 0013 was unused. The everyday check passed with Go
`go1.26.8 linux/amd64` using
`GOTOOLCHAIN=local GOPROXY=off ./scripts/check.sh`. Documentation links and
whitespace are validated for this unit. No environmental limitation affected
these checks; validation was performed on Linux/amd64 only. No runtime changes
were made.
## Stage 2 — Share version interpretation without changing defaults
1. Separate reusable value interpretation and callback reporting from checkVersion,
retaining its default entry point, absence/null/text, exact integers, warnings,
safe errors, primary/related locations, and context closure.
2. Reuse existing scalar/path/hook/lifetime tests; add only missing protection for
the refactor. Introduce a small private result separating checked value and
diagnostics. Failure decisions must use severity, not slice length.
3. Do not globally rewrite unrelated diagnostic plumbing or public behavior.
Validate: engine version/validation and facade version tests; everyday check.
Done: shared mechanisms exist with unchanged default behavior.
Completion evidence: `checkVersion` remains the existing diagnostic-only entry
point and now delegates to private scalar interpretation and callback-reporting
helpers through a result that keeps the interpreted `Version` separate from
diagnostics. Checker invocation and returned-error fallback use diagnostic
severity, preserving warnings without treating them as failures. Added a
regression for a warning followed by a returned checker error. These checks
passed with Go `go1.26.8 linux/amd64`:
- `GOTOOLCHAIN=local GOPROXY=off go test ./internal/engine -run 'Version|Validation'`
- `GOTOOLCHAIN=local GOPROXY=off go test . -run 'Version'`
- `GOTOOLCHAIN=local ./scripts/check.sh`
No environmental limitation affected validation; checks ran on Linux/amd64.
## Stage 3 — Implement source-local extraction and exact consumption
1. Add a private processor receiving original document, local rule, source role,
destination, and mode. Implement decisions 6–12, including missing/obstructed
lookup and pre-consumption structural overlap.
2. Return owned remaining payload, original import declarations, and private
evidence with original segment/ancestor context. No second parser, fragment
decoding, or mutation of reusable input.
3. Add focused tests for vocabulary, missing/obstructed locations, malformed
selected nodes, literal/parsed numeric keys and indices, empty ancestors,
sequence deletion, and actual versus unrelated structural overlap.
Validate: helper/version-path/directive/inheritance-control tests and everyday
check. Done: the processor is tested independently of public loading.
Completion evidence: added a private source-local processor that resolves the
configured path against the original document, separates imports, records
private path/ancestor/location evidence, and consumes accepted contribution
headers only from an owned payload copy. Missing and obstructed paths retain
their cause and nearest available location; required absence fails before the
checker, while optional main absence still invokes it. Structural directive
overlap is rejected before payload consumption. Focused coverage includes the
version vocabulary, malformed nodes, contextual numeric keys and indices,
empty ancestors, sequence deletion, and actual versus unrelated structural
overlap. These checks passed with Go `go1.26.8 linux/amd64`:
- `GOTOOLCHAIN=local GOPROXY=off go test ./internal/engine -run 'SourceVersion|Version|Import|Underlay|Overlay|Destination|Inheritance'`
- `GOTOOLCHAIN=local ./scripts/check.sh`
- `git diff --check`
No environmental limitation affected validation; checks ran on Linux/amd64.
## Stage 4 — Add private mode state and main-document integration
1. Add the engine mode to ordinary/layered options and loadRequest. Validate it at
both entry points before source access. Underlay helper options must not be
treated as standalone loads missing the shared rule.
2. Carry local/effective rules, source roles, warnings, and evidence in per-load
state. Let acquired inputs return evidence and nonfatal diagnostics.
3. In enabled mode, check actual main before imports/composition, retain its node,
and prepare schema before callbacks. Skip the old later main check only in
enabled mode. Preserve NoSource's absent callback and default timing.
4. Thread effective protection through typed conversion, inheritance, CLI, and
application inspection while keeping declaration callbacks local. Test mounted
main, absence/NoSource, null acceptance, invalid options before reading, and
exactly one main callback.
Validate: load/grouped/version/destination/hook tests and everyday check. Done:
private enabled main behavior works without changing old callers.
Completion evidence: added the private mode to ordinary and layered engine
options and the shared load request. Both entry points validate enabled modes
and their shared checker before source acquisition; enabled loads build schema
before invoking the main checker. The parsed main document is checked at its
source-local path before import traversal or layered composition, and its node
remains in the application payload. A single semantic destination-prefixed rule
now protects mounted main ownership during decoding, inheritance, acquired-input
placement, overlays, and CLI overrides; declaration callbacks and diagnostics
retain local paths. Default mode keeps its prior post-composition check timing.
The per-load assembled input retains main evidence and successful warning
diagnostics. NoSource invokes the existing absent-main callback once in either
enabled mode. These checks passed with Go `go1.26.8 linux/amd64`:
- `GOTOOLCHAIN=local GOPROXY=off go test ./internal/engine -run 'SourceVersion|Version|Load|Grouped|Destination|Hook'`
- `GOTOOLCHAIN=local ./scripts/check.sh`
- `git diff --check`
No environmental limitation affected validation; checks ran on Linux/amd64.
The engine mode remains private; public options are scheduled separately.
## Stage 5 — Integrate recursive imports, warnings, and accounting
1. Thread shared state through import entry/traversal. Check each acquired child
once against its own local header, then place and compose remaining payload.
Keep nested/glob effective destinations and original directive discovery.
2. Apply effective main protection afterward. Preserve physical identity,
cycle/repetition, optional absence, source records, resource contexts, and raw
node/byte charges. Header-only acquired mappings still contribute empty bodies.
3. Propagate warnings to load diagnostics without aborting traversal. Update only
relevant callers currently treating any diagnostic as failure.
4. Cover nested/glob imports, mounted relative destinations, accepted unequal
declarations, rejected hidden declarations, required omissions, safe locations,
optional missing sources, raw accounting, and default import.version failures.
Reuse existing containment/glob/source-kind tests.
Validate: import/glob/destination/accounting/version tests and everyday check.
Done: imports use one policy without lost warnings or changed physical traversal.
Completion evidence: threaded one per-load source-version state through the
main import entry and recursive traversal. Each parsed child is checked against
its source-local rule before placement; accepted headers are removed from an
owned payload while the original root still supplies raw byte/node charges and
the processor's original import declarations drive nested traversal. Effective
main ownership is checked against the remaining placed payload. Evidence is
retained with the acquired SourceID, and warning diagnostics accumulate without
stopping traversal. Existing identity, cycle, optional-source, source-record,
resource-context, and accounting paths remain in use. Added coverage for nested
and glob imports at mounted relative destinations, accepted unequal declarations,
rejected hidden declarations, required omissions with source-local locations,
optional missing imports, evidence identity, and raw header byte/node charges. The
existing default import-version failure coverage also passes. These checks
passed with Go `go1.26.8 linux/amd64`:
- `GOTOOLCHAIN=local GOPROXY=off go test ./internal/engine -run 'SourceVersion|Version|Import|Glob|Destination' -count=1`
- `GOTOOLCHAIN=local ./scripts/check.sh`
- `git diff --check`
No environmental limitation affected validation; checks ran on Linux/amd64.
## Stage 6 — Integrate direct underlays and their strict groups
1. Thread state through acquireUnderlayGroup/grouped assembly. Direct underlays
are non-main; imports reuse Stage 5. Preserve layers, indices, contexts,
evidence, and warnings across groups.
2. Consume local headers before strict composition, then protect remaining placed
payload. Preserve tier precedence, empty containers, raw budgets, and reusable
sources in allowed separate tiers.
3. Cover accepted distinct peer declarations, a rejected underlay hidden by main,
required absence, empty/NoSource groups, mounted nested headers, retained
payload conflicts, and unchanged default underlay.version failures.
Validate: grouped/underlay/destination/version tests and everyday check. Done:
direct and imported underlays follow the same checking policy.
Completion evidence: threaded the per-load version state through direct underlay
group acquisition and invoked the shared processor with contribution semantics
for each acquired underlay root. Its own processed payload/import declarations
now drive placement and recursive imports, while remaining placed payload is
still checked against the effective main-owned path before strict composition.
Warnings are accumulated through underlay groups; source records retain their
layer and group index, and the existing accounting, context, and reusable-source
paths remain in place. Added coverage for distinct declarations within one
strict group, warning retention across groups, rejected declarations beneath a
later main value, required omissions, empty groups and NoSource, mounted nested
headers/imports, and protected remaining payload. Existing default underlay
version-protection coverage remains passing. These checks passed with Go
`go1.26.8 linux/amd64`:
- `GOTOOLCHAIN=local GOPROXY=off go test ./internal/engine -run 'SourceVersion|Version|Import|Underlay|Overlay|Destination' -count=1`
- `GOTOOLCHAIN=local ./scripts/check.sh`
- `git diff --check`
No environmental limitation affected validation; checks ran on Linux/amd64.
## Stage 7 — Integrate selected overlays and final ownership checks
1. Process overlay headers after parse/structural validation, before remaining
payload protection and application. Reuse shared state in ordinary and layered
overlay loading; preserve source identity, namespaces, close behavior, and raw
node/byte charges.
2. Carry accepted warnings through assembly. Keep overlay-control restrictions,
and effective main protection through CLI and inheritance.
3. Cover accepted/required/rejected overlay headers, warnings, unchanged default
behavior, ordinary CLI fields without header requirements, mounted-main
protection, and protected payload after nested/sequence consumption. Sequence
evidence stays original while remaining indices shift.
Validate: overlay/override/inheritance/version tests and everyday check. Done:
all document roles are integrated privately; synthetic operations are exempt
from header checks, not from existing protection.
Completion evidence: threaded the shared version state through ordinary and
layered selected-overlay loading. After parse and overlay-directive validation,
the selected overlay is checked as a non-main source; accepted headers are
removed from its owned payload before effective-path protection and application.
The original parsed node still supplies raw byte/node charges, and source
identity, filesystem namespace, closure, overlay-control checks, and override
protection remain on their existing paths. Overlay warnings are accumulated with
the other per-load diagnostics. Added coverage for accepted, required-missing,
and rejected overlay declarations; structural validation before callbacks;
warning retention; CLI overrides without document-header checks; mounted-main
protection after header consumption; and sequence deletion retaining original
evidence coordinates while later payload indices shift. Existing default overlay
version/directive/accounting coverage remains passing. These checks passed with
Go `go1.26.8 linux/amd64`:
- `GOTOOLCHAIN=local GOPROXY=off go test ./internal/engine -run 'SourceVersion|Version|Import|Underlay|Overlay|Override|Inheritance|Destination' -count=1`
- `GOTOOLCHAIN=local ./scripts/check.sh`
- `git diff --check`
No environmental limitation affected validation; checks ran on Linux/amd64.
## Stage 8 — Expose the public load-wide mode
1. Add root mode/constants/option fields and both conversions from decision 1.
Preserve zero-value behavior and root-owned boundaries.
2. Add external-package tests for ordinary/layered enabled loads, zero-mode
compatibility, invalid mode/rule before reading, and reuse of one Definition
with different modes. Use representative source-kind coverage.
3. Document implemented mode behavior in Go declarations, including local versus
effective interpretation. Evidence remains private until Stage 11; do not
claim it is publicly available yet.
Validate: facade version/layered/source and boundary tests; everyday check.
Done: both supported public load APIs expose the implemented modes.
Completion evidence: added the root-owned `SourceVersionMode` enum and constants
to ordinary and layered options, with Go documentation for zero-value behavior,
source-local declaration paths, destination-prefixed main protection, and the
two enabled policies. The facade converts explicitly through the internal
engine boundary, which keeps its mode type private and preserves unknown values
for option validation before acquisition. Added external-package coverage for
enabled ordinary and layered loads using byte, file, and mapping sources; zero
mode compatibility; invalid modes and missing rules before reader access; and
reusing one Definition with different modes. Source evidence remains private.
These checks passed with Go `go1.26.8 linux/amd64`:
- `GOTOOLCHAIN=local GOPROXY=off go test . -run 'SourceVersionMode|Version|Layered|SourceInfo|Destination|PublicFacadeBoundary|DependencyImportBoundary' -count=1`
- `GOTOOLCHAIN=local GOPROXY=off go test ./internal/engine -run 'SourceVersion|Version|Layered|Source|Destination|Import' -count=1`
- `GOTOOLCHAIN=local ./scripts/check.sh`
- `git diff --check`
No environmental limitation affected validation; checks ran on Linux/amd64.
## Stage 9 — Classify evidence at original paths and ancestors
1. Include declaration paths/ancestors in final classification candidates,
independently of effective/historical Metadata indexes. Do not add consumed
nodes to applications or explanation replay.
2. Apply schema-tag and predicate rules using original mounted semantic context,
including tags beneath dynamic containers and ancestor classification.
Preserve numeric-key/index intent before sequence shifts; consumed paths need
not survive final decoding. Reuse existing redaction mechanisms.
3. Test private evidence with disposable canaries: unclassified text, direct/
ancestor tags and predicates, original sequence shape versus shifted payload,
and null/absence. Keep raw evidence private throughout this stage.
Validate: source-version/sensitivity/rendering tests and everyday check. Done:
direct/ancestor classification is correct without making headers payload history.
Completion evidence: carried private source-version evidence from per-load state
into the resolved publication input, without adding consumed declarations to
applications or metadata. Final classification now evaluates each declaration's
original mounted semantic path and captured ancestors with the existing schema
tag and predicate rules, then uses the existing sensitivity result for rendering.
This covers dynamic map/slice paths, typed original sequence indices, and
null/absent evidence while leaving unclassified evidence and shifted neighboring
payload visible. Evidence remains private. These checks passed with Go
`go1.26.8 linux/amd64`:
- `GOTOOLCHAIN=local GOPROXY=off go test ./internal/engine -run 'SourceVersion|Secret|Sensitivity|History|Inheritance|Resolution' -count=1`
- `GOTOOLCHAIN=local ./scripts/check.sh`
- `git diff --check`
No environmental limitation affected validation; checks ran on Linux/amd64.
## Stage 10 — Preserve historical and dependency sensitivity
1. Connect evidence candidates to existing historical, inheritance, and recorded
dependency propagation. Later replacement or consumption must not erase
applicable classification; do not invent new propagation policy.
2. Consider evidence-only paths even when absent from final metadata. Keep
Resolution.Record's existing schema-path validity; do not invent dependencies
on headers solely to create tests.
3. Cover displaced historical classification and legitimate recorded dependencies,
with inheritance where applicable. Ensure neighboring unclassified evidence
remains visible and no synthetic effective/history/diff paths are introduced.
Validate: source-version/history/inheritance/resolution/sensitivity tests,
`go test -race ./internal/engine`, and everyday check. Done: established
sensitivity closure applies without polluting payload history.
Completion evidence: added original mounted evidence paths and ancestors to the
existing classification path set before schema/predicate evaluation and the
shared dependency closure. This lets existing recorded dependencies and
inheritance links carry evidence sensitivity into dependent and displaced
historical values, including evidence-only paths absent from final metadata;
Resolution.Record validation and dependency policy are unchanged. Added a
load-level regression using a valid recorded dependency on the credentials
container (not a synthetic dependency on the header), plus inherited displaced
history coverage. Neighboring values remain visible, and consumed headers create
no metadata, explanation, or Diff paths. These checks passed with Go
`go1.26.8 linux/amd64`:
- `GOTOOLCHAIN=local GOPROXY=off go test ./internal/engine -run 'SourceVersion|Secret|Sensitivity|History|Inheritance|Resolution' -count=1`
- `GOTOOLCHAIN=local GOPROXY=off go test -race ./internal/engine`
- `GOTOOLCHAIN=local ./scripts/check.sh`
- `git diff --check`
No environmental limitation affected validation; checks ran on Linux/amd64.
## Stage 11 — Publish detached sanitized source evidence — Done
1. Add private/public SourceVersionCheck and SourceInfo.VersionCheck records.
Extend both conversions and all clone paths, including callback source queries,
successful inspection, and existing LoadError source access.
2. Before final classification, expose conservative sanitized records; after
successful classification, freeze final sanitized evidence. Keep local paths,
physical locations, absence/null, and unchecked distinction. Raw evidence
stays in private state; no loading handle is retained.
3. Add facade/copy tests mutating nested paths/locations and records. Repeated
queries and independent loads remain unaffected. Delete sources after load
and verify evidence/resource metadata without reopening files.
4. Cover early acquisition and later resolver/validator failures plus callback
source queries with canaries. Default and synthetic CLI records stay nil.
Validate: record/conversion/source/version/ownership and public boundary tests,
root/engine race checks, and everyday check. Done: existing public source
inspection surfaces safely expose detached evidence.
Completion evidence: exposed root-owned and engine-private `SourceVersionCheck`
records through `SourceInfo`, added explicit conversions and deep copies for
paths and locations, and published conservative evidence to resolver queries
and failed loads while retaining final classified evidence in successful
inspection. Covered malformed and early import-acquisition failures, resolver
and validator errors, callback-time canaries, optional absence/null, default
and synthetic CLI records, source removal after load, repeated queries, and
independent mutation of callback, error, conversion, and inspection copies.
These checks passed with Go `go1.26.8 linux/amd64`:
- `GOTOOLCHAIN=local GOPROXY=off go test ./internal/engine -run 'SourceVersion|SourceInfo|LoadError|Resolution|Validation' -count=1`
- `GOTOOLCHAIN=local GOPROXY=off go test . -run 'SourceVersion|SourceInfo|LoadError|PublicFacadeBoundary|DependencyImportBoundary' -count=1`
- `GOTOOLCHAIN=local GOPROXY=off go test -race ./internal/engine .`
- `GOTOOLCHAIN=local ./scripts/check.sh`
- `git diff --check`
No environmental limitation affected validation; checks ran on Linux/amd64.
## Stage 12 — Audit lifecycle agreement and compatibility — Done
1. Map tests to every roadmap participation row/edge decision; add only missing
cross-boundary regressions with one primary owner per behavior.
2. Verify final values, zero/false/null presence, defaults, exact-path integer
conversion, resolution, secret lookup, and validation after consumption.
Hooks see one assembled configuration. Accepted declarations need not match.
3. Compare equal payloads with differing consumed headers: evidence changes but
effective rendering/history/Diff gain no consumed fields. Main history stays
normal. Cover empty ancestors and ordinary versions outside the header path.
4. Check physical contexts after placement/inheritance/precedence, existing
accounting boundaries, all supported document source kinds, and default
import/underlay/overlay/CLI/version compatibility.
5. Run `./scripts/check.sh` and
`GOTOOLCHAIN=local GOPROXY=off go test -race ./...`. Record actual evidence;
do not claim untested platforms or consumer adoption. Consumer checks are not
new release gates.
Done: all target behavior has public-boundary evidence and deterministic checks
pass, without extra contract restrictions or redundant coverage.
Completion evidence: mapped source-local lookup, malformed/overlapping headers,
consumption, roles, mounted protection, directives, and source participation to
the focused `source_version_test.go` suite; mapped classification and historical
propagation to `secret_classification_test.go`, `inheritance_secret_test.go`,
and the recorded-dependency regression; and retained existing destination,
accounting, resource-context, and default-mode suites as the owners for those
boundaries. Added a public-facade lifecycle regression that accepts distinct
main/underlay declarations while verifying exact large-integer decoding,
zero/false/null, defaults, one resolved configuration, secret lookup, validation,
evidence retention, and unchanged effective explanation/rendering/Diff. No
source-order promise or additional policy was introduced. These checks passed
with Go `go1.26.8 linux/amd64`:
- `GOTOOLCHAIN=local GOPROXY=off go test ./internal/engine -run 'SourceVersion|Version|Import|Underlay|Overlay|Destination|Secret|Sensitivity|History|Inheritance|Resolution' -count=1`
- `GOTOOLCHAIN=local GOPROXY=off go test . -run 'SourceVersion|Version|Layered|SourceInfo|Destination|Diff|PublicFacadeBoundary|DependencyImportBoundary' -count=1`
- `GOTOOLCHAIN=local ./scripts/check.sh`
- `GOTOOLCHAIN=local GOPROXY=off go test -race ./...`
- `git diff --check`
No environmental limitation affected validation; checks ran on Linux/amd64.
## Stage 13 — Add maintained adoption examples — Done
1. Add a small executable example showing if-present and required adoption with
one Definition.Version policy, accepted distinct declarations, main/import/
underlay or overlay sources, and safe evidence inspection.
2. Include a mounted input illustrating local lookup and effective main
protection. Keep ordinary release metadata at another path; no consumer-side
second load/merge, collection checker, or implicit schema migration.
3. Keep copyable assets in examples with tests for final payload and safe output.
Reuse existing examples for remaining source plumbing; do not print canaries
or promise callback ordering.
Validate: example/consumer tests, links, and everyday check. Done: tested examples
demonstrate the coherent shared policy with reduced consumer plumbing.
Completion evidence: added an executable layered consumer example and embedded
main/underlay fixtures. One source-local `Definition.Version` policy accepts the
distinct declarations in if-present and required modes; the mounted main maps
its local `version` to `/service/version`, while ordinary application release
metadata remains at `service.metadata.release`. The example inspects only
sanitized presence/path/redaction fields, and its test verifies final payloads
and effective-path override protection without printing declaration contents or
promising callback order. These checks passed with Go `go1.26.8 linux/amd64`:
- `GOTOOLCHAIN=local GOPROXY=off go test ./examples/consumers`
- `GOTOOLCHAIN=local GOPROXY=off go test . -run '^TestLocalDocumentationLinks$' -count=1`
- `GOTOOLCHAIN=local ./scripts/check.sh`
- `git diff --check`
No environmental limitation affected validation; checks ran on Linux/amd64.
## Stage 14 — Reconcile documentation and finish the audit
1. Update Go docs and docs/config.md as canonical owners for modes, participation,
local diagnostics, consumption, effective protection, mounted main, evidence
states/disclosure, and default behavior. Explain conservative callback-time
redaction and declared compatibility versus final validation. Link from
consumer guidance, development, and internal overview.
2. Update the architecture ownership summary where needed, linking to contract
and ADR rather than duplicating mechanics. Preserve previous accepted ADR
decisions. Reconcile obsolete design/consumption notes in the source request.
Repair completed relative-import roadmap/remediation links that meant the old
overwritten plan, using the Git-history pointer above.
3. Audit for accidental default checks, equality/non-null rules, path grammar,
fragment decoding, ordering promises, extra uniqueness/caps, unsafe evidence
queries, and new release gates. Match every roadmap outcome to test evidence.
4. Mark plan/roadmap complete only after all stages pass. Run everyday checks,
full race suite after code corrections, and final link/whitespace checks.
Commit the validated unit; leave a clean workspace. Do not publish or retire
completed records in this stage.
Done: implementation, coverage, documentation, and approved scope agree. No
product questions remain open under the roadmap and concrete decisions above.
Completion evidence: updated the public Go comments, configuration contract,
consumer guidance, development guide, and component ownership summary for the
implemented behavior. Reconciled the original source-version request with later
accepted decisions, marked the source-version roadmap implemented, and repaired
relative-import/remediation references to the overwritten plan using the
confirmed `d9f08ff:docs/roadmap/implementation.md` history pointer. The scope
audit found no default-mode expansion, equality/non-null rule, extra path
restriction, fragment decoding, callback-order promise, new uniqueness/cap,
unsafe evidence query, or release gate. Reviewed behavioral coverage in
`internal/engine/source_version_test.go`,
`facade_source_version_mode_test.go`,
`internal/engine/secret_classification_test.go`,
`internal/engine/inheritance_secret_test.go`, and
`examples/consumers/layered_test.go`. These checks passed with Go
`go1.26.8 linux/amd64`:
- `GOTOOLCHAIN=local ./scripts/check.sh`
- `GOTOOLCHAIN=local GOPROXY=off go test . -run '^TestLocalDocumentationLinks$' -count=1`
- `git diff --check`
No runtime code changed in this documentation unit, so the race suite was not
rerun. No environmental limitation affected validation; checks ran on
Linux/amd64 only. Publication and release preparation remain outside this work.
## Post-implementation remediation evidence
The subsequent review found coverage gaps beyond the original stage validation;
the [remediation record](remediation.md#shared-source-version-review-2026-10-15)
owns their status. Stage 9–12 evidence now also includes
`TestSourceVersionEvidenceClassificationKeepsCollidingShapes` and
`TestFacadeSourceVersionEvidenceRedactsCollidingShapes`. Both failed before the
shape-collision fix and pass afterward. Classification preserves original
mapping-key and sequence-index candidates independently without erasing payload
classification. Focused source-version/sensitivity/history tests,
`GOTOOLCHAIN=local ./scripts/check.sh`, and
`GOTOOLCHAIN=local GOPROXY=off go test -race ./...` passed on
`go1.26.9 linux/amd64`. Existing historical/dependency propagation and neighboring
public-value coverage remain green; no new sensitivity policy was introduced.
Additional remediation regressions protect import opt-in
(`TestFacadeSourceVersionModesPreserveImportOptIn`), complete absence paths
(`TestSourceVersionAbsentPathKeepsUnresolvedSegmentIntent` and
`TestFacadeSourceVersionAbsenceKeepsCompleteEvidencePath`), and optional missing
headers below controls
(`TestFacadeSourceVersionMissingHeaderDoesNotOverlapControl`). Each failed
before its production correction and passes afterward. All four independent
review probes, the final everyday check, and the full race suite now pass on
`go1.26.9 linux/amd64`. The remediation record contains completion evidence;
the agreed source-version policy and release gates are unchanged.

View File

@@ -1,174 +0,0 @@
# Configuration Input Destinations Roadmap
Status: implemented. This roadmap records the approved behavior and rationale
for the [PromptKit feature request](promptkit-feature-request.md). The
completed implementation plan is preserved in Git history at
`315f464:docs/roadmap/implementation.md`, including its audit and remediation
evidence; the [configuration contract](../config.md#input-destinations)
owns current behavior.
## Targeted end state
A consumer can assign a mapping destination to a configuration input and perform
one ordinary `LoadLayered` call. The input's application content contributes
beneath that destination while retaining normal composition, precedence,
inheritance, typed loading, diagnostics, and inspection. PromptKit can assemble
collections without parsing and rewriting sources, merging values, or rebuilding
provenance itself.
For example, a fragment defining `summarize.alerts` can contribute beneath
`collections`, `weatherreporter`, `prompts`. An independent source with the same
local key can contribute beneath another collection without conflict. Collection
and prompt names remain ordinary application keys, not ConfigKit namespaces.
The public surface adds an optional `Destination Path` field on `Input`, not a
new loading entry point or an operation on completed results. Content-bearing
main inputs and underlay inputs support
the same destination model; root placement remains the default.
The following behavior defines the target:
- Omitted and empty destinations retain root placement. Destinations consist of
literal mapping keys, including dots, slashes, and numeric-looking text.
Explicit sequence-index destinations are outside the requested capability.
- A valid destination on `NoSource` contributes nothing. An actual empty
application mapping materializes its destination mappings as explicit input,
matching authored wrappers, without clearing lower-tier children.
- Existing structural reservations apply to introduced mappings. Destinations
that materialize `configkit` or a mapping-valued `extends` contrary to existing
contracts fail through the structured loading-error boundary. Keys merely
containing those words remain ordinary keys.
- Host files, native filesystem files, bytes, readers, and mapping sources all
support placement under their existing acquisition and ownership contracts.
Filesystem context, import opt-in, and precedence remain independent choices.
- Mounted contributions have the same application values and conflict behavior
as explicitly wrapped contributions, with accurate original source locations.
Disjoint mappings combine; overlapping non-mapping values within a group
conflict even when equal. A later tier cannot repair a strict-group conflict.
- Different destinations can coexist in one group, and the same destination can
occur in different tiers. Ancestor/descendant destinations follow ordinary
composition and overlay rules without namespace uniqueness restrictions.
Later tiers recursively overlay mappings and replace other values. Empty
mappings do not clear lower values; sequences remain atomic.
- A prompt's ordinary `version` override changes that field and retains omitted
lower fields. It neither identifies a merge record nor replaces a whole prompt.
- Source-root `configkit.imports` remains structural. Imported application data
inherits the input destination transitively when its import omits
`destination` or supplies `[]`; the accepted [relative-import extension](../adr/0012-relative-import-destinations.md)
appends any declared segments. Import paths still resolve from their declaring
source; containment, optionality, cycles, and repeated-source rules remain
applicable. Placement does not change physical source identity.
- Sibling inheritance runs after assembly in the effective destination scope.
Peer fragments can supply a child and its sibling parent. Qualified PromptKit
references do not acquire cross-collection inheritance meaning.
- Version ownership is enforced at effective application paths. A collection's
mounted `version` is ordinary data, while underlay/import contributions to the
actual protected path, or replacements of its ancestors, remain invalid.
There is one main-owned version check and no blanket nested numeric conversion.
- Defaults, decoding, resolution, secret lookup, and validation run once on the
assembled application, not independently on fragments. Callbacks and value
diagnostics use effective paths. Failures use structured load errors and yield
no usable partial result.
- Presence, provenance, history, explanations, effective rendering, and diffs
agree with the final values. Sensitivity is evaluated at effective application
paths under the existing historical and dependency rules.
- Existing source records expose placement, including inherited import
placement. Introduced ancestor mappings retain the contributing source's
identity; any root-location fallback identifies that document, not an authored
wrapper. Actual content retains its real source coordinates.
- Original source locations and passive resource contexts survive placement,
overrides, and inheritance. `Result.SourceContext` and
`Resolution.SourceContext` still identify the declaring filesystem and base
directory for a field such as `content_file: ./summary.md`.
- Destinations and the structure they introduce participate in existing load
limits; placement cannot bypass depth or node accounting. Source ownership,
complete-on-return loading, and detached result behavior remain intact.
Import-destination syntax, independent collection specification headers, and
cross-collection inheritance are separate possible follow-ups. This work does
not add directory discovery, collection installation, remote acquisition,
namespace ownership enforcement, dependency solving, live reload, filename-based
identity, arbitrary transformations, or new merge strategies. PromptKit owns its
collection inventory, qualified IDs, and execution semantics.
## Implementation baseline (v0.7.1)
The request reviewed v0.7.1. At that baseline, the relevant boundaries were:
- [Input](../../layered.go) holds a source, import opt-in, and filesystem context,
but no destination. Root types delegate to the engine through the existing
[facade boundary](../internal/overview.md).
- [Layered loading](../config.md#layered-loading) already provides strict groups,
ordered precedence, shared acquisition budgets, and one final lifecycle.
[Underlay acquisition](../../internal/engine/load_request.go) gathers source
applications and recomposes them into their strict group.
- [Input acquisition](../../internal/engine/load_input.go) and
[import traversal](../../internal/engine/imports.go) process source-root
directives and source identity before final assembly. Imported version claims
are checked during traversal; underlay claims are checked before group
composition. These currently operate without a destination.
- [Version claim checks](../../internal/engine/directives.go) inspect a document
using the configured application path. Wrapping only an already acquired
result would leave early fragment checks and diagnostic paths incorrect.
- [Sibling inheritance](../adr/0009-sibling-inheritance.md) already runs after
source precedence and preserves source-to-destination derivation. Placement
must feed that existing mechanism rather than resolve fragments independently.
- Existing source records identify acquisition and precedence but do not expose
input placement. Existing history retains source applications, so moving only
the final tree would be insufficient for coherent inspection.
The architectural fit is an extension of input assembly, not a parser extension
or a second configuration model. The
[architecture policy](../policy/architecture.md) supplies the ownership and
consistency constraints; [deferred scope](deferred.md) continues to govern the
unrequested features above.
## Selected Design Decisions
### Main-input destinations and absent inputs
Allow the same destination model for content-bearing main inputs and underlays,
with `VersionRule.Path` remaining an effective application path and any
specification declaration still owned by main. The checker retains its existing
handling of an absent declaration and its existing lifecycle phase. `NoSource`
contributes nothing even with a valid destination. This keeps `Input` coherent
across its roles and preserves absence without per-fragment version checks.
### Public placement inspection and synthetic ancestors
Expose effective placement as `SourceInfo.Destination Path` on existing source
records, including inherited import placement.
Use original source identity for introduced ancestor mappings, documenting that
any root-location fallback identifies the contributing document rather than a
physically authored wrapper. Actual content keeps its real coordinates. This
allows consumers to connect placement to sources without a new query system.
Preserve that relationship through inherited fields, composite origins, and the
same source selected in separate tiers. Placement is per source application;
consumers can inspect it without inferring it from final paths.
### Reserved structural keys in destinations
Use existing `Path` conventions for literal keys and parsed
numeric tokens, reject explicit index segments, and preserve current structural
reservations in the effective tree. Reject destination segments that would
materialize `configkit` or a mapping-valued `extends` contrary to those contracts,
with a clear structured destination error. Ordinary keys containing those words,
dots, or slashes remain ordinary keys. Source-root directives are processed before
placement and are not rejected simply because their application body is mounted.
Mounted and authored wrappers therefore retain the same structural meaning.
Destination diagnostics identify placement as the cause of invalid introduced
structure, using existing error categories where suitable.
### Empty mappings and presence of introduced containers
Mounting an actual empty application mapping materializes its
destination mappings as explicit contributions, matching authored wrappers.
This preserves the request's equivalence principle and lets consumers inspect
an intentionally empty collection. Such mappings still do not clear lower-tier
children. An absent source contributes nothing.
This applies equally to empty imported application bodies and inputs containing
only import directives. Presence follows the contribution's meaning rather than
whether an implementation happened to allocate wrapper nodes.

View File

@@ -1,500 +0,0 @@
# PromptKit Feature Request: Mounted Collections And Specification Headers
Status:
- Input destinations are implemented on the ConfigKit development branch
(placement baseline reviewed at `315f464`).
- Relative import destinations have been accepted and implemented; the
[roadmap](relative-import-destinations.md) records their completed delivery.
- Source specification headers have been partially accepted in the shared
application-version-policy form described below. This does not accept
PromptKit's original independent collection-header semantics. Design and
implementation of this addition remain separate from relative import work.
## Requested Capability
Extend configuration loading with relative import destinations and opt-in
checks that contributing sources declare a version accepted by the consumer's
single configuration specification policy. Preserve the implemented capability
to select a destination mapping path for each configuration input. The input's
application content contributes
beneath that path while retaining ConfigKit's normal composition, precedence,
inheritance, validation, diagnostics, and inspection behavior.
PromptKit needs this capability for its proposal 3, **Collections With Mounted
Configuration Fragments**, recorded in PromptKit's
`docs/roadmap/configuration-collections-mounted-fragments.md`. This request
describes the public API needs and observable acceptance criteria. It does not
prescribe ConfigKit's internal design or an implementation plan. The disposition
and revised source-version scope below supersede conflicting parts of PromptKit's
original proposal.
The original request targeted ConfigKit v0.7.1. The development branch now
supplies `Input.Destination` and `SourceInfo.Destination`, as documented in the
[input destination contract](../config.md#input-destinations). The placement
requirements below remain the baseline for the two additions. Current released
behavior and development-branch behavior must not be confused with the newly
proposed header and import syntax.
## Consumer Problem
PromptKit applications assemble independently maintained collections of prompts,
inference profiles, and backends. Collections can contain identical local names,
such as `summarize.alerts`, without identifying the same resource. PromptKit wants
an effective configuration organized by collection, while letting authors omit
repeated collection and resource-container wrappers from individual files.
A file selected from a collection's `prompts` directory would contain:
```yaml
summarize.alerts:
version: "1"
default_profile: provider/brief
messages:
- role: user
content: Summarize the weather alerts.
output:
format: text
validation_mode: none
```
Given destination `[collections, weatherreporter, prompts]`, its contribution
would appear in the final application configuration as:
```yaml
collections:
weatherreporter:
prompts:
summarize.alerts:
version: "1"
default_profile: provider/brief
messages:
- role: user
content: Summarize the weather alerts.
output:
format: text
validation_mode: none
```
Another collection can contribute `summarize.alerts` beneath
`[collections, homewatch, prompts]` without conflicting. After final handoff,
PromptKit interprets these as `weatherreporter/summarize.alerts` and
`homewatch/summarize.alerts`. The profile reference is illustrative and must be
supplied separately by the application configuration.
PromptKit will support one installed definition per prompt. Its optional flat
`version` is ordinary domain metadata, not a merge identity. This request does
not need identified-record sequences, sequence-root documents, or a custom
merge policy.
Authors could write the complete wrappers today, but that repeats assembly
details in every fragment. Consumer-side parsing and wrapping would put generic
source processing back into PromptKit and complicate source attribution.
PromptKit instead wants to select sources and destinations, then receive one
final configuration through ConfigKit's supported loading lifecycle.
## Implemented Placement API And Required Baseline
### Per-Input Destination
The development branch provides `Destination Path` on each `Input` passed to
`LoadLayered`, matching the original request. The following partial consumer
example uses that implemented field, which was not available in v0.7.1:
```go
promptInput := configkit.Input{
Source: configkit.File("/usr/local/share/collections/weatherreporter/prompts/alerts.yml"),
Destination: configkit.Keys("collections", "weatherreporter", "prompts"),
}
peers := configkit.Underlay{
Inputs: []configkit.Input{promptInput},
}
```
The consumer would include `peers` in its existing `LayeredOptions` and invoke
`LoadLayered` with its normal root main input and application `Definition`.
This is a partial API sketch, not an executable example or a proposed new
loading entry point. A single public layered load is sufficient for PromptKit;
it does not require a separate mounting operation on completed results.
The API must let the consumer:
- Express a destination relative to the application configuration root using
literal mapping-key segments. Dots, slashes, and numeric-looking text inside
a key are not implicit path separators or array indexes. PromptKit's namespace
grammar remains its own domain rule.
- Omit the destination to retain existing root placement. An explicitly empty
destination should have the same meaning. Mapping destinations are sufficient;
sequence-index destinations are not needed by this request.
- Assign different destinations to inputs in the same strict composition group,
and assign the same destination to inputs in separate precedence groups.
- Use the destination with existing content-bearing source kinds: host files,
native filesystem files, bytes, readers, and mapping sources, subject to their
existing acquisition and ownership contracts.
- Retain the existing import opt-in and filesystem context independently of the
destination. A configuration destination is not a filesystem root or base
directory.
PromptKit can continue to enumerate an explicitly selected directory and attach
the same destination to its returned inputs. ConfigKit does not need a new
directory scanner, namespace registry, filename convention, or PromptKit schema.
The namespace comes from the application's assembly declaration, not from an
implicit interpretation of a directory basename.
### Representative Assembly
These are application choices, not ConfigKit-specific reserved paths:
| Selected inputs | Destination key segments |
| --- | --- |
| Application main document | Root |
| `weatherreporter/collection.yml` | `collections`, `weatherreporter` |
| YAML files under `weatherreporter/prompts` | `collections`, `weatherreporter`, `prompts` |
| YAML files under `homewatch/prompts` | `collections`, `homewatch`, `prompts` |
| YAML files under `provider/profiles` | `collections`, `provider`, `profiles` |
| YAML files under `provider/backends` | `collections`, `provider`, `backends` |
Peer collection contributions belong to a single strict underlay group when the
application wants conflict detection among them. Intentional operator overrides
use later groups or main. Destination selection must not establish precedence
or make source traversal order authorize replacement.
## Required Observable Behavior
### Composition And Precedence
Mounted and explicitly wrapped contributions must have equivalent application
values and conflict behavior at their effective destinations. Actual source
locations naturally differ and must remain accurate.
Retain the [strict composition](../config.md#strict-document-composition) and
[layering](../config.md#layered-loading) rules:
- Disjoint mapping entries at the same destination combine.
- Overlapping non-mapping values within a strict group conflict, including equal
scalars, nulls, and sequences. Different destinations do not collide merely
because the source documents share a local key.
- Later precedence layers recursively override mappings and replace other
values. A strict conflict cannot be repaired by an upper layer.
- Empty mappings do not clear lower definitions; ordinary arrays remain atomic.
In particular, `messages` does not acquire sequence-merging behavior.
- Overlapping destination paths and contributions at their ancestors follow
those same rules. No namespace-level uniqueness restriction is requested.
Changing only a prompt's `version` in a later layer changes that field while
retaining omitted lower-layer fields. ConfigKit must not treat a domain version
label as a request to replace an entire definition.
### Imports And Existing Structural Directives
An input with imports enabled must be able to use its existing source-root
`configkit.imports` directive while contributing application data at a nested
destination. The directive remains structural configuration, not an application
field below that destination.
When an import omits `destination` or supplies an empty list, it inherits its
importing document's effective destination, including transitively. An explicit
sequence appends to that destination. Import paths continue to resolve relative
to their declaring source's filesystem context. A mounted document must not send
its imported application fields back to the configuration root accidentally.
Existing opt-in, containment, missing-input, cycle, and repeated-source behavior
must remain applicable. A different destination is not a request to bypass the
current rejection of a repeated physical source within a strict group.
Sibling `extends` must resolve against mappings at their effective destinations.
For example, two independently supplied profile fragments mounted beneath the
same collection's `profiles` mapping can establish `extends: brief` once they
compose. Inheritance remains ConfigKit-owned; PromptKit does not perform another
relationship-resolution pass.
### Protected Specification Paths
The distinction between source-local field names and effective application
paths is essential. PromptKit's main document will declare `version: 1` at the
configuration root. A separate collection metadata file can contain:
```yaml
version: "2.4.0"
```
Mounted at `[collections, weatherreporter]`, that field is an ordinary collection
release label. It must not be rejected as an attempt to contribute the protected
root specification field. The same applies to a prompt's nested optional
version. This describes the existing placement baseline, with no new source
checks enabled. The partially accepted extension will reuse the application's
[VersionRule](../config.md#application-schema-versions) for source declarations;
header lookup and its interaction with ordinary version payload remain design
questions, not a guarantee that every source-local `version` stays unchecked.
Conversely, placement must not permit an underlay or imported fragment to claim
or replace the actual protected specification path or an ancestor that would
replace it. The public contract must define protection in terms of effective
application paths, rather than reserving the spelling `version` in every source.
Protection must remain valid with imports, inheritance, overlays, and explicit
CLI overrides.
Retain existing behavior when the new source checks are not enabled. The
accepted direction extends the same application version policy across source
files, rather than assigning separate policies to collections. Its interaction
with protected-path ownership and declaration retention must be designed
explicitly; it does not implicitly authorize version-field contributions.
PromptKit does not need main mounted away from the root or integer-to-string
conversion for every nested ordinary `version` field.
### Typed Loading, Diagnostics, And Inspection
The destination must be visible consistently through the ordinary public
loading and inspection surface:
- Final typed configuration and application callbacks use effective destination
paths. Defaults, resolution, and validation retain the existing single-load
lifecycle; fragments are not independently validated as whole applications.
- Metadata and presence queries distinguish omission, explicit null, and other
explicit values at the mounted paths.
- Diagnostics identify the effective conflicting or invalid path and the real
contributing source locations. Consumers can determine which source and
placement produced a value without recreating that relationship themselves.
- Effective rendering, explanations, history, and configuration comparison agree
with the final configuration. Existing sensitivity and disclosure rules apply
to mounted fields, including application path-based secret classification.
- Unsupported destination forms and destination-related loading failures use
the normal structured error boundary. Exact new diagnostic codes, if needed,
are for ConfigKit to define.
PromptKit does not require a separate mount registry or new inspection facade.
Existing inspection APIs are sufficient if they expose accurate effective paths,
source attribution, and placement information. ConfigKit should document how
source-local locations relate to effective paths.
### Source-Relative Resources And Lifecycle
For a mounted field such as `content_file: ./summary.md`, obtaining its effective
origin and calling `Result.SourceContext` or `Resolution.SourceContext` must
still identify the declaring source's filesystem and base directory. The same
holds when that field survives an override or is inherited into another mapping.
Its configuration destination must not change the meaning of the relative file
reference.
Retain existing source ownership, load completion, result lifetime, containment,
and resource-access behavior. Destinations must participate in documented load
limits without creating a way to bypass them; this request does not prescribe
new limit values or their accounting machinery.
## Partially Accepted Addition: Shared Source Specification Checks
Status: the shared-policy portion below is implemented. Current behavior is
defined by the [configuration contract](../config.md#shared-source-version-checks),
and the accepted decision and implementation evidence are in
[ADR 0013](../adr/0013-shared-source-version-checks.md) and the
[source-version roadmap](source-version-checks.md).
### Accepted Scope
A ConfigKit consumer defines one configuration specification, which may evolve
through versions. `Definition.Version` identifies the declaration path and the
consumer's checker determines which versions are acceptable. The accepted
extension lets the consumer apply that same policy across the source files
contributing to a configuration, rather than checking only the main declaration.
It does not introduce independent collection or subtree specification policies.
The consumer can opt in with increasing strictness:
1. **Current behavior:** retain existing loading and version-checking behavior,
including existing protected-path restrictions. No new source-header check.
2. **Check declarations when present:** a source that declares a version must
satisfy `Definition.Version`; omission is allowed.
3. **Require declarations:** every participating source must declare a version
that satisfies `Definition.Version`; omission is an error.
Acceptance is determined by the existing consumer-supplied checker, not by a
new ConfigKit-owned expected string or automatic equality with the main file's
declaration. A consumer may accept several compatible versions. A declaration
rejected under the selected mode fails the attempted load through structured
errors, with no usable partial result; a higher-precedence payload cannot turn
an incompatible source declaration into an acceptable one.
This establishes that checked sources declare an accepted specification version.
It does not prove that every fragment conforms to its declaration. Existing
final decoding and application validation still determine the validity of the
assembled configuration; fragments are not independently loaded as complete
applications.
### Difference From PromptKit's Original Proposal
PromptKit originally requested explicitly selected collection-entry rules,
separate from `Definition.Version`, with ordinary imports exempt from independent
checks. It also recommended consuming accepted entry headers and exposing them
as source evidence while leaving imported release metadata untouched.
The independent collection-entry policies and exemption for ordinary imports
were not accepted. The accepted direction instead uses one shared
`Definition.Version` policy and optional or required declarations across
participating sources. Separately versioned subsets or collections remain
outside this addition's scope. Header consumption and sanitized source evidence,
which were suggestions in the original proposal, were subsequently accepted for
this shared-policy design and are implemented as described by the linked
contract.
### Design Questions Resolved by the Accepted Roadmap
The following questions were open in the original request and are resolved by
the accepted roadmap and ADR 0013:
- How the configured declaration path is interpreted for individual sources,
particularly before destination placement.
- Which source kinds and roles participate, including direct underlay inputs,
transitive imports, and other contributions.
- Whether checked declarations are consumed or retained, how that interacts
with composition and main-owned version protection, and what inspection
evidence is exposed.
- How null and malformed declarations behave in each mode, preserving the
distinction between absence and a present declaration and considering the
existing string/decimal-integer version vocabulary.
- The opt-in API, callback lifecycle, and source-local diagnostic attribution.
In particular, an imported `version: "2.4.0"` at the configured source-local
header path is checked and consumed in an enabled mode. Consumers that need an
ordinary release label retain it at a different path. No automatic field
consumption applies in the default mode, and no new coercion or per-collection
checker is part of the implemented design.
## Requested Addition: Relative Import Destinations
Disposition: accepted and implemented under the completed
[relative import destinations roadmap](relative-import-destinations.md).
The behavior below records the accepted capability; exact syntax and completion
status follow that work, rather than implying a released feature.
Extend the public YAML import directive so a `path` or `glob` import can specify
a relative destination as literal mapping-key segments. The roadmap selects
`destination` as a sequence of strings, for example `destination: [prompts]`.
The public behavior should be:
- The importing document has an effective destination established by its input
or its own import. An explicit import destination appends key segments to that
destination. Omission or an empty list retains the importing destination.
- Every file matched by a glob receives the same declared relative destination.
Transitive imports are relative to their immediate importing document's
resulting destination, not always to the original entry destination.
- Filesystem paths still resolve relative to the declaring file, with existing
containment rules. Logical placement and physical file resolution are
independent; there is no root-reset or parent-traversal meaning in destination
key segments. Literal keys follow the same rules as `Input.Destination`.
- Imports retain their existing strict-composition tier. Import order and
destination declarations do not create precedence or permit replacement.
- Existing optionality, repeated-source, cycle, source-kind, and ownership rules
remain intact. A destination does not authorize importing one physical file
twice in a group. Existing source-root directive and reserved-key rules apply.
- `SourceInfo.Destination` reports each import's resulting effective destination.
Effective paths, original locations, resource contexts, protected version
paths, sensitivity, and error behavior retain the placement baseline above.
- Invalid directive shapes fail through the normal structured error boundary;
destination fields must not be silently ignored.
This is declarative access to the existing placement capability. It does not
request recursive glob syntax, implicit directory scanning, namespace inference,
or a new composition strategy.
## Relative Import Collection Entry Example
An entry document selected by the application with destination
`[collections, weatherreporter]` could describe its layout as follows. This
example illustrates relative imports only and assumes no new source checks:
```yaml
configkit:
imports:
- path: metadata.yml
- glob: prompts/*.yml
destination: [prompts]
- glob: profiles/*.yml
destination: [profiles]
- glob: backends/*.yml
destination: [backends]
```
This is proposed syntax, not currently executable configuration. Each required
glob assumes matching files; collections can omit unused categories or use
existing optional-import behavior. The application still selects and provisions
the collection and assigns its namespace. ConfigKit does not discover it.
With current source-version behavior, `metadata.yml` can contain
`version: "2.4.0"` as an ordinary mounted collection release label. Prompt
fragments retain local mapping keys and optional prompt versions, and contribute
beneath the collection's `prompts` mapping. The main application can retain its
own version declaration. Enabling the proposed shared source checks may require
changes to these files once declaration lookup and retention are defined.
Publishers can describe collection layout while applications select collections
and destinations. The later shared-version feature will add opt-in declaration
checking; it does not establish package authenticity or release integrity.
## Deferred And Excluded Work
**Identity-based list composition.** Keep this deferred. Proposal 3's local
mapping keys already identify each installed resource; the two requested
additions do not require a different representation or sequence merge policy.
**Cross-collection inheritance.** Nested collection profiles are not immediate
siblings across collections. Current sibling inheritance remains sufficient for
the initial proposal, with authored definitions flattened where necessary.
Generic cross-path inheritance could be considered separately if consumer needs
justify revisiting the [deferred scope](deferred.md). Mounting must not silently
reinterpret `extends: provider/brief` as a cross-collection reference.
PromptKit also does not request collection installation, automatic directory
discovery, remote fetching, namespace ownership enforcement, dependency solving,
live reload, filename-derived identity, or arbitrary document transformations.
Its qualified resource IDs, optional prompt version semantics, collection
inventory, and execution APIs remain PromptKit responsibilities.
## Public Acceptance Scenarios
Evaluate the implemented placement baseline and accepted relative-import
extension through supported public loading and inspection APIs. Source-version
rows below express the partially accepted direction; their unresolved details
must be settled before they become implementation acceptance tests.
| Scenario | Expected consumer-visible result |
| --- | --- |
| Destination omitted or empty | Existing root-placement behavior |
| Same fragment shape mounted under two distinct collection keys from independent sources | Two separate effective resources |
| Disjoint inputs at the same destination in one group | Mapping contributions combine |
| Equal or unequal overlapping scalar fields in one group | Structured composition conflict with both source locations |
| Same destination across deliberate precedence tiers | Existing recursive overlay behavior |
| Version-only prompt override | Label changes; omitted prompt body fields survive |
| Nested mounted import without a destination directive | Imported fields contribute at the inherited destination |
| Local profile inheritance across mounted peer fragments | Parent resolves at the effective sibling scope |
| Collection metadata containing `version` mounted below root, with new checks disabled | Existing ordinary nested-field behavior |
| Mounted/imported contribution actually targeting the protected version path | Rejected under version ownership rules |
| Mounted field using source-relative content, including inheritance | Original declaring resource context remains available |
| Mounted explicit null and omitted field | Distinct effective presence information |
| Mounted validation error or secret field | Effective paths, physical locations, and existing disclosure rules agree |
| File, native filesystem, byte, reader, or mapping input with equivalent content | Equivalent application values, subject to existing source contracts |
| Unsupported destination or failed load | Normal structured failure, no usable partial result |
| Source-version checks disabled | Existing version-checking and protected-path behavior |
| Check-when-present mode, declaration absent | Omission is allowed |
| Check-when-present mode, declaration present | Must satisfy the shared `Definition.Version` policy |
| Required mode, declaration absent | Structured load failure |
| Required mode, declaration present | Must satisfy the shared `Definition.Version` policy |
| Different declarations both accepted by the consumer checker | No additional exact-equality requirement |
| Rejected source declaration with payload superseded by a higher layer | Rejection still fails the load |
| Relative import destination omitted or empty | Existing inherited-destination behavior |
| Entry at `[collections, weather]`, import at `[prompts]`, nested import at `[extras]` | Nested contribution appears beneath `[collections, weather, prompts, extras]` |
| Glob import with a relative destination | Every matched contribution uses that destination and composes strictly |
| Relative import destination with source-relative resource | Effective configuration path changes; resource base remains the declaring source |
| Load without new source checks or relative import directives | Existing loading and main VersionRule semantics remain unchanged |
The relative-import outcome is a consumer that selects entry sources and
destinations, performs one ordinary layered load, and receives final typed
configuration with reliable diagnostics and inspection. Entry documents describe
their own relative import placements without consumer-side source rewriting,
generic merging, or provenance reconstruction.
The separately scoped, partially accepted source-version extension adds optional
or required declaration checking through the consumer's single
`Definition.Version` policy. It does not require or authorize independently
versioned collections.

View File

@@ -1,133 +0,0 @@
# Relative Import Destinations Roadmap
Status: complete. The implementation plan and its evidence were replaced by the
shared source-version plan and remain in Git history at
`d9f08ff:docs/roadmap/implementation.md`.
This roadmap addresses only the relative-import-destination addition in
the [PromptKit request](promptkit-feature-request.md#requested-addition-relative-import-destinations).
Source-version checks were outside this roadmap's scope and are now implemented
separately in the [shared source-version roadmap](source-version-checks.md).
## Targeted end state
Authors can assign a relative mapping destination to a `path` or `glob` import.
ConfigKit appends its literal key segments to the immediate importing document's
effective destination, then processes the imported application content through
the existing placement and loading lifecycle. Applications continue to select
entry sources, input destinations, filesystem contexts, and precedence tiers.
Selected YAML syntax:
```yaml
configkit:
imports:
- path: metadata.yml
- glob: prompts/*.yml
destination: [prompts]
- glob: profiles/*.yml
destination: [profiles]
```
If the application places this entry at `[collections, weather]`, its metadata
import contributes at that same path, and each prompt file contributes at
`[collections, weather, prompts]`. An import declared inside a prompt file with
`destination: [extras]` contributes at
`[collections, weather, prompts, extras]`. A subsequent sibling import from the
entry is still relative to `[collections, weather]`.
This example does not declare or consume a source specification header. A
source-local `version` remains ordinary payload unless governed by the existing
main-owned version contract. The combined header example in the broader request
is not an acceptance requirement for this roadmap.
The feature must preserve these requested and existing behaviors:
- Omission and an empty destination list retain the importing document's
destination. Each glob match receives the same relative placement. Transitive
imports accumulate destinations from their immediate parents.
- An explicit `destination` is a sequence of strings. Null, scalar values, and
non-string members are invalid directive shapes; values are not coerced.
For a numeric-looking key, use a string such as `destination: ["0"]`.
- Segments are mapping keys under the existing `Input.Destination` rules. Dots,
slashes, and numeric-looking text within a key are literal. There is no
filesystem-style normalization, parent traversal, or application-root reset.
Do not add a collection-name grammar or reject otherwise supported literal
keys merely because their spelling resembles a filesystem path.
- Imports retain their strict-composition tier. Placement and traversal order
do not confer precedence. Later tiers retain existing recursive overlay
behavior; sequences remain atomic and overlapping destinations use existing
composition rules.
- Filesystem paths resolve from their declaring source independently of logical
placement. Import opt-in, optionality, glob behavior, containment, cycle and
repeated-source rules, source kinds, and ownership remain unchanged. A new
destination does not allow a physical source to repeat within a strict group.
- Source-root directives are interpreted before application placement. Existing
reserved-key rules continue to apply to destinations. Invalid new directive
shapes produce normal structured failures rather than being ignored.
- `SourceInfo.Destination` reports each acquired source's resulting effective
destination, including nested imports and sources recorded on failed loads
to the extent supported by existing error reporting.
- Composition and value diagnostics use effective application paths and real
source locations. Directive errors identify their source-local declaration.
Placement does not manufacture authored coordinates or alter resource bases.
- Main-owned version protection applies at the resulting effective paths.
Ordinary nested version fields stay ordinary payload. Inheritance resolves
in the assembled sibling scope, then defaults, decoding, resolution, secret
lookup, and validation follow the existing single-load lifecycle.
- Presence, provenance, history, explanations, rendering, diffs, and sensitivity
agree with the placed contributions. Source-relative resource contexts survive
inheritance and precedence under their existing contracts.
- Accumulated placement and introduced wrappers participate in existing depth
and node accounting. This work does not select new limit values or promise
new performance bounds. An actually imported empty mapping retains existing
placement behavior; an optional import that finds no source creates no payload.
The syntax is available wherever existing explicit imports are supported,
including ordinary root loading and layered main/underlay inputs. It does not
require a new Go loading entry point, mount registry, or consumer-side rewriting.
Source header policies and consumption, cross-collection inheritance, sequence
merging, root-reset syntax, automatic discovery, recursive glob syntax, and
collection management are outside this delivery. This scope statement does not
establish new permanent exclusions beyond [existing deferred scope](deferred.md).
## Pre-implementation baseline
Reviewed baseline: `315f464`, with input destinations implemented. At that
revision, the
[configuration contract](../config.md#input-destinations) and
[ADR 0011](../adr/0011-input-destinations.md) described that feature.
- [Directive parsing](../../internal/engine/directives.go) accepted `path` or
`glob` plus `optional`; it rejects unrecognized fields. Import declarations
carried no relative destination.
- [Import traversal](../../internal/engine/imports.go) held one destination for
an input's entire import tree. Both document placement and imported source
records use it. Per-document effective placement is therefore required.
- [Input acquisition](../../internal/engine/load_input.go) assigned
the input destination to all returned imported source records. That uniform
assignment must no longer overwrite distinct import destinations.
- [Destination helpers](../../internal/engine/destination.go) already validate
literal mapping paths, place document bodies, and check introduced depth and
node costs. Composition, version protection, and retained source applications
already operate on placed content.
- The root facade already exposes `SourceInfo.Destination`; the new syntax can
use that inspection surface without a separate placement API.
The change extends traversal and directive parsing. It should reuse existing
placement and assembly rather than introduce a second merge or validation model.
## Selected Design Decision
### YAML destination representation
Use `destination` as a sequence of strings, for example
`destination: [prompts, "0"]`. Omission and `[]` inherit placement. Null, scalar
destinations, and non-string members are invalid shapes rather than values to
coerce. Every string key allowed by existing destination rules remains valid,
including the empty string.
This representation directly expresses literal segments and keeps numeric key
text distinct from YAML numbers. It provides one explicit notation consistent
with the existing mapping-destination model. These shape rules define the new
directive syntax; other configuration values retain their existing semantics.

View File

@@ -1,392 +0,0 @@
# ConfigKit review findings
Reviewed `d3d468a..945e111` against the approved
[input-destinations roadmap](input-destinations.md),
input-destination implementation plan (preserved in Git history at
`315f464:docs/roadmap/implementation.md`), and
[ADR 0011](../adr/0011-input-destinations.md).
The root-owned destination fields, placement before strict composition,
transitive import placement, effective-path version ownership, final lifecycle,
source records, resource contexts, retained history, and existing disclosure
mechanisms broadly match the selected architecture. The required correction
below concerns an early diagnostic path, not loading or composition semantics.
No additional unintended strengthening of contracts was identified.
## 1. Rebase known Mapping-source failure paths to the input destination (P2)
Status: complete. Known Mapping value-builder paths are now prefixed with the
normalized destination at acquisition, preserving semantic key/index segments.
Public regressions cover mounted main and underlay failures, escaped keys,
sequence indices, cyclic values, unchanged root paths, and source-constructor
errors retaining source coordinates. The mounted value cases failed before the
fix and pass afterward. Existing directive/syntax checks remain unchanged.
Focused destination/source/diagnostic/import tests, `./scripts/check.sh`, and
`GOTOOLCHAIN=local GOPROXY=off go test -race ./...` passed on
`go1.26.8 linux/amd64`.
Affected: `assembleBaseInputTracked` in
[load_input.go](../../internal/engine/load_input.go), lines 27–32 at the reviewed
commit; the Mapping failure originates in `readSource` in
[source.go](../../internal/engine/source.go), lines 206–208.
`Mapping` snapshots its input through the existing value builder. When a nested
value is unsupported, cyclic, or exceeds a value-builder limit, the retained
issue already identifies a semantic path inside that application mapping.
`readSource` turns it into a structured diagnostic. The early return from
`assembleBaseInputTracked` annotates the source record with its destination but
returns those diagnostic paths unchanged.
For example, select this input as either main or an underlay:
```go
configkit.Input{
Source: configkit.Mapping("bad mapping", map[string]any{
"item": map[string]any{"bad": func() {}},
}),
Destination: configkit.Keys("catalog", "entries"),
}
```
The load correctly fails with `source.invalid`, but `Diagnostic.Path` is
`/item/bad` rather than `/catalog/entries/item/bad`. The source record correctly
reports `/catalog/entries`, so the two inspection records use inconsistent
application coordinates. A consumer displaying or associating the failure with
the final application shape would point to the wrong configuration field.
This diverges from implementation decision 10 and Stage 6: existing paths that
genuinely identify application content must be translated to effective paths
exactly once. A Mapping value-builder issue has that information; it is not a
source-read or YAML syntax failure for which a logical child path is unavailable.
Required remediation:
- Prefix known application-relative Mapping value-builder diagnostic paths with
the normalized input destination at the acquisition boundary. Preserve key
escaping and sequence-index intent in the existing semantic Path model.
- Keep source IDs, original primary/related locations, codes, value-free
messages, source destinations, and nil-result behavior unchanged. Root
placement must retain its current paths.
- Do not blanket-prefix parse/read failures, source-constructor errors, or
structural import-directive diagnostics. Do not invent logical child paths
for failures that do not identify application content.
- Add bounded regression coverage through the public API for mounted main and
underlay Mapping failures, alongside unchanged root placement. Reuse existing
syntax/directive tests to protect their different coordinate convention.
- Run the focused destination/source/diagnostic checks and the everyday check.
Reconcile the completed plan's diagnostic evidence with this correction;
do not redefine the agreed effective-path behavior to accept the gap.
## Review validation
- `GOTOOLCHAIN=local ./scripts/check.sh` passed at `945e111` on
`go1.26.8 linux/amd64` (formatting, whitespace, tests, vet, and build).
- `GOTOOLCHAIN=local GOPROXY=off go test -race ./...` passed.
- `GOTOOLCHAIN=local GOPROXY=off go test -count=1 ./...` passed.
- An additional external-package probe in a disposable checkout outside the
repository failed for both main and underlay placement, observing `/item/bad`
instead of `/catalog/entries/item/bad` with `source.invalid`.
- No production fixes or regression tests were added to this repository during
the review. No performance measurements or new guarantees were inferred.
## Relative import destination review (2026-10-14)
Reviewed `7248a8b..10edd95` against the
[relative-import roadmap](relative-import-destinations.md), completed
implementation plan (preserved in Git history at
`d9f08ff:docs/roadmap/implementation.md`), and
[ADR 0012](../adr/0012-relative-import-destinations.md).
The implementation matches the selected architecture: literal string segments
append to each immediate parent's destination; placement uses the existing
strict-composition and final-loading pipeline; source records retain distinct
effective destinations without changing physical resource contexts. No runtime
defect, missing feature behavior, or unintended strengthening of consumer
contracts was identified. The completed input-destination finding above remains
resolved. The documentation correction identified below is now resolved.
### 2. Restore ADR 0011's accepted decision text (P3)
Status: complete. Restored the accepted Decision paragraph verbatim from
`7248a8b`, leaving the extension link in ADR 0011's Status section and ADR 0012
unchanged. A comparison of everything from `## Date` onward is identical to the
baseline; the full ADR diff against `7248a8b` changes only Status. The uncached
local documentation-link check and `git diff --check` passed. The existing
`TestFacadeDestinationMappingFailurePaths` regression also passed, confirming
the earlier completed finding remains addressed. No runtime code, current
configuration/API documentation, or tests were changed.
Affected: [ADR 0011](../adr/0011-input-destinations.md), lines 34–42 at
`10edd95`. That commit rewrote its accepted Decision paragraph to incorporate
relative import destinations. Stage 1 of the implementation plan explicitly
required preserving that text, and the
[documentation policy](../policy/documentation.md#architecture-decision-records)
treats accepted decision content as immutable. Rewriting it obscures the original
decision and its subsequent extension, despite the accurate current behavior.
Required remediation:
- Restore that Decision paragraph verbatim from `7248a8b` (the same paragraph
remains unchanged at `a8beb3e`). Keep the extension link in ADR 0011's Status
section and retain ADR 0012 as the accepted extension record.
- Leave the implemented relative-import behavior and current configuration/API
documentation unchanged. No additional ADR or runtime tests are needed.
- Confirm the ADR 0011 diff against `7248a8b` changes only its Status section;
run the local documentation-link and whitespace checks.
### Relative import review validation
At `10edd95d4d9060bd46db51e0d6782c12dbda3120`, using
`go1.26.8 linux/amd64`, all of these passed:
- `GOTOOLCHAIN=local ./scripts/check.sh` (formatting, whitespace, tests,
examples, links, package boundaries, vet, and build).
- `GOTOOLCHAIN=local GOPROXY=off go test -race ./...`.
- `GOTOOLCHAIN=local GOPROXY=off go test -count=1 ./...`.
The review changed only this findings document; it did not implement fixes.
## Shared source-version review (2026-10-15)
Reviewed `70c95bf..020d720` against the
[source-version roadmap](source-version-checks.md), completed
[implementation plan](implementation.md), and
[ADR 0013](../adr/0013-shared-source-version-checks.md).
The root-owned mode and evidence records delegate to private per-load checking
state. Main's local header is retained, accepted non-main headers are removed
before placement/composition, and the destination-prefixed main rule reaches
payload protection and typed conversion. Warnings, raw accounting, source
contexts, and final loading reuse the existing pipeline. Four must-address
issues were identified and are now resolved below. Findings 1 and 2 above remain
completed historical findings.
### 3. Preserve distinct semantic shapes during evidence classification (P1)
Status: complete. Original evidence shapes are classified independently before
their results enter the existing pointer-based sensitivity/dependency closure.
Evidence no longer overwrites payload candidates. New engine and public
regressions failed before the fix and pass afterward, covering both acquisition
orders, schema tags, predicate observation of both shapes, and unchanged final
payload. Focused source-version/sensitivity/history tests, the everyday check,
and the full race suite passed on `go1.26.9 linux/amd64`. The implementation
plan records the additional Stage 9–12 regression evidence.
Affected: `classifySecretPathsWithEvidence` and `addSecretEvidencePath` in
[secret.go](../../internal/engine/secret.go), lines 78–104 at `020d720`.
Evidence candidates are stored in `map[string]Path`, keyed only by pointer text.
Adding a later candidate overwrites an earlier semantic Path with the same
pointer. A numeric mapping key and sequence index therefore compete for one
classification candidate. If the last shape is incompatible with a schema map,
`schemaSecretRoot` misses the secret field tag on the earlier mapping-shaped
header. Final source inspection can disclose its accepted text.
Reproduction through `LoadLayered`:
- Select required mode, `ParsePath("/meta/0/version")`, and a checker accepting
every declaration.
- The final Go type has ordinary `Meta map[string]Header` and
`Data.Meta map[string]SecretHeader`, with
`SecretHeader.Version string` tagged `yaml:"version" configkit:"secret"`.
- Main is `meta: {"0": {version: main}}` plus `data: {meta: null}`.
- Mount two separate underlay tiers at `Keys("data")`. The first document is
`meta: {"0": {version: KEY-HEADER-CANARY}}`; the second is
`meta: [{version: INDEX-HEADER-CANARY}]`.
Both headers are accepted and consumed. Main's null replaces their remaining
payload, so final decoding succeeds. The mapping header's source record exposes
`Text: "KEY-HEADER-CANARY"` with `Redacted: false`, despite its schema tag.
Reversing the underlay order redacts that same header; the classification result
depends on which shape overwrites the candidate, not the established sensitivity
policy. Three repeated runs reproduced this difference. The canaries are
disposable fixture text, not real credentials.
Required remediation:
- Retain/evaluate all distinct original semantic candidate shapes before
combining their classification results under the existing sensitivity policy.
Preserve both evidence and payload candidates; replacing one shape with
another must not erase applicable schema or predicate classification.
- Reuse historical/dependency sensitivity closure and conservative failure
disclosure. Do not fix this by exposing less evidence, forbidding numeric
keys/sequence headers, or requiring fragment conformance to the final Go type.
- Add a public regression covering both acquisition orders, the tagged consumed
header, and unchanged final payload. Include a focused shape-collision test at
the classification boundary and reuse existing neighboring-public-value tests.
- Run source-version/sensitivity/history tests, the everyday check, and the full
race suite. Reconcile the plan's Stage 9–12 completion evidence.
### 4. Preserve import opt-in before stripping source directives (P2)
Status: complete. Directive opt-in is now checked on the original document
before source-version processing. The shared acquisition boundary covers main
and direct underlay sources. The public regression failed for both enabled
modes and roles before the fix and now verifies `import.disabled`, source-local
attribution, and no partial result. Focused source-version/directive/import
tests and the everyday check passed on `go1.26.9 linux/amd64`; existing enabled
import and selected-overlay tests remain green.
Affected: `assembleBaseInputTrackedForRole` in
[load_input.go](../../internal/engine/load_input.go), lines 54–87, and
`processSourceVersion` in
[source_version.go](../../internal/engine/source_version.go), at `020d720`.
Enabled source checking calls `splitImportDirectives` regardless of Imports.
For an input with imports disabled, it then checks `disabledDirectiveDiagnostics`
on the already stripped application payload. A valid root directive disappears
without either traversal or the required `import.disabled` error.
For example, load bytes containing:
```yaml
version: main
configkit:
imports:
- path: missing.yml
value: ok
```
Use a version rule at `Keys("version")`, an accepting checker, and leave Imports
false. Default mode correctly returns `import.disabled`. Both enabled modes
incorrectly return a successful configuration, silently omitting the requested
import. This changes import semantics even though the new mode selects only
version checking.
Required remediation:
- Enforce the existing directive opt-in against original source content before
stripping/consumption can hide it. Cover both ordinary main and direct underlay
acquisition through their shared boundary. Keep genuine enabled imports and
selected-overlay restrictions unchanged.
- Add a focused public regression for enabled modes with Imports false and
source-root imports. Keep default-mode behavior and safe source-local failure
attribution; do not add a second implicit import-enabling mechanism.
- Run source-version/directive/import tests and the everyday check.
### 5. Retain the complete requested path for absent declaration evidence (P2)
Status: complete. Lookup now clones the full requested path and refines only
segments whose container shape was actually observed. Unresolved suffixes remain
intact, and nearest source locations remain independent of the header path.
Engine regressions protect segment intent and caller ownership; public
regressions cover missing early members, scalar obstruction, and unavailable
intermediate indices through optional final/callback evidence and required-load
failure evidence. Both regressions failed before the fix and pass afterward.
Focused source-version/path/source-record tests and the everyday check passed
on `go1.26.9 linux/amd64`. The configuration contract clarifies absence evidence.
Affected: `resolveSourceVersionPath` and `sourceVersionCheckForEvidence` in
[source_version.go](../../internal/engine/source_version.go), especially lines
74–90 at `020d720`; evidence classification uses the same partial-path fallback.
Lookup retains only the prefix reached before absence or obstruction, and that
prefix is still a valid Path. Public evidence therefore selects it instead of
the complete configured header path. For a rule at `Keys("meta", "version")`,
an optional underlay containing `meta: false` produces `Present: false` but
`VersionCheck.Path == "/meta"`, not `/meta/version`. Missing an early member or
an intermediate sequence index likewise discards the remaining suffix. The
record identifies an existing ancestor or partial path rather than the header
whose absence was checked.
Required remediation:
- Preserve the full requested local path when lookup stops early. Resolve the
traversed prefix's key/index intent using actual source shape and retain
unresolved suffix intent without guessing nonexistent container kinds.
- Keep nearest physical Location separate from the declaration Path. Reuse the
complete path consistently in successful/callback/failure evidence and its
classification candidates; do not change omission policy or invent authored
coordinates below the obstruction.
- Add bounded regressions for a missing early member, scalar obstruction, and
unavailable intermediate index, including successful optional and failed
required loads. Reuse existing present-path and copy-ownership coverage.
- Run source-version/path/source-record tests and the everyday check.
### 6. Apply structural-overlap rejection only to an actual declaration (P2)
Status: complete. Overlap rejection now requires an actually present selected
declaration. Optional absence proceeds without a non-main checker invocation;
required absence and malformed controls still fail under their existing rules.
The public regression failed before the fix and now covers valid inheritance
in separate mounted scopes, required omission with NoSource main, and invalid
inheritance controls. Existing present-overlap tests remain green. Focused
source-version/directive/inheritance tests, the everyday check, and the full
race suite passed on `go1.26.9 linux/amd64`. The configuration contract clarifies
the omission boundary. This removes the unintended strengthened restriction.
Affected: `processSourceVersion` in
[source_version.go](../../internal/engine/source_version.go), lines 197–219 at
`020d720`.
The structural-overlap test runs before handling absence and examines the
partially resolved prefix. A valid scalar `extends` control obstructing a deeper
header lookup is treated as a present overlapping declaration, even though
lookup correctly classified the requested header as absent. The roadmap's
accepted overlap restriction concerns an actual header/control overlap; missing
headers remain governed by the selected mode.
Reproduction: choose if-present mode and
`Keys("entries", "child", "extends", "version")`, with a checker accepting
main absence. Mount main `{}` at `Keys("main")`. Mount a separate underlay at
`Keys("under")` containing:
```yaml
entries:
base: {value: good}
child: {extends: base}
```
The underlay has no declaration, so its checker should be skipped and its valid
inheritance should remain available. Its inheritance paths do not intersect
main's destination-prefixed protected path. Instead, the load stops with
`version.error` reporting a structural overlap. This rejects an otherwise valid
optional omission, not a present value serving two structural roles.
The same fixture succeeds in default mode.
Required remediation:
- Base declaration-overlap rejection on actual header presence. Continue
validating malformed source controls independently, and preserve remaining
payload/inheritance ownership checks after omission handling.
- Keep optional non-main absence permissible and required absence a missing
declaration failure. Do not widen the accepted restriction into a ban on all
configured paths beneath directive spellings.
- Add a focused load-level regression with separate mounted scopes as above,
plus missing-required behavior. Reuse present-overlap and malformed-control
tests to ensure genuine conflicts remain rejected.
- Run source-version/directive/inheritance tests and the everyday check.
### Shared source-version review validation
At `020d72084d5988e958c46381724e5dad78cada3d`, using
`go1.26.9 linux/amd64`, these existing checks passed:
- `GOTOOLCHAIN=local ./scripts/check.sh`.
- `GOTOOLCHAIN=local GOPROXY=off go test -race ./...`.
- `GOTOOLCHAIN=local GOPROXY=off go test -count=1 ./...`.
Four additional external-package regression probes reproduced findings 3–6 in
an isolated temporary module outside this repository, with a local replace to
the reviewed checkout. The full probe command was
`GOTOOLCHAIN=local GOPROXY=off GOWORK=off go test -mod=mod -count=1 -v ./...`;
the classification and absent-control cases also reproduced with `-count=3`.
The module was located at `/tmp/configkit-source-version-review.L7YGaM` during
this review; the descriptions above preserve the cases independently of that
temporary directory.
No production fixes, repository regression tests, dependency updates, or release
changes were made. Only this findings document was revised.
### Remediation completion validation
All four external review probes now pass against the corrected checkout,
including both shape-collision acquisition orders. The final everyday check and
`GOTOOLCHAIN=local GOPROXY=off go test -race ./...` passed on
`go1.26.9 linux/amd64`. Each new regression was observed failing before its
corresponding production fix.
The earlier completed Mapping diagnostic regression was rerun successfully, and
ADR 0011 from `## Date` onward still matches its accepted text at `7248a8b`.
No findings remain open. No dependencies, source-version policies, or release
gates changed, and no release was published.

View File

@@ -1,269 +0,0 @@
# Shared Source Version Checks Roadmap
Status: implemented and validated. Current behavior is owned by the
[configuration contract](../config.md#shared-source-version-checks), and the
accepted decision is recorded in [ADR 0013](../adr/0013-shared-source-version-checks.md).
The completed [implementation plan](implementation.md) records execution and
validation evidence. This roadmap develops the
partially accepted source-version request in
[PromptKit's proposal](promptkit-feature-request.md#partially-accepted-addition-shared-source-specification-checks)
using the consumer's single `Definition.Version` policy. The design direction
below incorporates the maintainer's subsequent decisions on lookup, consumption,
inspection, and declaration handling; it supersedes the proposal's earlier
open-ended discussion of those subjects for this work.
## Targeted end state
A consumer can require that source documents contributing to a configuration
declare a version accepted by its existing `Definition.Version` checker. There
is one consumer-owned configuration specification and one acceptance policy.
Different accepted declarations may coexist: ConfigKit does not require exact
equality with the main declaration, impose SemVer, or invent an expected value.
The consumer selects `SourceVersionMode` on ordinary and layered options. Its
zero value retains current behavior. The modes are
`SourceVersionModeDefault`, `SourceVersionModeIfPresent`, and
`SourceVersionModeRequired`.
The same `Definition.Version` can be reused across loads with different
strictness. The three modes are:
| Mode | Missing non-main declaration | Present non-main declaration |
| --- | --- | --- |
| Current behavior, the default | Existing behavior | Existing behavior, including protected-path restrictions |
| Check when present | Proceed without a source-version callback for that omission | Apply the shared version vocabulary and checker |
| Require declarations | Structured load error | Apply the shared version vocabulary and checker |
The main declaration remains part of the configuration and retains its existing
checker policy. The treatment of missing main input and mounted main paths is
addressed explicitly below rather than silently replacing existing behavior.
An enabled mode requires a configured version rule and checker; select a normal
options error for an unusable rule, rather than silently ignoring the mode.
### Source-local lookup and shared policy
In enabled modes, interpret the configured header path relative to each source
document before mounting. Read that document's own declaration, not a composed
or overlaid value. An import cannot supply its parent's missing declaration,
and another source or later tier cannot hide a rejected declaration. Check
acquired participating documents even if their application payload is later
superseded. A rejection fails the load with no usable partial result.
Reuse the existing version vocabulary: absence, explicit null, strings, and
strict decimal integers. Null is present and reaches the checker as null; it is
not an omission. A required declaration need not be non-null if the consumer's
checker accepts null. Unsupported scalar/container kinds remain present but
fail the existing kind of version-type validation before invoking the checker.
Do not coerce malformed values into strings or treat them as missing. Preserve
exact string/integer text and the existing absence of a machine-integer range
requirement. No new string-versus-integer discriminator is required by this scope.
This checks declared compatibility. Final decoding and domain validation still
validate the assembled application; fragments are not independently decoded as
complete applications, and declarations do not prove schema conformance.
### Header consumption and evidence
After a non-main declaration passes its check, consume that declaration as
structural source metadata before its application content participates in
composition. Preserve the main declaration and existing main value semantics.
Do not consume unrelated nested versions, imported payload fields at other
paths, or declarations in the default current-behavior mode.
For example, with a source-local header path `version`, a non-main document's
`version: "1"` is checked and consumed even when that document will be mounted
under `collections/weather`. A local release label at that same path cannot
simultaneously remain ordinary payload in an enabled mode. Applications must
choose a distinct location for release metadata or for their specification
header. A prompt's `version` below a separate prompt key is unaffected unless
that is the configured header path.
Retain each checked declaration, its source-local path, and its physical source
location as inspectable evidence. Keep this evidence separate from effective
domain values and their composition history; consumed headers should not appear
as surviving fields or create effective-value diff changes. Source destination
and resource context remain available through existing inspection. Missing
declarations need an explicit absence state and an appropriate source-location
fallback for diagnostics, not an invented authored header coordinate.
Preserve normal safe diagnostics, warning behavior, callback lifetime, and
disclosure rules. Evidence will be attached to existing source inspection as a
detached declaration record, distinguishing unchecked sources, checked absence,
present null/text, and redaction, with local path and physical location. The
agreed sensitivity rules below govern disclosure; retaining evidence does not
authorize unredacted exposure of sensitive values or arbitrary malformed input.
### Participation and unchanged boundaries
Document participation must be explicit, independent of filesystem naming or
collection identity. The agreed scope is:
| Contribution | Treatment |
| --- | --- |
| Selected main document | Shared checker; declaration retained; no duplicate main callback |
| Direct underlay documents | Check under the selected mode; accepted headers consumed |
| Imported documents, including transitive imports and glob matches | Same policy and mode; accepted headers consumed |
| Selected overlay document | Same policy and mode; accepted header consumed before overlay application |
| Host files, native filesystem files, bytes, readers, and mapping sources | Equivalent document participation under their existing source contracts |
| CLI field overrides | Synthetic field operations; no document header requirement |
| Typed defaults, resolution operations, inheritance copies, and placement wrappers | Not newly acquired documents; no additional header checks |
| `NoSource`, unmatched optional imports, and empty groups | No acquired document and no new header requirement |
A present empty document mapping is still a document. Reuse of a source in
allowed separate tiers retains existing acquisition semantics; do not introduce
a global once-only check or a new uniqueness policy.
Existing source acquisition, containment, import destinations, strict composition,
precedence, inheritance, and complete-on-return loading continue to apply.
Consumption permits the checked header to serve as metadata; it must not allow
remaining payload to bypass main-version ownership. Synthetic overrides remain
subject to their existing protected-path restrictions.
This feature does not add independently versioned collections or subtrees,
per-input compatibility policies, automatic schema migration, YAML-selected
callbacks, or general source-validation hooks. These are outside this delivery,
not new permanent prohibitions on future work.
### Agreed main-path and consumption behavior
In enabled modes, locate main's header source-locally, retain it, and derive its
effective protected path by prefixing main's destination. Use that effective
path consistently for protection, existing typed version conversion, and
inspection. Invoke the shared checker once for main. Preserve its existing
acceptance policy for absence; required mode additionally rejects a missing
header when an actual main document participates. `NoSource` retains the existing
absent-main checker behavior because it is not a document.
Default mode keeps the existing effective-path interpretation. Root-mounted main
inputs need no path adjustment; mounted-main callers opting in must account for
the enabled mode's source-local interpretation. No separate main-version path or
root-only main restriction is introduced.
For nested mapping headers, consume only the selected non-main declaration and
retain authored ancestor containers, including newly empty mappings. This avoids
removing additional input presence or changing unrelated default/composition
behavior. Sequence-element and structural-directive behavior is settled below.
## Current baseline
Reviewed worktree baseline: `b3fc6a5`. Input destinations and relative import
destinations are implemented; the
[relative import roadmap](relative-import-destinations.md) records completion.
- [VersionRule and Version](../../lifecycle.go) provide a path, a checker, and
absent/null/text states. There is no expected-version string or source-check
mode. Root facade conversions preserve the public/internal boundary.
- The [version contract](../config.md#application-schema-versions) checks the
main version before defaults and typed decoding. The current path is an
effective application path; changing its interpretation in enabled modes
needs explicit handling for mounted main documents.
- [Loading](../../internal/engine/load.go) invokes the main check after main
acquisition/composition. [Version validation](../../internal/engine/validation.go)
already handles scalar vocabulary, checker diagnostics, and safe callback
errors. It is a useful shared mechanism, not a ready-made source-check API.
- [Imports](../../internal/engine/imports.go),
[underlays](../../internal/engine/load_request.go), and
[selected overlays](../../internal/engine/selected_overlay.go) currently reject
contributions to the protected version path. Enabled source checks need to
extract and consume headers before payload protection and composition, without
weakening the default behavior or checking only the eventual winning values.
- Existing source records carry identity, locations, and destination, but no
dedicated checked-version evidence. Existing sensitivity and inspection are
primarily organized around application paths and retained application trees.
## Acceptance outcomes
The completed feature supports all three modes through ordinary and layered
public loading, with these observable outcomes:
- One checker can accept several distinct source declarations. Rejected acquired
declarations fail the load even when later tiers would replace their payload.
- Absence, explicit null, strings, exact decimal integers, and malformed selected
values follow the shared version vocabulary and selected mode.
- Main retains its own declaration and is checked once; mounted main uses the
enabled mode's source-local interpretation, while NoSource and default mode
retain their documented behavior.
- Every participating document role and supported acquisition kind follows the
same selected policy. Synthetic field operations and absent sources add no
document-header requirement.
- Accepted non-main headers are consumed exactly, preserving authored empty
containers and ordinary sequence-removal semantics. Remaining payload retains
strict composition, precedence, protected-path rules, and ordinary versions
outside the selected header path.
- Source evidence is detached, physically attributed, available without reopening
files, and subject to the agreed sensitivity rules. Warnings survive successful
checks; generated failure messages remain safe.
- Effective values, presence, provenance, history, inheritance, rendering, diffs,
and resource contexts agree with remaining application payload. Consumed
declarations remain separate source evidence; main history stays ordinary.
- Maintained public examples demonstrate gradual and required adoption with one
policy. Current API/configuration documentation describes the delivered
contract, and existing default-mode behavior remains covered.
Independent specification policies, release preparation, and publication are
outside this feature's completion criteria.
## Agreed Edge-Case Decisions
The maintainer has accepted all three decisions below. Together with the mode
API location, source participation, main-path handling, source-record evidence,
and nested mapping ancestor preservation above, these settle the discussed
product decisions. Public names and integration mechanics are recorded in the
completed [implementation plan](implementation.md); current behavior belongs
to the [configuration contract](../config.md#shared-source-version-checks).
### Sensitivity of consumed declaration evidence
Classify declaration evidence using its original source shape
at its mounted application path, including classified ancestors. Preserve the
header as private evidence long enough to apply applicable existing field-tag,
predicate, historical, and recorded-dependency sensitivity rules; consumption
must not make a classified declaration public. Do not require the consumed path
to survive in final effective metadata. Use explicit redaction state and omit the
classified text from public evidence while retaining presence, source attribution,
and the fact that it was checked. The checker still receives the declaration as
trusted application code, as the existing version checker does.
For example, a header `meta.version` mounted at `collections.weather` is masked
if either that header path or an ancestor such as `collections.weather.meta` is
classified. An unclassified header remains inspectable. A later override must
not erase applicable historical classification. Sequence evidence uses original
source coordinates and segment kinds, not indexes shifted by consumption.
On failed loads, retain the existing safe diagnostic boundary: do not include raw
header text in generated failure messages. If checked evidence is exposed through
load errors before full sensitivity is available, mask its text unless applicable
classification can already be established safely; this does not require a new
failed-load evidence API or continuing a failed load to finish classification.
### Headers that are sequence elements
Support consumption of a selected sequence element by deleting
that element, retaining the sequence and its order among remaining elements.
Later indexes shift in the remaining payload; an emptied sequence remains `[]`.
This is ordinary removal, not a merge strategy. Retain the header's original
index and physical location in source evidence; downstream payload paths reflect
the post-consumption sequence. Main's retained declaration is not deleted.
Removing a mapping member inside a sequence element instead retains that element,
including an empty mapping, under the agreed ancestor-preservation rule.
This gives exact-node consumption a concrete meaning without restricting existing
path forms. Verify protected-path and typed behavior where main retains an element
but non-main payloads have shifted indexes; do not invent alignment or bypass
normal sequence replacement, decoding, or ownership rules to make such inputs fit.
### Overlap with structural directives
Reject an actual overlap between a checked header and an existing
structural directive node with a structured error in enabled modes. A value must
not simultaneously serve as a consumed specification declaration and as import
or inheritance control. Include a header selected inside an import declaration
or at an `extends` control; preserve ordinary structural validation so malformed
controls cannot disappear through consumption. Use source-local paths and real
locations for these errors. This is an explicitly accepted, narrow restriction
for enabled source checks, not a general prohibition on nested header paths.
Limit the rule to structural overlap, not substrings in ordinary keys. A header
at `metadata.version` is not blocked merely because its source also contains
`configkit.imports`, and a key such as `extends_version` is not an `extends`
control. Missing headers remain governed by the selected mode; existing invalid
directives still fail independently.