Record source version decisions and remaining edge cases

This commit is contained in:
2026-10-14 20:30:58 +00:00
parent e3860036f8
commit 2c91e41d10

View File

@@ -16,7 +16,10 @@ is one consumer-owned configuration specification and one acceptance policy.
Different accepted declarations may coexist: ConfigKit does not require exact Different accepted declarations may coexist: ConfigKit does not require exact
equality with the main declaration, impose SemVer, or invent an expected value. equality with the main declaration, impose SemVer, or invent an expected value.
The consumer selects one of three modes through the supported loading API: The consumer selects a load-wide enum on ordinary and layered options. Its zero
value retains current behavior; exact exported names remain implementation design
work. 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 | | Mode | Missing non-main declaration | Present non-main declaration |
| --- | --- | --- | | --- | --- | --- |
@@ -77,16 +80,18 @@ declarations need an explicit absence state and an appropriate source-location
fallback for diagnostics, not an invented authored header coordinate. fallback for diagnostics, not an invented authored header coordinate.
Preserve normal safe diagnostics, warning behavior, callback lifetime, and Preserve normal safe diagnostics, warning behavior, callback lifetime, and
disclosure rules. The public evidence representation and treatment of classified disclosure rules. Evidence will be attached to existing source inspection as a
declarations need the design decision below; retaining evidence does not authorize detached declaration record, distinguishing unchecked sources, checked absence,
present null/text, and redaction, with local path and physical location. The
remaining sensitivity decision below governs disclosure; retaining evidence does not authorize
unredacted exposure of sensitive values or arbitrary malformed input. unredacted exposure of sensitive values or arbitrary malformed input.
### Participation and unchanged boundaries ### Participation and unchanged boundaries
Document participation must be explicit, independent of filesystem naming or Document participation must be explicit, independent of filesystem naming or
collection identity. The recommended scope is: collection identity. The agreed scope is:
| Contribution | Recommended treatment | | Contribution | Treatment |
| --- | --- | | --- | --- |
| Selected main document | Shared checker; declaration retained; no duplicate main callback | | Selected main document | Shared checker; declaration retained; no duplicate main callback |
| Direct underlay documents | Check under the selected mode; accepted headers consumed | | Direct underlay documents | Check under the selected mode; accepted headers consumed |
@@ -99,8 +104,7 @@ collection identity. The recommended scope is:
A present empty document mapping is still a document. Reuse of a source in A present empty document mapping is still a document. Reuse of a source in
allowed separate tiers retains existing acquisition semantics; do not introduce allowed separate tiers retains existing acquisition semantics; do not introduce
a global once-only check or a new uniqueness policy. The participation boundary a global once-only check or a new uniqueness policy.
and mode API remain recommendations to settle in stage 1.
Existing source acquisition, containment, import destinations, strict composition, Existing source acquisition, containment, import destinations, strict composition,
precedence, inheritance, and complete-on-return loading continue to apply. precedence, inheritance, and complete-on-return loading continue to apply.
@@ -113,6 +117,26 @@ per-input compatibility policies, automatic schema migration, YAML-selected
callbacks, or general source-validation hooks. These are outside this delivery, callbacks, or general source-validation hooks. These are outside this delivery,
not new permanent prohibitions on future work. 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 cases remain open below.
## Current baseline ## Current baseline
Reviewed worktree baseline: `b3fc6a5`. Input destinations and relative import Reviewed worktree baseline: `b3fc6a5`. Input destinations and relative import
@@ -144,13 +168,13 @@ destinations are implemented; the
### 1. Finalize the contract and integration design ### 1. Finalize the contract and integration design
Resolve the Open Decisions below. Select public mode names and placement, source Resolve the Open Decisions below. Select exact public mode and evidence type
participation, main-path handling, and evidence types. Define header lookup for names within the agreed load-wide API, participation, main-path, and inspection
design. Define header lookup for
nested paths and distinguish a missing member from an obstructing non-container nested paths and distinguish a missing member from an obstructing non-container
ancestor. Specify consumption without broadening it into a generic transformation ancestor. Specify consumption without broadening it into a generic transformation
API. Recommend removing only the selected header node and preserving its authored API. Preserve authored ancestors when consuming nested mapping headers as agreed;
ancestor containers, including newly empty mappings; decide handling of overlaps settle sequence-element consumption and structural-directive overlaps below.
with structural directives before implementation.
Record the change to main-only version ownership in an ADR and review public API Record the change to main-only version ownership in an ADR and review public API
compatibility under repository policy. Do not rewrite accepted ADR decisions or compatibility under repository policy. Do not rewrite accepted ADR decisions or
@@ -245,74 +269,87 @@ required to complete this feature.
## Open Decisions ## Open Decisions
### Mode API and document participation These are recommendations for further discussion, not accepted restrictions or
implemented behavior. The mode API location, source participation, main-path
handling, source-record evidence, and nested mapping ancestor preservation above
have been agreed.
Recommendation: expose a load-wide enum on ordinary and layered options, with ### Sensitivity of consumed declaration evidence
zero retaining current behavior, and use the participation table above. This
allows one reusable `Definition.Version` policy to be used with different adoption
strictness on different loads. Include mapping sources and selected overlays:
they contribute documents even though they are not necessarily imported files.
Exclude synthetic field operations and absent sources as described above.
Alternative: place the mode on `VersionRule` so policy and enforcement travel Recommendation: classify declaration evidence using its original source shape
together. This reduces option plumbing but requires changing/copying the rule at its mounted application path, including classified ancestors. Preserve the
to vary enforcement. A narrower first implementation checking imports and header as private evidence long enough to apply applicable existing field-tag,
underlays but exempting overlays is also viable; it reduces integration work but predicate, historical, and recorded-dependency sensitivity rules; consumption
offers weaker assurance about all contributing documents and needs an explicit must not make a classified declaration public. Do not require the consumed path
documented exception. Neither choice should imply per-source independent policies. 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.
### Mounted main documents and the protected application path 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.
Recommendation: in enabled modes, locate main's header source-locally as requested, On failed loads, retain the existing safe diagnostic boundary: do not include raw
retain it, and derive its effective protected path by prefixing main's destination. header text in generated failure messages. If checked evidence is exposed through
Use that derived path consistently for protection, existing typed version load errors before full sensitivity is available, mask its text unless applicable
conversion, and inspection. Invoke the shared checker once for main, preserving classification can already be established safely; this does not require a new
its existing acceptance policy for absence; required mode additionally rejects a failed-load evidence API or continuing a failed load to finish classification.
missing declaration when an actual main document participates. `NoSource` retains
the existing absent-main checker behavior, since it is not a document.
Default mode keeps today's effective-path interpretation unchanged. This supports
mounted main input without contradicting source-local lookup or losing protection.
Alternative: separate the public source-header path from the existing effective Alternative: always redact declaration text in public source evidence. This
main-version path while still using one checker. It makes both coordinate systems simplifies classification and prevents disclosure, but hides ordinary version
explicit and can ease migration for mounted main callers, but adds configuration identifiers that the consumer explicitly wants to inspect. Conversely, treating
and two paths that must be kept coherent. Root-only main input would simplify the headers as inherently public would be simpler but would bypass existing
problem but would restrict an existing capability; do not impose that restriction classification where the configured header path or its ancestor is sensitive.
as an implementation shortcut. Prefer normal classification rather than either blanket rule.
### Evidence representation and sensitivity ### Headers that are sequence elements
Recommendation: attach a detached declaration record to existing source Recommendation: support consumption of a selected sequence element by deleting
inspection, with local path, presence/null/text state, physical location, and that element, retaining the sequence and its order among remaining elements.
explicit redaction state. Reuse existing sensitivity machinery where applicable Later indexes shift in the remaining payload; an emptied sequence remains `[]`.
to the source's mounted header path, retaining classification evidence before This is ordinary removal, not a merge strategy. Retain the header's original
consumption so removing a node does not erase its sensitivity. Main evidence and index and physical location in source evidence; downstream payload paths reflect
source evidence should use consistent disclosure. Retain actual declaration text the post-consumption sequence. Main's retained declaration is not deleted.
privately when needed; inspectable sensitive evidence can be redacted without Removing a mapping member inside a sequence element instead retains that element,
denying that a declaration was checked. Specify handling for historical tags and including an empty mapping, under the agreed ancestor-preservation rule.
dependency classification before choosing storage, rather than exposing raw
`Version.Text` unconditionally.
Alternative: expose declarations through a dedicated source query returning safe This gives exact-node consumption a concrete meaning without restricting existing
snapshots instead of extending source records. This better accommodates on-demand path forms. Verify protected-path and typed behavior where main retains an element
redaction and richer evidence, but adds an API and lookup semantics. Both choices but non-main payloads have shifted indexes; do not invent alignment or bypass
must preserve physical attribution and the requested declaration evidence; neither normal sequence replacement, decoding, or ownership rules to make such inputs fit.
should expose arbitrary malformed input or require reopening the source.
### Nested consumption and structural directives Alternative: reject headers whose selected node is a sequence element in enabled
modes, while permitting mapping-member headers nested inside sequences. This
avoids index shifts and may be easier for consumers to reason about, but narrows
supported header locations and is not required for correctness if deletion is
specified. Replacing the element with null would preserve indexes but fabricate a
payload value and could change decoding; it is not the recommended interpretation
of consumption.
Recommendation: remove only the checked non-main declaration and retain authored ### Overlap with structural directives
ancestor containers, even when empty. Validate source structure and interpret
existing directives so header consumption cannot conceal an invalid directive or
silently cancel an import. Preserve the existing path vocabulary where its
semantics apply; explicitly work through sequence-index and structural-path
interactions before implementation instead of adding a blanket top-level-only
header restriction. Retaining ancestors best preserves input presence outside
the requested consumed value.
Alternative: prune ancestor containers emptied by consumption. This can make a Recommendation: reject an actual overlap between a checked header and an existing
nested metadata-only header disappear completely from application payload, but structural directive node with a structured error in enabled modes. A value must
also removes authored empty-container presence and can affect defaults and not simultaneously serve as a consumed specification declaration and as import
composition. A dedicated top-level header location would simplify these cases, or inheritance control. Include a header selected inside an import declaration
but narrows the existing configurable-path model and would need an explicit or at an `extends` control; preserve ordinary structural validation so malformed
consumer requirement or design decision rather than an implementation shortcut. controls cannot disappear through consumption. Use source-local paths and real
locations for these errors. This is a proposed semantic restriction to approve,
not an existing 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.
Alternative: interpret structural directives first, then allow the same node to
serve as a checked and consumed header. This can be defined and avoids a new
path restriction, but gives one value two unrelated roles: for example, an
accepted version string could also name an import file or inheritance parent.
It also needs extra ordering and retained-control rules to keep consumption from
canceling behavior. Prefer an explicit overlap error over that dual interpretation;
there is no identified consumer need for sharing these roles.