Document field-level resolution and matched sequence history

This commit is contained in:
2026-11-03 01:01:25 +00:00
parent 43ac34d10e
commit c2b3cb32da
10 changed files with 59 additions and 21 deletions

View File

@@ -772,6 +772,15 @@ in history. Across tiers, history follows the selected
whole-record upsert. Incoming contributions use their assigned positions, and
replaced payload remains available as historical evidence.
During resolution, eligible application-registered records are matched between
entry and final snapshots by identity at the same collection destination.
Queries at a surviving final position follow that record's entry history, while
collection queries retain physical ordering and removed evidence. For example,
if `[reports, invoices]` becomes `[invoices]`, item 0 follows invoices' earlier
position, the collection retains reports' removal, and the absent old item 1
remains available through historical-position queries. An added identity does
not inherit the old occupant's history. Source locations stay source-local.
A mapping assembled from the selected file and imports has one composite base origin
whose locations follow [scoped assembly order](#explicit-imports), not source-record
acquisition order. A mapping also visited by an overlay has an overlay-layer
@@ -1198,6 +1207,18 @@ records, helper targets, and unrecorded mutations fail the load. Equal-valued
derived assignments may still record application intent even when there is no
structural change.
Resolution compares equal-length ordinary lists recursively by position, so an
edit to `/resources/0/path` can be recorded at that field. A length change needs
a list or ancestor record. For application-registered collections, valid unique
entry and final identities match records at the same destination; changing order
or membership still requires list or ancestor coverage. Invalid or duplicate
endpoint identities make a changed list atomic for auditing, without an early
identity error; final registration validation remains at its normal boundary.
YAML-only declarations do not provide this correspondence. Records and helper
targets use final positions, and helpers never follow a later move automatically.
Fixed entry queries continue to use entry positions. See
[ADR 0021](adr/0021-sequence-resolution-correspondence.md) for the decision.
`Resolution.Error` and `Resolution.Warning` attach exact or nearest available input
locations. Reported errors fail the load; warnings survive on a successful result. A
returned resolver error adds one generic `resolve.hook_failed` error only when the
@@ -1356,7 +1377,7 @@ Incompatible overlaps and invalid registrations produce `options.invalid` before
source acquisition. Unused and root registrations are allowed and have no effect.
Registrations provide identity for strict composition, sequence operations,
inheritance lookup, and final validation. Dynamic selectors apply at concrete
inheritance lookup, resolution correspondence, and final validation. Dynamic selectors apply at concrete
strict-composition, inheritance, and final-result destinations without requiring
the application to enumerate mapping keys. Each strict tier has independent
duplicate-identity state among its peers; layered precedence and inheritance
@@ -2056,10 +2077,10 @@ rebuilt by unmarshaling their canonical text into fresh values, detaching privat
backing storage. Ignored and unexported struct fields are zeroed rather than copied.
Private structural comparison operates on canonical trees. Mapping additions,
removals, and descendant replacements retain semantic paths. A sequence is atomic:
any sequence change reports the sequence path and never invents correspondence
between old and new indices. Cross-kind and null/container changes replace the
affected path.
removals, and descendant replacements retain semantic paths. The shared
comparison used by effective diff treats sequences atomically. Resolution uses
its separate positional and registered-identity comparison described above.
Cross-kind and null/container changes replace the affected path.
These mechanisms support decoding, resolution auditing, metadata,
and rendering. They are not a public generic conversion or cloning API.

View File

@@ -65,6 +65,7 @@ Accepted decisions:
- [Apply CLI overrides after inheritance and decoding](adr/0018-cli-after-structural-resolution.md)
- [Keep main-file discovery explicit and composable](adr/0019-explicit-configuration-discovery.md)
- [Scope configuration controls to document roots](adr/0020-document-root-controls.md)
- [Match registered records during resolution inspection](adr/0021-sequence-resolution-correspondence.md)
For cross-cutting changes, follow every applicable row. Completed implementation
and remediation plans are preserved in Git history at `cb5b435`; use current
@@ -285,6 +286,7 @@ code changes. Use the component overview to find the corresponding implementatio
| Shared source-version modes and evidence | [Engine source-version tests](../internal/engine/source_version_test.go), [public facade tests](../facade_source_version_mode_test.go), and [consumer example tests](../examples/consumers/layered_test.go) | `go test ./internal/engine -run 'SourceVersion'`, `go test . -run 'SourceVersion'`, and `go test ./examples/consumers` |
| Schema, defaults, projection, cloning | [Implementation flow](internal/overview.md#implementation-flow), [schema](../internal/engine/schema_test.go), [decoding](../internal/engine/decode_test.go), [projection and typed cloning](../internal/engine/projection_test.go) | `go test ./internal/engine -run 'Schema\|Decode\|Projection\|Clone\|Inline\|CompareDocument'` |
| Lifecycle, resolution, validation, callback ownership | [Loading](../internal/engine/load_test.go), [resolution](../internal/engine/resolution_test.go), [validation](../internal/engine/validation_test.go), [ownership](../internal/engine/callback_ownership_test.go), [independence](../internal/engine/independence_test.go) | `go test ./internal/engine -run 'Load\|Resolution\|Validation\|Validator\|Hook\|Callback\|Parallel'` |
| Resolution sequence matching and inspection | [Correspondence](../internal/engine/resolution_correspondence_test.go), [auditing and history](../internal/engine/resolution_compare_test.go), [public integration](../facade_resolution_sequences_test.go) | `go test ./internal/engine -run 'Resolution\|History\|Explain\|Sensitivity'` and `go test . -run 'Resolution\|SequenceIdentity\|Diff'` |
| Paths, metadata, provenance | [Paths](../internal/engine/path_test.go), [metadata](../internal/engine/metadata_test.go), [provenance](../internal/engine/provenance_test.go) | `go test ./internal/engine -run 'Path\|Metadata\|Provenance\|Queries'` |
| Passive resource contexts | [Engine contexts](../internal/engine/resource_context_test.go), [public contexts](../facade_resource_context_test.go) | `go test ./internal/engine -run 'Resource'` and `go test . -run 'SourceContext'` |
| Secrets, bootstrap, redaction, explanation | [References](../internal/engine/secret_reference_test.go), [bootstrap](../internal/engine/secrets_test.go), [rendering](../internal/engine/render_test.go), [explanation](../internal/engine/explain_test.go) | `go test ./internal/engine -run 'Secret\|Bootstrap\|Effective\|Explain\|Explanation\|History\|Sensitivity'` |

View File

@@ -161,6 +161,9 @@ before application resolution begins. The callback retains its separate fixed
entry view; it does not read a builder that continues to change. After successful
resolution auditing, [`resolveLoadInput`](../../internal/engine/load_result.go)
adds the original resolution records and their net replay groups. At the existing
resolution boundary, [`resolution_correspondence.go`](../../internal/engine/resolution_correspondence.go)
matches eligible registered records and the shared history retains their paired
coordinates for selection and safe replay. At the existing
pre-secret admission boundary, [`prepareInspectionHistory`](../../internal/engine/load_result.go)
freezes that builder and counts the retained document roots before secret lookup
and validation.

View File

@@ -376,6 +376,11 @@ it does not cover every intermediate callback state or later caller mutations.
YAML-only declarations carry no such final guarantee through defaults, CLI
assignments, or application resolution.
For resolution auditing and inspection, eligible registered collections match
entry and final records by identity within each destination. Structural changes
still require container coverage, and this correspondence does not change source
composition or move final validation earlier. Public diff remains sequence-atomic.
Preserve source attribution, presence, history, sensitivity, and resource context
as records survive, move, or are replaced. Identity diagnostics use paths and
available origin information without exposing identity values. The guarantee

View File

@@ -1,5 +1,7 @@
# Sequence resolution and inspection implementation plan
Status: complete.
## Scope and execution
Implement the agreed [feature roadmap](resolution-sequences.md) from baseline
@@ -423,6 +425,8 @@ no exported type, selector, or callback lifecycle change is needed.
## Stage 13 — Document the implemented contract and validate the sprint
**Status:** Complete. Focused engine, facade, and consumer checks, `go test -race ./...`, and `./scripts/check.sh` passed with Go 1.27.0 on Linux.
**Scope:** canonical documentation and final integration checks.
1. Update `docs/config.md` resolution, layered metadata, historical explanation,

View File

@@ -1,6 +1,6 @@
# Field-level resolution and identity-aware sequence history
Status: agreed direction; not implemented. This roadmap describes field-level
Status: implemented. This roadmap records field-level
resolution auditing and identity-aware inspection of registered collections.
The [implementation plan](implementation.md) owns execution order and verification.
@@ -17,27 +17,25 @@ inspection. For ordinary lists, position is the only available correspondence;
compare positions as addresses without inferring which application object an
element represents.
## Current baseline
## Prior baseline
The [resolution contract](../config.md#recorded-application-resolution) audits
the typed configuration before and after `Definition.Resolve`. Mappings are
compared recursively, while the private structural comparator reports any list
change at the whole-list path. A field-level record inside a changed list
therefore fails to cover that list-level delta. Recording the whole list works.
At the planning baseline, the resolution contract audited the typed configuration
before and after `Definition.Resolve`. Mappings were compared recursively, while
the private structural comparator reported any list change at the whole-list
path. A field-level record inside a changed list therefore failed to cover that
list-level delta. Recording the whole list worked.
For example, changing `/resources/0/path` and recording only that path currently
produces an unrecorded-change diagnostic at `/resources`. This is established
For example, changing `/resources/0/path` and recording only that path previously
produced an unrecorded-change diagnostic at `/resources`. That was established
behavior rather than a new regression associated with v1.0.0.
The comparator also serves other inspection and metadata operations. Making
resolution auditing more precise must account for those callers rather than
silently changing the public diff contract.
Resolution currently captures before and after values at the same numeric path.
Its metadata refresh also consults entry metadata at that path. Following a
registered record through a move therefore requires more than changing which
deltas pass the coverage audit: the retained evidence must identify the matching
entry and final record.
The prior resolution implementation captured before and after values at the
same numeric path and refreshed metadata at that path. Following a registered
record through a move requires the matching entry and final record evidence.
Application registrations already define identities for collections and constrain
their final values. YAML-only declarations have a shorter lifetime. The