242 lines
14 KiB
Markdown
242 lines
14 KiB
Markdown
# Source origins during resolution
|
|
|
|
Status: planned; not implemented. This roadmap replaces the Notarius feature
|
|
request and owns the targeted end state. The [implementation plan](implementation.md)
|
|
owns execution order, progress, and handoffs.
|
|
This document does not authorize implementation or select a release version.
|
|
|
|
Current behavior remains defined by the [configuration contract](../config.md).
|
|
The [architecture policy](../policy/architecture.md) governs responsibility
|
|
boundaries, and the [documentation policy](../policy/documentation.md) governs
|
|
contract ownership and retirement of completed plans.
|
|
|
|
## Motivation
|
|
|
|
Notarius needs source-aware normalization of configured filesystem paths.
|
|
For example, `../references/party.txt` declared in `pipelines/dnd.yml` should
|
|
resolve relative to that declaring file, rather than the process working directory.
|
|
A profile replacement should use the profile file's directory even when it supplies
|
|
identical text; an untouched sibling should retain its original declaration.
|
|
|
|
Notarius separately chooses invocation-relative CLI paths, main-configuration-
|
|
relative defaults, and unchanged absolute paths. These are application policies,
|
|
not ConfigKit conventions. ConfigKit must provide the provenance needed to apply
|
|
them in one load without independently parsing YAML or reading files again.
|
|
|
|
Today, `Definition.Resolve` can inspect explicitness and record normalization, but
|
|
source-origin queries are available only after loading. Changing `Result.Config`
|
|
then would not change its frozen effective output or explanation. The target is to
|
|
make provenance available before normalization, while keeping runtime values and
|
|
inspection consistent through existing change recording.
|
|
|
|
## Target public surface
|
|
|
|
Add the following methods to the existing callback-scoped `Resolution` context:
|
|
|
|
```go
|
|
func (r *Resolution) InputInfo(path Path) (FieldInfo, bool)
|
|
func (r *Resolution) Source(id string) (SourceInfo, bool)
|
|
```
|
|
|
|
These signatures are the proposed target API, not callable APIs in the current
|
|
release. Reuse existing public `Path`, `FieldInfo`, `Origin`, `Location`, and
|
|
`SourceInfo` types. Do not introduce a second provenance record hierarchy, a
|
|
generic node API, or a new callback.
|
|
|
|
### InputInfo: a fixed pre-resolution view
|
|
|
|
Despite its short name, `InputInfo` describes the **decoded configuration at entry
|
|
to resolution**, including retained application defaults. It is not limited to
|
|
literal YAML nodes and is not a live view of callback mutations.
|
|
|
|
Its snapshot is taken after imports/composition, selected-profile application,
|
|
CLI overrides, defaults, and typed decoding, but before application resolution
|
|
and typed credential lookup. The query returns the same presence/origin meaning
|
|
as effective metadata at that boundary:
|
|
|
|
| Path state at callback entry | Result |
|
|
| --- | --- |
|
|
| Surviving file, profile, or CLI contribution | Found; existing explicit/null and origin information |
|
|
| Present default-only value | Found; non-explicit, default origin, no invented file location |
|
|
| Explicit null accepted by the schema | Found; explicit and null |
|
|
| Missing path or descendant removed before decoding | Not found unless ordinary decoding retained/reintroduced it through defaults |
|
|
| Invalid path or key/index shape mismatch | Not found |
|
|
|
|
A Go zero-valued field that exists in the decoded projection is not a missing
|
|
path. Zero metadata fields alone must not be used to infer absence; callers use
|
|
the returned boolean. Invalid/missing queries add no diagnostics.
|
|
|
|
Queries address exact semantic paths through nested structs, inlined fields,
|
|
mapping entries, arrays, and sequence elements. Use existing path rules, including
|
|
escaped keys, explicit key/index distinctions, and canonical contextual sequence
|
|
indices. Never substitute nearest-ancestor diagnostic lookup for exact field lookup.
|
|
|
|
For a scalar, the origin identifies its surviving declaration, not the contributors
|
|
of its parent mapping. Equal-valued profile and CLI replacements retain the
|
|
replacing origin. Containers retain their existing possibly composite locations;
|
|
the API does not select a representative owner or single declaring directory.
|
|
|
|
`InputInfo` and `IsExplicit` must agree for paths present in the entry snapshot.
|
|
The extension must not change existing `IsExplicit` behavior for paths outside
|
|
that view. Normalization records, additions, removals, and moves do not alter the
|
|
snapshot. A removed original path stays queryable; a new destination that did not
|
|
exist at entry remains absent from this view. If a destination already existed,
|
|
its query describes that original value, not the moved value.
|
|
|
|
### Source: resolve identities without filesystem work
|
|
|
|
`Source(id)` returns the existing source record for an ID belonging to this load.
|
|
Empty or unknown IDs return a zero record and false without adding diagnostics.
|
|
The source view contains the records already acquired before resolution, including
|
|
main input, imported files, the selected profile, and CLI records where applicable.
|
|
It does not discover files, reload input, or inspect unselected profiles.
|
|
|
|
A returned `SourceInfo.Path` is the canonical local path only when the existing
|
|
source model supplies one. File paths retain current canonicalization and symlink
|
|
semantics. Labels remain diagnostic labels, never a substitute for paths.
|
|
Bytes/readers and CLI records have no fabricated file path, even if a filesystem
|
|
context was supplied for imports or profiles. Defaults have no source record or
|
|
synthetic file identity.
|
|
|
|
Every source identity referenced by the entry view's origin locations must be
|
|
resolvable through this source view. Existing fields distinguish layers and import
|
|
relationships; do not require callers to guess scalar ownership from source order.
|
|
The existing main-source identity convention remains unchanged. Applications may
|
|
use the main source record for their own default-base policy; a non-file main
|
|
source still supplies no directory, and the application must choose its fallback.
|
|
|
|
Multiple locations do not imply one declaring file. Consumers needing a single
|
|
file must inspect the exact value's origin and source records and handle ambiguity
|
|
according to application policy. ConfigKit does not require rejection: an
|
|
application may choose an explicit fallback, retain a relative value, warn, or
|
|
report an error. No new `DeclaringFile` shortcut should silently discard contributors.
|
|
|
|
### Lifetime and detachment
|
|
|
|
Both methods require an active resolution callback. Nil, zero, or retained contexts
|
|
used after callback closure follow the existing fixed, value-free panic convention,
|
|
including when a callback returns an error. These methods do not make the context
|
|
safe for concurrent use.
|
|
|
|
Returned records are detached, including nested slices and `ImportedFrom`
|
|
locations. They may be retained after callback exit; modifying them must not affect
|
|
subsequent queries, other loads, final metadata, or explanations. The context's
|
|
lifetime restriction does not apply to those copied records.
|
|
|
|
## Consumer normalization workflow
|
|
|
|
The intended Notarius workflow is:
|
|
|
|
1. Query a path's entry-state field information before moving or normalizing it.
|
|
2. Resolve its origin source IDs and select a base using application policy.
|
|
3. Compute the application path without asking ConfigKit to interpret it.
|
|
4. Assign and record the result using existing `Normalize` or `Resolution.Record`
|
|
semantics, including dependencies where appropriate.
|
|
5. Use the completed result for both execution and configuration inspection.
|
|
|
|
Consumers may retain original origin records for later dependent work in the same
|
|
callback. ConfigKit does not infer ownership transfers, rewrite the input view, or
|
|
automatically record a move. Existing mutation auditing and record validation
|
|
continue to apply to all changes.
|
|
|
|
For canonical file sources, a symlink refers to the resolved target file's
|
|
directory, not necessarily the directory containing the link used to open it.
|
|
The maintained example must make this explicit. Applications wanting another
|
|
path-base policy own that policy and the additional information it requires.
|
|
|
|
## Intended internal architecture
|
|
|
|
Keep one provenance model and one loading lifecycle. The existing
|
|
`resolveLoadInput` in `load_result.go` already assembles decoded metadata and
|
|
overlay history before calling `runResolution`. Pass a read-only entry-state
|
|
provenance view and the load's acquired source records into the private resolution
|
|
state. No public parser types or new external dependencies are needed.
|
|
|
|
The view must support complete effective semantic-path lookup when the callback
|
|
starts. Current result metadata indexes are finalized later; exposing an unfinished
|
|
`Metadata` value would make legitimate lookups fail. Reuse or factor existing
|
|
effective-path traversal and shape matching so entry queries and final queries
|
|
cannot acquire different path semantics. A full historical index or public
|
|
`Metadata.History` interface during resolution is not required by this feature.
|
|
|
|
Keep queries stable across consumer normalization and return detached records.
|
|
Private copying, immutable sharing, or ownership transfer may satisfy that contract;
|
|
no particular backing representation or eager-copy strategy is required. Prefer
|
|
existing traversal/indexing machinery and proportionate allocation. A per-load
|
|
source index is a recommended mechanism, not a public complexity guarantee or a
|
|
prohibition on scanning the bounded existing source collection. Consumers should
|
|
not need to rebuild source bookkeeping themselves.
|
|
|
|
The source and input views remain bounded by existing load limits. They contain
|
|
provenance records, not configuration values or resolved credentials. Do not read
|
|
the environment, resolve secret references, perform filesystem I/O, or invoke
|
|
application codecs in response to a query.
|
|
|
|
Existing composition precedence, canonical decoding, normalization auditing,
|
|
resolution origins/history, redaction, and frozen result publication remain
|
|
authoritative. This is an information-access extension, not a new normalization
|
|
engine or permission to mutate post-load snapshots.
|
|
|
|
## Acceptance criteria
|
|
|
|
The completed feature must demonstrate the following through public API behavior,
|
|
using real temporary files where source identity matters:
|
|
|
|
1. A main-file scalar can be normalized relative to its canonical declaring file
|
|
during resolution, independent of invocation directory.
|
|
2. An imported scalar, including a sequence element, identifies its own declaring
|
|
file rather than the main file or a parent container's contributors.
|
|
3. A profile replacement identifies the profile source while untouched siblings
|
|
retain their base/import origins.
|
|
4. Equal-text profile and CLI replacements still identify the replacing source.
|
|
5. Defaults, missing paths, explicit null, CLI input, and file input are distinct;
|
|
`NoSource` and non-file input introduce no fictional declaring paths.
|
|
6. Composite containers retain all contributing locations without selecting a file.
|
|
Every referenced source ID resolves, and unknown IDs are harmless misses.
|
|
7. Queries remain fixed across normalization, deletion, addition, and moves;
|
|
retained copies remain useful and detached.
|
|
8. Exact lookup works through typed and dynamic structures, escaped mapping keys,
|
|
and sequence indices; invalid paths and shape mismatches do not borrow ancestor
|
|
or sibling provenance.
|
|
9. A successful single load yields identical runtime and effective values, with
|
|
explanation preserving original declarations and recorded normalization reasons.
|
|
10. Querying origins performs no extra load or credential lookup, and existing
|
|
secret redaction, safe diagnostics, and callback-lifetime rules remain intact.
|
|
11. Mutating returned records cannot affect later queries or final results.
|
|
12. Maintained Notarius-shaped examples demonstrate the policy using only the
|
|
public API and standard library, including documented canonical/symlink behavior.
|
|
|
|
Tests should extend existing provenance, resolution, ownership, and consumer
|
|
behavioral owners rather than duplicate every mechanism at every layer. Acceptance
|
|
does not require a live Notarius checkout, external credentials, or target-file
|
|
existence checks.
|
|
|
|
## Scope and existing boundaries
|
|
|
|
Application responsibilities remain application responsibilities: interpreting
|
|
path-valued strings, normalizing filesystem paths, choosing default/CLI bases,
|
|
and deciding whether to check existence, impose confinement, or use fallbacks.
|
|
This feature neither performs those operations nor prohibits consumers from doing so.
|
|
Actual Notarius integration remains downstream work.
|
|
|
|
Existing contracts, including import/profile composition and frozen post-load
|
|
inspection, remain unchanged. Their authority comes from the current contracts,
|
|
not from this roadmap's exclusions.
|
|
|
|
Source discovery, historical value access during resolution, automatic move
|
|
tracking, and a general provenance service are not included in this feature.
|
|
That scope choice is not a repository-wide prohibition on future proposals.
|
|
|
|
## Compatibility and canonical ownership
|
|
|
|
This is an additive public capability; existing callbacks, loading order, and
|
|
configuration semantics must remain unchanged. Release selection and validation
|
|
follow the [release policy](../policy/release.md) and
|
|
[release procedure](../release.md). No release gate is waived here.
|
|
|
|
In the completed feature, Go reference comments own API and lifetime definitions,
|
|
the configuration contract owns shared resolution/provenance semantics, and the
|
|
adoption guide and executable examples own consumer usage. Component documentation
|
|
describes the resulting internal boundaries. Accepted ADR rationale remains
|
|
historical; changes to governing decisions follow the documented supersession policy.
|