338 lines
22 KiB
Markdown
338 lines
22 KiB
Markdown
# Interface Contract Remediation Roadmap
|
|
|
|
Status: implemented. The shared history owner and all four audit corrections
|
|
are complete; validation is recorded in the completed plan at
|
|
`07e4ae5:docs/roadmap/implementation.md`.
|
|
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 design intent. The completed plan at
|
|
`07e4ae5:docs/roadmap/implementation.md` records 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.
|
|
|
|
Build one shared internal history model from which metadata history and
|
|
explanation are derived. It will own the historical evidence and semantic
|
|
attribution used by both views, replacing their independent reconstruction of
|
|
that attribution. This is the selected resolution of OD-1.
|
|
|
|
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.
|
|
|
|
History currently reconstructs definition and previous-origin records, while
|
|
explanation replays source contributions, inheritance, decoding checkpoints, CLI
|
|
assignments, and grouped resolution snapshots. Neither public representation
|
|
contains everything needed by the other. The target is shared underlying evidence
|
|
with distinct projections for each interface, preserving their existing behavior.
|
|
|
|
| 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; explanation selects evidence separately. | Both interfaces derive attribution from one shared history model, selecting exact definitions, previous origins, and applicable ancestor operations by semantic path. |
|
|
| 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. Build A Shared Internal History Model
|
|
|
|
#### Evidence And Ownership
|
|
|
|
Give historical evidence one private owner in the engine. Composition,
|
|
inheritance, decoding, CLI assignment, and resolution continue to perform their
|
|
existing work and supply evidence to that owner. The model does not drive a
|
|
second configuration load or repeat application callbacks.
|
|
|
|
Organize the evidence into ordered groups with the phase boundaries required by
|
|
the existing inspection contracts:
|
|
|
|
| Evidence group | Information to preserve |
|
|
| --- | --- |
|
|
| Default baseline | Canonical typed application defaults or Go-zero values and their default attribution. |
|
|
| Source contributions | Underlay, main-tier, and selected-overlay contributions in their existing order; exact definitions, composite origins, displaced definitions, raw policy-validated values, and sequence-position projections. |
|
|
| Inheritance | Parent transfers, child contributions including equal-valued replacements, controlling directives and their consumption, identity-field preservation, and contribution origins/dependencies. |
|
|
| Typed checkpoints | Pre-CLI and post-CLI typed projections and attribution needed to reconcile retained defaults and raw contributions with decoded values. |
|
|
| CLI contributions | Explicit assignments, including equal-valued assignments, raw supplied representation, source attribution, and affected paths. |
|
|
| Resolution | Original recorded operations, reasons, dependencies, and before/after evidence, with the grouping needed to explain overlapping records as net subtree changes. |
|
|
|
|
The logical order remains baseline, source contributions, inheritance, pre-CLI
|
|
decoding reconciliation, CLI contributions, post-CLI typed reconciliation, then
|
|
recorded resolution. The two typed checkpoints belong on their respective sides
|
|
of the CLI contribution phase.
|
|
|
|
The groups describe logical inspection chronology, not a transcript of every
|
|
physical mutation. Retain the existing net-change and coalescing rules for
|
|
resolution. Preserve superseded raw input that need not decode into the final
|
|
application type; typed checkpoints do not replace that evidence.
|
|
|
|
Use a small set of ordinary private structures and existing document/path
|
|
mechanisms. Exact types, indexes, and storage layout belong in the implementation
|
|
plan. Complete evidence means enough for the existing history, explanation, and
|
|
internal attribution consumers; it does not add persistence, a public event API,
|
|
or recording of transient application effects.
|
|
|
|
#### Shared Selection And Attribution
|
|
|
|
Retain concrete semantic-path evidence when it is available. A query over the
|
|
model must distinguish exact definitions from operations affecting an ancestor
|
|
or descendant, determine the relevant previous origins, and apply sequence
|
|
contribution coordinates at their assigned positions.
|
|
|
|
The affected semantic path and the operation's reported path are distinct facts.
|
|
For example, a replacement at `/shape` can remove a mapping key and introduce a
|
|
sequence index with the same pointer spelling. Preserve enough before/after shape
|
|
evidence to answer each explicit query correctly. Merely filtering `Change.Path`
|
|
after concrete segment kinds have been discarded is insufficient.
|
|
|
|
Make this selection and attribution common to both projections, including
|
|
inheritance integration and `Previous` reconstruction. A false definition must
|
|
not survive as a previous origin after it has been excluded from the query's
|
|
history. Preserve genuine ancestor replacement/removal effects and chronology.
|
|
A path that never existed does not acquire history solely because an ancestor
|
|
changed.
|
|
|
|
Keep the contextual meaning of parsed numeric tokens across historical states
|
|
and the one-entry-per-canonical-pointer enumeration of `HistoricalPaths`.
|
|
Historical-path membership and enumeration must also derive from the shared
|
|
evidence. Preserve the existing inventory of default-only paths, displaced
|
|
descendants, consumed inheritance controls, and historical mapping-key and
|
|
sequence-index shapes, including evidence that a public projection coalesces or
|
|
omits. Indexes and compact derived records may remain, but their membership and
|
|
attribution must come from that evidence. Metadata queries should not require
|
|
full configuration replay or YAML rendering to obtain definition and origin
|
|
records.
|
|
|
|
#### Distinct Public Projections
|
|
|
|
`Metadata.History` produces detached definition, inheritance, and resolution
|
|
records from the model. `Explain` reconstructs and safely renders values from
|
|
the same evidence, retaining its existing decoding and grouping behavior. Neither
|
|
public API should be implemented by translating the other's public result.
|
|
|
|
Preserve these intentional differences:
|
|
|
|
| Case | Metadata history | Explanation |
|
|
| --- | --- | --- |
|
|
| Several strict contributions to a mapping | Composite definition/origin information. | Each contribution's effect on the mapping. |
|
|
| Raw input differs from canonical typed values | Definition and attribution records. | The applicable decoding reconciliation transition. |
|
|
| Overlapping resolution records | Specific recorded attribution under the existing coverage rules. | One net subtree event with combined reasons and dependencies. |
|
|
| Equal-valued input or inheritance operations | Explicit definition and operation attribution remains visible. | Relevant operations remain visible even when before and after values are equal. |
|
|
|
|
Agreement means consistent semantic identity and attribution, not identical
|
|
record lists. Retain original resolution records as evidence from which net
|
|
replay groups are derived; a grouped explanation alone loses information needed
|
|
by metadata.
|
|
|
|
#### Replace Existing Attribution Machinery
|
|
|
|
The common model must replace independent historical attribution, rather than
|
|
become another representation alongside it. The completed plan at
|
|
`07e4ae5:docs/roadmap/implementation.md` mapped the following owners to their
|
|
replacements:
|
|
|
|
| Current owner | Required change |
|
|
| --- | --- |
|
|
| History-building portions of `metadataFromDecoded`, `applyOverlayMetadata`, and `appendPostDecodeCLIHistory` in [load_metadata.go](../../internal/engine/load_metadata.go) and [override_context.go](../../internal/engine/override_context.go) | Produce or project shared evidence instead of independently reconstructing pointer-keyed histories. Retain effective-field construction where it is still needed. |
|
|
| Resolution-history construction in `applyResolutionMetadata` and `appendResolutionDescendantHistory` in [load_metadata.go](../../internal/engine/load_metadata.go) | Derive resolution records and previous origins through common semantic selection and attribution. Retain the effective-field updates still needed by final metadata. |
|
|
| Historical-path inventory in `finalizeMetadataPaths` in [metadata.go](../../internal/engine/metadata.go) | Derive historical membership, shape indexes, and enumeration from the shared evidence while preserving the existing path inventory. Retain effective-path indexing where needed. |
|
|
| Previous-origin reconstruction and inheritance insertion in [inheritance_history.go](../../internal/engine/inheritance_history.go) | Use common semantic selection and attribution while preserving the existing inheritance projection rules. |
|
|
| Relevance and chronology orchestration in [explain.go](../../internal/engine/explain.go) | Consume the shared model; retain useful value reconstruction and redaction helpers. |
|
|
| Separate published inheritance evidence for metadata and explanation in [load_result.go](../../internal/engine/load_result.go) | Consolidate under the frozen history owner where independent copies are unnecessary; keep required output detachment. |
|
|
| Original and coalesced resolution evidence in [history.go](../../internal/engine/history.go) | Own original records in the model and derive the replay grouping without losing specific attribution or maintaining a second independently interpreted history. |
|
|
|
|
Existing source, sequence-combination, and inheritance evidence producers may
|
|
remain useful. Their algorithms need not be rewritten merely to contribute to
|
|
the model. Likewise, final effective fields, the frozen resolution-entry view,
|
|
source records, and final configuration snapshots retain their responsibilities.
|
|
Findings 1 and 2 still require explicit presence corrections.
|
|
|
|
#### Other Consumers, Lifetime, And Resource Accounting
|
|
|
|
Move historical dependency reads in
|
|
[secret classification](../../internal/engine/secret.go) and historical location
|
|
fallbacks in [override_context.go](../../internal/engine/override_context.go) to
|
|
the shared evidence. Do not retain the old history machinery solely for those
|
|
readers. Preserve existing sensitivity propagation and historical classification;
|
|
correcting attribution does not authorize narrowing redaction for paths that
|
|
share a pointer spelling.
|
|
|
|
Evidence must be available at the lifecycle stages where these internal readers
|
|
need it, including diagnostic attribution before publication. Keep the callback's
|
|
entry metadata fixed while later resolution evidence is added. Finalize the
|
|
model for published queries without introducing lazy mutation of shared state.
|
|
Private frozen evidence may be shared by the two views while their returned
|
|
records remain detached under the existing ownership contract.
|
|
|
|
Reuse retained trees and existing lookup machinery where practical. Review
|
|
retained-history accounting against the resulting ownership graph, including
|
|
typed checkpoints and inheritance/resolution evidence. The model does not require
|
|
a full document copy per event, a new quota, or a new concurrency guarantee.
|
|
Reduced duplication is a design benefit to evaluate, not a promised memory or
|
|
speed improvement.
|
|
|
|
Preserve final retained-history budget admission before effective secret lookup
|
|
and application validation. Do not defer that check to publication or a later
|
|
query. Count the resulting retained ownership graph; tests should exercise its
|
|
budget boundary and failure timing without requiring obsolete duplicate copies
|
|
or the old representation's exact node totals.
|
|
|
|
### 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.
|
|
|
|
## 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, including historical paths whose evidence is
|
|
coalesced or omitted by a public projection. Preserve existing inheritance and
|
|
overlay cases; add another case only where those integrations expose an
|
|
unprotected risk.
|
|
- Shared-model projections across composite mapping origins, typed decoding and
|
|
default reconciliation, equal-valued writes, inheritance controls/transfers,
|
|
mapped sequence replacements and removed descendants, and overlapping
|
|
resolution records. Reuse existing cases where they protect those semantics;
|
|
check the intentional differences between the public projections as well as
|
|
agreement on attribution.
|
|
- Historical sensitivity/dependency propagation, diagnostic source-location
|
|
fallback, detached returned records, and complete retained-history accounting
|
|
under the new ownership graph. Preserve budget rejection before effective
|
|
secret lookup and application validation. Use small controlled budget cases
|
|
and existing disclosure fixtures rather than memory or timing thresholds.
|
|
- 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, appropriate development checks, and both history interfaces deriving
|
|
semantic attribution from the shared model. Review the final producer/consumer
|
|
map to confirm superseded independent reconstruction has been removed,
|
|
historical-path discovery derives from shared evidence, and internal attribution
|
|
readers have migrated. Passing the four defect regressions while retaining the
|
|
old parallel attribution machinery is not completion.
|
|
|
|
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.
|
|
|
|
## Decision Status
|
|
|
|
OD-1 is settled: build one shared internal history model with distinct metadata
|
|
and explanation projections. The design investigation found that both views
|
|
require evidence the other's public representation does not preserve. Shared
|
|
ownership can replace independent attribution and redundant retained evidence,
|
|
while keeping the current lifecycle and public contracts.
|
|
|
|
The narrower repair of stored history events would leave the separate replay
|
|
attribution owner in place. Promoting current explanation output to authoritative
|
|
history would lose composite-definition and specific resolution information.
|
|
The selected common underlying model addresses both concerns. This accepts a
|
|
larger internal refactor than a local shape-filtering fix.
|
|
|
|
No significant open decision remains at roadmap level. Private representation,
|
|
indexing, and helper choices are recorded in the completed plan at
|
|
`07e4ae5:docs/roadmap/implementation.md`.
|
|
This roadmap does not revise accepted ADRs.
|