173 lines
8.8 KiB
Markdown
173 lines
8.8 KiB
Markdown
# Field-level resolution auditing for sequences
|
|
|
|
Status: proposed; not implemented. This roadmap records the proposed contract
|
|
for reviewing changes made during application resolution. A staged implementation
|
|
plan can follow once the remaining decisions are settled.
|
|
|
|
## 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.
|
|
|
|
Use explicit collection identity where available to recognize records across
|
|
positions. For other lists, compare positions as addresses without inferring
|
|
which application object an element represents.
|
|
|
|
## 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.
|
|
|
|
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.
|
|
|
|
### 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.
|
|
|
|
This identity-aware comparison applies to resolution auditing; it does not
|
|
introduce a new collection merge operation or change whole-record `upsert`.
|
|
YAML-only identity declarations do not acquire a new final-resolution guarantee.
|
|
|
|
### 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.
|
|
|
|
Swapping the two records also changes their order, so recording only one path
|
|
does not suffice. Recording `/resources` covers that structural operation and
|
|
any field edits within the list. Matching uses the declared identity rather than
|
|
trying to deduce correspondence from similar field values.
|
|
|
|
### Recording, inspection, and ownership
|
|
|
|
The recommended addressing convention is for resolution records and helper
|
|
targets to describe final positions. `Resolution.EntryInfo` retains its existing
|
|
fixed entry-snapshot meaning. Numeric paths are not automatically rebound to an
|
|
identity when the application later moves an element.
|
|
|
|
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,
|
|
metadata, history, explanation, and sensitivity propagation. Do not attach an
|
|
old element's attribution to a different element merely because it occupies the
|
|
same index afterward. Explicit structural records remain the explanation of
|
|
application-owned list restructuring; the proposal does not reconstruct the
|
|
callback's intermediate mutations.
|
|
|
|
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.
|
|
- 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.
|
|
- Update the resolution contract and relevant API comments when implemented.
|
|
Review whether an ADR is warranted for the distinction between resolution
|
|
comparison and public diff presentation; preserve accepted ADR decision bodies.
|
|
|
|
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.
|
|
|
|
## Open Decisions
|
|
|
|
### Eligibility when the entry snapshot has invalid registered identities
|
|
|
|
**Recommendation:** use identity matching when both snapshots provide valid,
|
|
unique identities under the applicable registration. Otherwise use conservative
|
|
whole-list auditing for that collection and preserve existing final validation.
|
|
This allows resolution to repair invalid entry values, as the current lifecycle
|
|
permits, without inventing item correspondence or rejecting them earlier.
|
|
|
|
**Alternative:** use positional comparison when identities cannot be matched.
|
|
This permits finer recording of repairs but makes a registered collection switch
|
|
between identity and positional semantics depending on its contents. Consider
|
|
whether that flexibility justifies the additional explanation and metadata cases.
|
|
|
|
### Final-position addressing for records and helper targets
|
|
|
|
**Recommendation:** adopt the final-position convention described above, retaining
|
|
entry positions for `EntryInfo`. This fits the existing verification of helper
|
|
values against final targets and avoids introducing identity selectors into the
|
|
public path API. Applications that restructure a list can record its container.
|
|
|
|
**Alternative:** add explicit identity-based resolution targets that follow a
|
|
record across moves. This could simplify callbacks that edit and then reorder
|
|
records, but adds a public addressing mechanism and requires decisions about
|
|
removed records, dependencies, nested collections, and inspection. It is a larger
|
|
extension than the field-level auditing improvement motivating this proposal.
|