2026-11-02 13:09:03 +00:00
|
|
|
# Field-level resolution and identity-aware sequence history
|
2026-11-02 12:42:25 +00:00
|
|
|
|
2026-11-02 13:09:03 +00:00
|
|
|
Status: agreed direction; not implemented. This roadmap describes field-level
|
|
|
|
|
resolution auditing and identity-aware inspection of registered collections.
|
|
|
|
|
A staged implementation plan will follow review of the integrated roadmap.
|
2026-11-02 12:42:25 +00:00
|
|
|
|
|
|
|
|
## Purpose
|
|
|
|
|
|
|
|
|
|
Allow applications to normalize or derive individual fields inside lists and
|
|
|
|
|
record those fields directly. Preserve the requirement that every net change
|
|
|
|
|
made during resolution has an exact or ancestor record, together with coherent
|
|
|
|
|
final values, provenance, history, and sensitivity metadata.
|
|
|
|
|
|
2026-11-02 13:09:03 +00:00
|
|
|
For a collection with an applicable application registration, the declared key
|
|
|
|
|
defines a record across positions. Use that correspondence in auditing and
|
|
|
|
|
inspection. For ordinary lists, position is the only available correspondence;
|
|
|
|
|
compare positions as addresses without inferring which application object an
|
|
|
|
|
element represents.
|
2026-11-02 12:42:25 +00:00
|
|
|
|
|
|
|
|
## Current 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.
|
|
|
|
|
|
|
|
|
|
For example, changing `/resources/0/path` and recording only that path currently
|
|
|
|
|
produces an unrecorded-change diagnostic at `/resources`. This is 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.
|
|
|
|
|
|
2026-11-02 13:09:03 +00:00
|
|
|
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.
|
|
|
|
|
|
2026-11-02 12:42:25 +00:00
|
|
|
Application registrations already define identities for collections and constrain
|
|
|
|
|
their final values. YAML-only declarations have a shorter lifetime. The
|
|
|
|
|
[identity contract](../config.md#application-registrations-and-identity-details)
|
|
|
|
|
and [ADR 0017](../adr/0017-collection-sequence-policy.md) own those distinctions.
|
|
|
|
|
|
|
|
|
|
## Target behavior
|
|
|
|
|
|
|
|
|
|
### Mappings and ordinary lists
|
|
|
|
|
|
|
|
|
|
Mapping comparison remains recursive. For lists without applicable application
|
|
|
|
|
identity registrations:
|
|
|
|
|
|
|
|
|
|
- If the lists have equal lengths, compare corresponding positions recursively.
|
|
|
|
|
A position is an address, not an assertion of element identity.
|
|
|
|
|
- If the length changes, report a structural change at the list path, requiring
|
|
|
|
|
a record for that list or an ancestor.
|
|
|
|
|
- For equal-length lists, a reorder manifests as changes at the affected
|
|
|
|
|
positions. Every detected change needs coverage; a whole-list record remains
|
|
|
|
|
a convenient way to cover the operation.
|
|
|
|
|
|
|
|
|
|
Apply these rules recursively to nested values. Preserve existing treatment of
|
|
|
|
|
additions, removals, null, scalar differences, and changes of value kind except
|
|
|
|
|
where finer sequence comparison deliberately changes the reported path.
|
2026-11-02 13:09:03 +00:00
|
|
|
Ordinary-list inspection remains positional; similar field values do not justify
|
|
|
|
|
moving provenance or history between positions.
|
2026-11-02 12:42:25 +00:00
|
|
|
|
|
|
|
|
### Lists with registered identity
|
|
|
|
|
|
|
|
|
|
Use applicable `Definition.SequenceIdentities` registrations to match records
|
|
|
|
|
between the entry and final snapshots. Reuse existing identity-field selection,
|
|
|
|
|
compound-identity, scalar-equality, and duplicate-detection semantics. Do not infer
|
|
|
|
|
an identity from a field merely named `name` or `id`.
|
|
|
|
|
|
|
|
|
|
| Observed change | Proposed auditing behavior |
|
|
|
|
|
| --- | --- |
|
|
|
|
|
| An ordinary field changes on a matched record | Compare recursively and allow a record covering that field. |
|
|
|
|
|
| Identity order changes | Report a structural change at the list path; require a list or ancestor record. |
|
|
|
|
|
| An identity is added or removed | Report a structural change at the list path; require a list or ancestor record. |
|
|
|
|
|
| An identity changes | Treat it as removal and addition, without inferring a rename. |
|
|
|
|
|
|
|
|
|
|
Compare matched records by identity even when their numeric positions differ.
|
|
|
|
|
Structural changes still require whole-list coverage. A field record by itself
|
|
|
|
|
does not cover an accompanying reorder or membership change.
|
|
|
|
|
|
2026-11-02 13:09:03 +00:00
|
|
|
Identity matching serves both audit coverage and inspection. Although a
|
|
|
|
|
whole-list record covers every edit in a moved collection, matching still
|
|
|
|
|
identifies which record's earlier values and source evidence belong to each
|
|
|
|
|
final record. Stopping at a whole-list delta is insufficient for that purpose.
|
|
|
|
|
|
|
|
|
|
Match only the resolution-entry and final snapshots. Identity changes are
|
|
|
|
|
removal and addition; temporary identities, intermediate moves, and the order of
|
|
|
|
|
callback mutations are not reconstructed. Matching does not introduce a new
|
|
|
|
|
collection merge operation or change whole-record `upsert`.
|
|
|
|
|
|
|
|
|
|
Use the existing registration selectors and scope. They traverse mapping keys,
|
|
|
|
|
not sequence indexes; recursive comparison of nested lists does not expand the
|
|
|
|
|
registration API. YAML-only declarations do not acquire a new final-resolution
|
|
|
|
|
guarantee or become identity selectors for this work.
|
|
|
|
|
|
|
|
|
|
### Matching eligibility
|
|
|
|
|
|
|
|
|
|
Use identity matching when both snapshots provide valid, unique identities under
|
|
|
|
|
the applicable registration. Otherwise use conservative whole-list auditing for
|
|
|
|
|
a changed collection, without inventing correspondence. An unchanged collection
|
|
|
|
|
does not require a resolution record merely because matching is unavailable.
|
|
|
|
|
|
|
|
|
|
Preserve existing final identity validation. Resolution may repair invalid entry
|
|
|
|
|
values; this feature does not reject them earlier or require valid identities
|
|
|
|
|
throughout the callback. When matching is unavailable, the recorded container
|
|
|
|
|
operation describes the change without claiming that individual records survived.
|
2026-11-02 12:42:25 +00:00
|
|
|
|
|
|
|
|
### Example
|
|
|
|
|
|
|
|
|
|
Assume the application registers `name` as the identity for `resources`:
|
|
|
|
|
|
|
|
|
|
```yaml
|
|
|
|
|
resources:
|
|
|
|
|
- name: reports
|
|
|
|
|
path: ./reports
|
|
|
|
|
- name: invoices
|
|
|
|
|
path: ./invoices
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Changing the first path to `/srv/reports` can be covered by a normalization record
|
|
|
|
|
at `/resources/0/path`. The unchanged `invoices` record requires no record.
|
|
|
|
|
|
2026-11-02 13:09:03 +00:00
|
|
|
If resolution also swaps the records, the final collection is:
|
|
|
|
|
|
|
|
|
|
```yaml
|
|
|
|
|
resources:
|
|
|
|
|
- name: invoices
|
|
|
|
|
path: ./invoices
|
|
|
|
|
- name: reports
|
|
|
|
|
path: /srv/reports
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Recording only `/resources/1/path` does not cover the reorder. Recording
|
|
|
|
|
`/resources` covers that structural operation and any field edits within the
|
|
|
|
|
list. A field record can additionally explain the path normalization.
|
|
|
|
|
|
|
|
|
|
Inspection at the final `reports` position includes that record's earlier
|
|
|
|
|
evidence from position 0, with its original source coordinates, and the recorded
|
|
|
|
|
collection operation explaining the move. Its previous path is `./reports`,
|
|
|
|
|
not the `./invoices` value that previously occupied position 1. The `invoices`
|
|
|
|
|
record moved without a field change. An ordinary unregistered list instead
|
|
|
|
|
describes changes at positions 0 and 1, without identifying records across them.
|
2026-11-02 12:42:25 +00:00
|
|
|
|
|
|
|
|
### Recording, inspection, and ownership
|
|
|
|
|
|
2026-11-02 13:09:03 +00:00
|
|
|
Resolution records and helper targets for surviving values describe final
|
|
|
|
|
positions. Preserve the existing ability to record removed values.
|
|
|
|
|
`Resolution.EntryInfo` and `Resolution.IsExplicit` retain their existing fixed
|
|
|
|
|
entry-snapshot meaning. Numeric paths and helper targets are not automatically
|
|
|
|
|
rebound to an identity when the application later moves an element. A helper
|
|
|
|
|
assignment must still agree with the final value at its declared target.
|
|
|
|
|
|
|
|
|
|
Identity establishes correspondence internally; this feature adds no public
|
|
|
|
|
identity-addressed path or mutation-tracking API. An application that restructures
|
|
|
|
|
a list can record its container rather than maintain individual helper targets
|
|
|
|
|
through moves.
|
2026-11-02 12:42:25 +00:00
|
|
|
|
|
|
|
|
Preserve helper-value consistency checks, reason and dependency recording,
|
|
|
|
|
presence distinctions, detached query results, and independent-load ownership.
|
|
|
|
|
Fine-grained accepted changes must appear consistently in the returned values,
|
2026-11-02 13:09:03 +00:00
|
|
|
metadata, history, explanation, and sensitivity propagation. For eligible
|
|
|
|
|
registered collections, use the same entry-to-final correspondence for before
|
|
|
|
|
values, source attribution, input explicitness, and sensitivity. Do not attach
|
|
|
|
|
the previous occupant's evidence to a different record merely because it now
|
|
|
|
|
occupies that index. Identity matching alone does not make a newly added record
|
|
|
|
|
explicit input or give it an invented source.
|
|
|
|
|
|
|
|
|
|
A history query at a final record's position includes the matched record's
|
|
|
|
|
earlier evidence from its entry position. Preserve original source coordinates
|
|
|
|
|
and include the recorded collection operation explaining the move. Recorded
|
|
|
|
|
normalization and derivation retain their resolution origins; following prior
|
|
|
|
|
evidence does not erase the application operation. Keep removed evidence
|
|
|
|
|
available under the historical inspection contract.
|
|
|
|
|
|
|
|
|
|
Explicit structural records remain the explanation of application-owned list
|
|
|
|
|
restructuring. Dependency paths retain their existing recording semantics;
|
|
|
|
|
identity matching does not turn supplied paths into selectors that follow later
|
|
|
|
|
mutations. Check sensitivity propagation through both the matched evidence and
|
|
|
|
|
recorded dependencies, including moves and removed values. The proposal does not
|
|
|
|
|
reconstruct the callback's intermediate mutations.
|
2026-11-02 12:42:25 +00:00
|
|
|
|
|
|
|
|
Public effective-configuration diff output remains atomic for lists. Resolution
|
|
|
|
|
comparison may need a dedicated internal operation or comparison mode so that
|
|
|
|
|
its new behavior does not alter unrelated callers.
|
|
|
|
|
|
|
|
|
|
## Work needed from the baseline
|
|
|
|
|
|
|
|
|
|
- Trace resolution comparison, record coverage, helper verification, metadata
|
|
|
|
|
updates, history replay, and sensitivity handling to identify where each needs
|
|
|
|
|
finer sequence changes or explicit before/after correspondence.
|
|
|
|
|
- Add positional recursive comparison for ordinary equal-length lists and
|
|
|
|
|
identity-aware comparison for eligible registered lists, while retaining
|
|
|
|
|
structural coverage for the cases above.
|
|
|
|
|
- Reuse collection identity machinery and validate matching eligibility without
|
|
|
|
|
strengthening the existing final-only registration guarantee.
|
2026-11-02 13:09:03 +00:00
|
|
|
- Carry matched entry and final positions into resolution snapshots, metadata,
|
|
|
|
|
history, and sensitivity handling through the existing shared history model.
|
|
|
|
|
A coverage-only implementation does not complete this feature. Preserve
|
|
|
|
|
source coordinates and distinguish a record's lineage from the prior occupant
|
|
|
|
|
of its final position.
|
2026-11-02 12:42:25 +00:00
|
|
|
- Verify field edits, nested lists, zero/false/null values, multiple simultaneous
|
|
|
|
|
changes, length changes, reorders, identity changes, helper targets, and
|
|
|
|
|
metadata behavior across the public loading boundary. Include cases in which
|
|
|
|
|
a field record must not conceal another unrecorded change.
|
2026-11-02 13:09:03 +00:00
|
|
|
- Exercise moved records with distinct source evidence, explicitness, and
|
|
|
|
|
sensitivity; combined reorders and field edits; removed values; repaired entry
|
|
|
|
|
identities; and dynamic registration selectors. Check agreement between final
|
|
|
|
|
metadata and explanation while retaining entry-query behavior and atomic public
|
|
|
|
|
diff output. Review historical queries at removed and reused positions against
|
|
|
|
|
the existing historical-path contract.
|
2026-11-02 12:42:25 +00:00
|
|
|
- Update the resolution contract and relevant API comments when implemented.
|
2026-11-02 13:09:03 +00:00
|
|
|
Review ADR and policy follow-up for registered identity as the correspondence
|
|
|
|
|
used during resolution inspection, as well as the distinction between
|
|
|
|
|
resolution comparison and public diff presentation. Preserve accepted ADR
|
|
|
|
|
decision bodies.
|
2026-11-02 12:42:25 +00:00
|
|
|
|
|
|
|
|
Use identity lookup tables for expected linear matching work in collection size,
|
|
|
|
|
plus recursive value-comparison work. Preserve deterministic output and existing
|
|
|
|
|
equality semantics; avoid pairwise searches for matching records. This is a
|
|
|
|
|
design objective, not a new public performance guarantee.
|
|
|
|
|
|
|
|
|
|
No configuration syntax, dependency, release gate, or new mutation-history system
|
|
|
|
|
is proposed. Existing callbacks that record a whole list should continue to work.
|
2026-11-02 13:09:03 +00:00
|
|
|
The matching-eligibility and final-position-addressing decisions are settled and
|
|
|
|
|
integrated above.
|