Plan interface contract remediation
This commit is contained in:
239
docs/roadmap/interface-remediation.md
Normal file
239
docs/roadmap/interface-remediation.md
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
# Interface Contract Remediation Roadmap
|
||||||
|
|
||||||
|
Status: proposed target; implementation has not begun.
|
||||||
|
Planning baseline: `1f64673` (v0.10.2 implementation plus documentation changes).
|
||||||
|
|
||||||
|
This roadmap covers the four findings in the [interface audit](interfaces.md).
|
||||||
|
The audit owns reproductions, evidence, and finding numbers. This document owns
|
||||||
|
the combined target, required changes, and general dependency order. A subsequent
|
||||||
|
implementation plan should own concrete stages, private implementation choices,
|
||||||
|
validation commands, and progress.
|
||||||
|
|
||||||
|
## Purpose And Scope
|
||||||
|
|
||||||
|
Restore agreement between ConfigKit's presence, metadata, history, and diagnostic
|
||||||
|
interfaces under their existing contracts. The first three findings concern
|
||||||
|
semantic paths that share a JSON Pointer spelling; the fourth concerns a
|
||||||
|
composition claimant order that is lost during diagnostic canonicalization.
|
||||||
|
|
||||||
|
The [Path declarations](../../path.go), [metadata contract](../config.md#layered-metadata),
|
||||||
|
[resolution contract](../config.md#recorded-application-resolution), and
|
||||||
|
[strict-composition contract](../config.md#strict-document-composition) remain
|
||||||
|
authoritative. This work changes the implementation of those contracts without
|
||||||
|
selecting a new public API, path syntax, precedence rule, or configuration
|
||||||
|
restriction. The existing root-owned facade and engine responsibilities remain.
|
||||||
|
|
||||||
|
## Current Baseline And Targeted End State
|
||||||
|
|
||||||
|
An explicit mapping-key path and an explicit sequence-index path can both render
|
||||||
|
as `/shape/0`. `ParsePath` deliberately leaves numeric tokens contextual, whereas
|
||||||
|
`Keys` and `Index` preserve the caller's explicit choice. The engine already has
|
||||||
|
shape-aware effective/historical path indexes and a frozen resolution-entry view,
|
||||||
|
but some queries and metadata updates still use pointer spelling alone.
|
||||||
|
|
||||||
|
| Finding | Current discrepancy | Target |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 1: resolution presence | `IsExplicit` and `Default` consult string-keyed presence after schema validation, which cannot determine actual shape beneath dynamic values. | Presence decisions use the matching semantic path in the fixed resolution-entry state. |
|
||||||
|
| 2: effective explicitness | Resolution retains old field information when pointer spelling survives a container-shape change. | Input explicitness carries forward for the same semantic path; a newly introduced key or index does not inherit another path's explicitness. |
|
||||||
|
| 3: history attribution | A historical-shape check admits the query, then `History` returns the entire textual-pointer bucket. | Exact definitions and previous origins belong to the queried semantic path, with applicable ancestor operations retained. |
|
||||||
|
| 4: claimant order | Composition constructs related locations in assembly order, then canonicalization sorts them by location. | Structured and rendered composition conflicts preserve the selected claimant order. |
|
||||||
|
|
||||||
|
For example, if input contains `shape: {'0': old}` and recorded application
|
||||||
|
resolution changes `shape` to a sequence, the old key remains an input path in
|
||||||
|
the callback's entry view. The new index is effective in final metadata but was
|
||||||
|
not explicitly supplied by that input. Its history must not present the old key's
|
||||||
|
definition as its own. This distinction concerns container shape, not identity
|
||||||
|
tracking for records that move within a sequence.
|
||||||
|
|
||||||
|
## Required Changes
|
||||||
|
|
||||||
|
### 1. Use Entry-State Shape For Resolution Presence
|
||||||
|
|
||||||
|
In [resolution.go](../../internal/engine/resolution.go), align `IsExplicit` and
|
||||||
|
the explicitness decision in `Default` with the frozen, shape-aware entry metadata
|
||||||
|
already used by `InputInfo` in
|
||||||
|
[load_metadata.go](../../internal/engine/load_metadata.go).
|
||||||
|
|
||||||
|
Reuse the existing semantic-path matching machinery. A shared private lookup can
|
||||||
|
avoid divergent presence decisions without requiring every public query to have
|
||||||
|
identical error behavior. In particular:
|
||||||
|
|
||||||
|
- Preserve `IsExplicit`'s schema-path validation and its existing diagnostics.
|
||||||
|
`InputInfo` remains a query that returns false for missing or mismatched paths
|
||||||
|
without those diagnostics.
|
||||||
|
- Preserve `Default`'s helper-target validation, assignment, recording, and final
|
||||||
|
helper-value checks. Correct its presence gate; do not bypass the other checks.
|
||||||
|
- Use entry-state shape even after application code changes the working value.
|
||||||
|
Explicit false, zero, empty, and null retain their existing presence meaning.
|
||||||
|
- Keep callback lifetime and detached metadata behavior intact.
|
||||||
|
|
||||||
|
A shape mismatch beneath a schema-valid dynamic path must not become explicit
|
||||||
|
merely because its pointer spelling matches an input path. Contextually parsed
|
||||||
|
numeric tokens continue to resolve against the entry state's actual containers.
|
||||||
|
|
||||||
|
### 2. Preserve Explicitness By Semantic Path During Resolution
|
||||||
|
|
||||||
|
Update resolution metadata application and field refresh in
|
||||||
|
[load_metadata.go](../../internal/engine/load_metadata.go) to compare the relevant
|
||||||
|
entry and final shapes before reusing presence information.
|
||||||
|
|
||||||
|
The comparison must account for container kinds along the full path. A mapping
|
||||||
|
key becoming a sequence index at an ancestor can change a descendant's semantic
|
||||||
|
path even when its last segment and full pointer spelling stay unchanged.
|
||||||
|
|
||||||
|
Keep explicitness for matching surviving paths, including descendants covered by
|
||||||
|
an ancestor normalization record. New semantic paths introduced by resolution
|
||||||
|
remain non-explicit, and retain the applicable recorded resolution origin.
|
||||||
|
Removed paths leave effective metadata while remaining available to historical
|
||||||
|
queries under the existing contract. Do not clear every descendant's presence
|
||||||
|
just because its parent was normalized.
|
||||||
|
|
||||||
|
Use the existing before/after projection and entry metadata where possible.
|
||||||
|
This work does not require tracking every transient mutation inside a callback;
|
||||||
|
the existing net-change and coalesced-record model continues to apply.
|
||||||
|
|
||||||
|
### 3. Retain Semantic Attribution In History
|
||||||
|
|
||||||
|
Repair history event construction and selection across
|
||||||
|
[load_metadata.go](../../internal/engine/load_metadata.go),
|
||||||
|
[metadata.go](../../internal/engine/metadata.go), and their inheritance-history
|
||||||
|
integration. Finding 3's choice of internal evidence representation is discussed
|
||||||
|
under [Open Decisions](#open-decisions).
|
||||||
|
|
||||||
|
An explicit query should select definitions belonging to its semantic shape.
|
||||||
|
Ancestor replacement or removal operations can still affect that path and must
|
||||||
|
remain in its history where applicable. An ancestor operation's `Change.Path`
|
||||||
|
need not equal the queried descendant; this is existing behavior.
|
||||||
|
|
||||||
|
Retain enough evidence to distinguish the affected path from the operation's
|
||||||
|
reported path. Merely filtering current `Change.Path` values is insufficient:
|
||||||
|
some construction paths recreate parsed tokens from strings, and ancestor events
|
||||||
|
intentionally name another path. Review event construction for defaults, input
|
||||||
|
definitions, precedence operations, inheritance, CLI, and resolution to identify
|
||||||
|
where these distinctions can be lost. Change the affected owners without
|
||||||
|
duplicating the entire loading lifecycle.
|
||||||
|
|
||||||
|
Select or reconstruct `Previous` origins using the relevant semantic history.
|
||||||
|
Review `historyWithInheritance` as well as stored events: filtering out a false
|
||||||
|
definition while retaining it as the previous origin would leave the attribution
|
||||||
|
defect unresolved. Preserve chronology and genuine ancestor effects, including
|
||||||
|
history of removed paths. A path that never existed does not acquire history
|
||||||
|
solely because an ancestor changed.
|
||||||
|
|
||||||
|
Retain the existing contextual behavior of `ParsePath` across historical shapes
|
||||||
|
and the one-entry-per-canonical-pointer enumeration of `HistoricalPaths`. Neither
|
||||||
|
needs a public redesign. `History` and `Explain` should agree on which path a
|
||||||
|
definition belongs to and what affected it; their different record forms do not
|
||||||
|
need to become identical lists.
|
||||||
|
|
||||||
|
Reuse existing evidence where practical. Any additional retained evidence must
|
||||||
|
respect existing ownership, confidentiality, and applicable accounting; avoid
|
||||||
|
retaining extra document trees merely to identify a path's shape. This is not a
|
||||||
|
new memory quota or a general history-storage redesign.
|
||||||
|
|
||||||
|
### 4. Preserve Composition Claimant Order Through Diagnostics
|
||||||
|
|
||||||
|
Keep the order produced by `compositionConflictDiagnostic` in
|
||||||
|
[compose.go](../../internal/engine/compose.go) through accumulation, `LoadError`,
|
||||||
|
facade conversion, and `WriteDiagnostics` in
|
||||||
|
[diagnostic.go](../../internal/engine/diagnostic.go).
|
||||||
|
|
||||||
|
Prefer a narrow internal canonicalization rule based on the diagnostic semantics
|
||||||
|
already carried across these boundaries. Identify the composition diagnostic
|
||||||
|
producers governed by the claimant-order contract rather than changing all
|
||||||
|
related-location lists. Preserve canonical ordering of diagnostic records and
|
||||||
|
the existing sorting of related locations for other diagnostics.
|
||||||
|
|
||||||
|
The root renderer converts public diagnostic records back to engine records and
|
||||||
|
canonicalizes them again. An engine-only marker discarded by facade conversion
|
||||||
|
would therefore be insufficient. No public ordering option is needed for this
|
||||||
|
correction. Keep primary selection, conflict detection, acquisition order, and
|
||||||
|
dependency-ready scheduling unchanged.
|
||||||
|
|
||||||
|
## General Implementation Order
|
||||||
|
|
||||||
|
1. Resolve the history-evidence decision and prepare focused public regression
|
||||||
|
cases from the audit. Establish expected semantic-path behavior using existing
|
||||||
|
contracts and shared path-matching helpers.
|
||||||
|
2. Repair entry-state presence queries and `Default` (finding 1). This is a local
|
||||||
|
correction that can proceed independently of the larger history work.
|
||||||
|
3. Repair final explicitness preservation (finding 2), retaining the shape evidence
|
||||||
|
needed by metadata updates. Coordinate this work with history construction so
|
||||||
|
the two fixes do not introduce competing definitions of path identity.
|
||||||
|
4. Repair historical definitions and previous-origin attribution (finding 3),
|
||||||
|
then check their interaction with final metadata and explanation. A successful
|
||||||
|
presence fix alone does not establish correct history.
|
||||||
|
5. Complete composition diagnostic ordering (finding 4) as an independent change;
|
||||||
|
it may proceed alongside the path work. Check rendering as well as the
|
||||||
|
structured error before considering it complete.
|
||||||
|
6. Review the integrated result, run applicable development checks, and update
|
||||||
|
affected implementation documentation and finding status.
|
||||||
|
|
||||||
|
These are high-level dependencies, not a requirement to combine unrelated fixes
|
||||||
|
in one commit. The implementation plan should select concrete edits and test
|
||||||
|
commands while keeping the four findings individually traceable.
|
||||||
|
|
||||||
|
## Validation And Completion
|
||||||
|
|
||||||
|
Follow the [testing policy](../policy/testing.md) and use the
|
||||||
|
[development guide](../development.md#baseline-validation) for checks. Extend
|
||||||
|
existing test owners rather than recreating full internal matrices at the facade.
|
||||||
|
The focused cases should cover:
|
||||||
|
|
||||||
|
- Both mapping-key/index mismatch directions beneath dynamic values, including
|
||||||
|
explicit paths and contextual parsed pointers. Check callback presence and
|
||||||
|
`Default` behavior, including the fixed entry view after working-value changes.
|
||||||
|
- Both container-shape transitions during recorded resolution, a descendant
|
||||||
|
affected by an ancestor shape change, and a same-shape normalization control.
|
||||||
|
Check explicitness and recorded origins through final metadata and explanation.
|
||||||
|
- Shape-changing CLI and resolution history, checking exact definitions,
|
||||||
|
`Previous`, and genuine ancestor replacement/removal. Include contextual
|
||||||
|
enumeration/query coverage and preserve existing inheritance and overlay cases;
|
||||||
|
add another case only where those integrations expose an unprotected risk.
|
||||||
|
- A composition conflict whose acquisition and assembly orders differ, using the
|
||||||
|
audit fixture. Check both published diagnostics and rendered related-location
|
||||||
|
order, while retaining coverage for ordinary diagnostic canonicalization.
|
||||||
|
|
||||||
|
Retain existing checks for invalid schema paths, callback lifetime, detached
|
||||||
|
records, safe historical rendering, and default/null presence semantics. Test
|
||||||
|
observable behavior rather than a particular private index or number of copies.
|
||||||
|
No benchmark campaign, broader audit, or new release gate is part of this roadmap.
|
||||||
|
|
||||||
|
Completion requires all four findings to have implementation and regression
|
||||||
|
evidence, agreement at the affected public boundaries, and appropriate development
|
||||||
|
checks. The audit's previous passing suite is not validation of these future
|
||||||
|
changes. Scope here does not promise identical output from different inspection
|
||||||
|
interfaces or a universal guarantee about unrelated path-processing code.
|
||||||
|
|
||||||
|
## Open Decisions
|
||||||
|
|
||||||
|
### OD-1. Where Should Shape-Specific History Evidence Live?
|
||||||
|
|
||||||
|
**Recommendation:** retain semantic-shape evidence with the internal history
|
||||||
|
events as they are built, using existing concrete paths and document evidence.
|
||||||
|
Select applicable events and previous origins for each query before producing
|
||||||
|
detached public `Change` records. The exact representation belongs in the
|
||||||
|
implementation plan.
|
||||||
|
|
||||||
|
This fits the existing stored-history owner and keeps metadata queries independent
|
||||||
|
of explanation rendering. It also permits a focused correction where event
|
||||||
|
construction currently loses shape. The cost is carrying and preserving evidence
|
||||||
|
across several event producers; the design must keep it consistent with replay
|
||||||
|
and avoid redundant retained trees. The amount of new state should follow what
|
||||||
|
existing evidence cannot supply, not an assumed new event framework.
|
||||||
|
|
||||||
|
**Viable alternative:** derive the relevant metadata history from existing
|
||||||
|
shape-aware replay evidence, sharing a private selection operation with
|
||||||
|
explanation where their semantics agree. This can reduce duplicated attribution
|
||||||
|
logic and reuse evidence that already handles the reported transition correctly.
|
||||||
|
However, metadata history includes default/definition and inheritance records
|
||||||
|
whose presentation differs from explanation. This option needs a clear account
|
||||||
|
of those differences, query cost, and evidence ownership; routing `History`
|
||||||
|
through the public explanation representation would introduce unnecessary
|
||||||
|
coupling.
|
||||||
|
|
||||||
|
Choose between these approaches after a focused comparison of the affected event
|
||||||
|
producers and replay coverage, before finalizing the implementation plan. The
|
||||||
|
decision concerns private ownership and implementation cost, not public path or
|
||||||
|
history semantics. No other significant product or architectural decision is
|
||||||
|
open for this scope.
|
||||||
@@ -5,6 +5,10 @@ Reviewed baseline: `01b08e76a3fecc22c4414437bfeaa07b4b7a85aa`.
|
|||||||
Status: four open findings. This is an audit record, not an implementation plan.
|
Status: four open findings. This is an audit record, not an implementation plan.
|
||||||
No production code or repository tests were changed.
|
No production code or repository tests were changed.
|
||||||
|
|
||||||
|
The [interface remediation roadmap](interface-remediation.md) describes the
|
||||||
|
proposed target, required changes, dependencies, and remaining design decision.
|
||||||
|
This audit retains the findings and reproduction evidence.
|
||||||
|
|
||||||
## Scope and evidence
|
## Scope and evidence
|
||||||
|
|
||||||
Reviewed the root-owned public facade and its conversions, engine loading and
|
Reviewed the root-owned public facade and its conversions, engine loading and
|
||||||
|
|||||||
Reference in New Issue
Block a user