From 21c9bc0ad0315c4ac96268b487a52376558a18d7 Mon Sep 17 00:00:00 2001 From: Eric Rakestraw Date: Tue, 27 Oct 2026 19:15:40 +0000 Subject: [PATCH] Retire completed roadmaps before the next release --- docs/development.md | 23 +- docs/roadmap/api.md | 282 --------- docs/roadmap/audit.md | 201 ------- docs/roadmap/future.md | 27 +- docs/roadmap/implementation.md | 498 ---------------- docs/roadmap/interface-remediation.md | 337 ----------- docs/roadmap/interfaces.md | 213 ------- docs/roadmap/internal-boundaries.md | 186 ------ docs/roadmap/remediation.md | 523 ----------------- docs/roadmap/review.md | 793 -------------------------- docs/roadmap/roadmap.md | 276 --------- 11 files changed, 39 insertions(+), 3320 deletions(-) delete mode 100644 docs/roadmap/api.md delete mode 100644 docs/roadmap/audit.md delete mode 100644 docs/roadmap/implementation.md delete mode 100644 docs/roadmap/interface-remediation.md delete mode 100644 docs/roadmap/interfaces.md delete mode 100644 docs/roadmap/internal-boundaries.md delete mode 100644 docs/roadmap/remediation.md delete mode 100644 docs/roadmap/review.md delete mode 100644 docs/roadmap/roadmap.md diff --git a/docs/development.md b/docs/development.md index 11c50ec..25d6953 100644 --- a/docs/development.md +++ b/docs/development.md @@ -43,7 +43,7 @@ public behavior. | Shared source-version checks | [Configuration contract](config.md#shared-source-version-checks), [ADR 0013](adr/0013-shared-source-version-checks.md), and the version/source-version tests below | | Documentation changes | The owning document | | Licensing, dependencies, attribution, or release-document changes | [Project grant](../COPYRIGHT) and [attribution](oss/attribution.md); release changes also follow the release procedure | -| Architecture decisions or future work | [Future features](roadmap/future.md), [remediation roadmap](roadmap/roadmap.md), and relevant ADRs listed below | +| Architecture decisions or future work | [Future features](roadmap/future.md) and relevant ADRs listed below | | Code and test changes | The owning contract and tests from the map below | | Examples or copyable assets | The owning implemented contract and [consumer adoption guide](consumers/README.md) | | Release preparation | [Release procedure](release.md) | @@ -199,8 +199,25 @@ git show 34cbc0d:docs/roadmap/remediation.md ``` Potential enhancements are tracked in [future features](roadmap/future.md). -The completed audit remediation's scope is recorded in the -[remediation roadmap](roadmap/roadmap.md). The consolidated audit, including +The completed audit-remediation, internal-boundary simplification, shared-history, +and public API cleanup roadmaps, reviews, and resolved findings are preserved at +`68400ff` under `docs/roadmap/`. For example: + +```sh +git show 68400ff:docs/roadmap/api.md +git show 68400ff:docs/roadmap/remediation.md +``` + +Their earlier completed implementation plans are preserved at +`bb5540b:docs/roadmap/implementation.md` (audit remediation), +`f6d54a0:docs/roadmap/implementation.md` (internal-boundary simplification), and +`07e4ae5:docs/roadmap/implementation.md` (shared history). The plan at +`68400ff:docs/roadmap/implementation.md` records the public API cleanup. +Current behavior and migration information belong to the +[configuration contract](config.md), public declaration comments, and +[consumer guide](consumers/README.md#pre-v1-public-api-cleanup). + +The consolidated audit, including findings, evidence, and settled scope decisions, is preserved at `453da68:docs/roadmap/audit.md`; the original nine reports are preserved at `ecd5d5d` under `docs/roadmap/`. Retrieve the consolidated record with: diff --git a/docs/roadmap/api.md b/docs/roadmap/api.md deleted file mode 100644 index 1dfc912..0000000 --- a/docs/roadmap/api.md +++ /dev/null @@ -1,282 +0,0 @@ -# Pre-v1 Public API Cleanup Roadmap - -Status: implemented. The accepted API changes and integrated validation are -recorded in the [implementation plan](implementation.md). -Planning baseline: `7548fa1` (accepted decisions, with the v0.10.2 implementation -and subsequent interface/history remediation already present). - -This roadmap owns the targeted end state, required changes, and completion -criteria. The [implementation plan](implementation.md) owns concrete edits, -sequential stages, validation commands, and progress tracking. The -[preliminary review](review.md) retains the API catalog and original proposals; -the [additional review](audit.md) retains -its evidence and alternatives. Proposal IDs below provide traceability. - -## Purpose And Scope - -Make the public API clearer before v1.0.0 through the accepted renames, narrower -recording methods, redundant-state removal, and direct source lookups. Preserve -existing configuration behavior except for the selected public replacements, -removals, and observable string changes. The root package remains the supported -facade; private engine structure can evolve within that boundary. - -This is a breaking pre-v1 cleanup, not a compatibility-shim rollout. The renamed -or replaced public declarations give way to their selected forms. Existing Go -declaration comments and the [configuration contract](../config.md) continue to -own behavior that this roadmap does not change. No redesign of loading, -composition, history ownership, or YAML syntax is selected. - -## Current Baseline - -- [lifecycle.go](../../lifecycle.go), [records.go](../../records.go), - [discovery.go](../../discovery.go), and [secrets.go](../../secrets.go) expose - the old declarations listed below. There are corresponding engine declarations - and facade conversions, including [conversion.go](../../conversion.go). -- `Resolution.Record` accepts the inspection `ChangeKind` type, then rejects - kinds other than derived-default and normalization during loading. The generic - assignment helpers already select their own supported intent. -- `ImportSelection` represents only empty selections, but still carries a - single-valued outcome. Source records expose version and destination evidence - through names that do not fully describe that evidence. -- `Resolution.Source(id)` already performs passive, detached lookup during a - callback. `Result` and `LoadError` expose ordered `Sources()` lists but no - direct lookup. Failure records contain only the evidence available at failure. -- The shared history implementation and its admission boundary are already in - place. Provenance still uses `"base"`, and retained-history failures still use - `"explain.limit_exceeded"`. -- Current production-reference checks find only declarations for - `CodeOptionsUnavailable`, and the kind guard in `resolutionState.addRecord` - produces `CodeResolveInvalidKind`. Their removal requires the checks described - below, rather than inventing a new supported use for either code. - -## Targeted Public API - -The following table is the complete accepted change set. Unless stated otherwise, -renames retain the existing signature/type and behavior. Internal names are not -part of this public replacement list. - -| Review item | Decision | -| --- | --- | -| D1 | Rename `Resolution.InputInfo` to `EntryInfo`, describing the fixed resolution-entry metadata view, including retained defaults and CLI assignments. Preserve its return type and lookup behavior. | -| D2 | Rename `Definition.Secret` to `Sensitive`. Preserve classification behavior, the `configkit:"secret"` tag, and typed `Secret` references. | -| D3 | Rename `SourceVersionModeDefault` to `SourceVersionModeMainOnly`, `SourceVersionCheck` to `SourceVersionEvidence`, and `SourceInfo.VersionCheck` to `VersionEvidence`. Retain `SourceVersionModeIfPresent` and `SourceVersionModeRequired`. | -| D4 | Rename `ImportSelection` to `EmptyImportSelection` and `SourceInfo.ImportSelections` to `EmptyImportSelections`. Retain path, location, and selector; remove `Outcome`, `ImportSelectionOutcome`, and `ImportSelectionNone`. | -| D5 | Rename `SourceInfo.DestinationUnresolved` to `DestinationIsFallback`, preserving its existing meaning and destination coordinates. | -| D6 (including N4) | Replace `Resolution.Record` with the two intent-specific methods below and remove `CodeResolveInvalidKind` once its public trigger is eliminated. Retain `ChangeKind` and its inspection constants. | -| D7 | Rename `LoadSecretsDir` to `LoadSecretsIntoEnvironment`, making process-environment mutation explicit. Preserve its signature apart from the name, report-plus-error behavior, and existing bootstrap semantics. | -| D8 | Remove `CodeOptionsUnavailable` after confirming there is no intentional supported role. Do not introduce a failure merely to give the code a producer; a concrete reason to retain it should be brought back for discussion. | -| A1 (including N5) | Add `Result.Source(id)` and `LoadError.Source(id)` for direct lookup of source evidence on successful and failed loads, alongside their existing `Sources()` enumeration. Keep source evidence distinct from passive resource access through `SourceContext(id)`. | -| N1 | Rename `Paths(paths ...string) Search` to `SearchPaths(paths ...string) Search`, replacing the old name outright. Preserve literal file candidates, ordering, deferred acquisition, and selection/failure behavior. Leave semantic `Path` APIs and `Searches` unchanged. | -| N2 | Replace `LayerBase = "base"` with `LayerMain = "main"`, without a compatibility alias. Update Go references and affected string comparisons, serialization, and rendered labels. Preserve the existing main-tier category, imported contributions' owning tiers, precedence, and acquisition order. | -| N3 | Replace `CodeExplainLimitExceeded = "explain.limit_exceeded"` with `CodeHistoryLimitExceeded = "history.limit_exceeded"`, without a compatibility alias. Update Go references and affected diagnostic-code consumers. Preserve the existing quota, retained-node accounting, failure conditions, diagnostic locations, and admission timing before effective secret lookup and application validation. | -| N6 | Rename `ImportDefaults.Follow` to `FollowSymlinks` and `ImportDefaults.Hidden` to `IncludeHidden`, replacing the old fields outright. Preserve `*bool` types, nil defaulting, explicit true/false, declaration overrides, traversal behavior, and search scope. Keep YAML keys `follow` and `hidden`, and Go fields `Required`, `Recursive`, and `Pattern`, unchanged. | - -### Recording Methods (D6) - -The selected signatures are: - -```go -func (r *Resolution) RecordDerivedDefault(path Path, reason string, dependencies ...Path) -func (r *Resolution) RecordNormalization(path Path, reason string, dependencies ...Path) -``` - -Both record intent for work already performed by application code. They do not -assign values; `RecordDerivedDefault` does not check absence or suppress work on -explicit input. Keep `Default` and `Normalize` as the assignment-and-recording -helpers. Preserve path/reason/dependency validation, container coverage, -coalescing, net-change auditing, and callback lifetime. Replace the old `Record` -method outright rather than retain parallel public recording interfaces. - -Include N4's diagnostic cleanup in this change: confirm that no supported public -input can still produce `resolve.invalid_kind`, then remove `CodeResolveInvalidKind` -without a replacement or compatibility alias. Preserve other recording diagnostics. -Internal defensive checks remain an implementation choice. If another legitimate -public trigger is found, bring it back for discussion rather than silently -reclassifying the failure. - -### Version Policy And Evidence (D3) - -`MainOnly` remains the zero mode and does not enable checking without a configured -version rule. The broader modes retain their requirement for a usable rule. -Document the modes together, preserving these distinctions: - -- `MainOnly` retains the existing main-version check at its effective path. - Broader modes inspect each participating document in source-local coordinates - before placement and composition. -- `IfPresent` passes an absent main declaration to the application checker, while - absent non-main declarations skip the checker. `Required` rejects missing - declarations in participating documents. Existing `NoSource` behavior remains. -- Version evidence describes observed declarations or absence, not checker - invocation or acceptance. Preserve nil-versus-observed-absence meaning, - explicit null, locations, consumption, and redaction. No acceptance flag is added. - -### Direct Source Lookup (A1) - -```go -func (r *Result[T]) Source(id string) (SourceInfo, bool) -func (e *LoadError) Source(id string) (SourceInfo, bool) -``` - -Look up an acquired source by its load-local ID, returning the same information -represented in `Sources()` as a detached record. Empty or unknown IDs return -false. The query performs no source acquisition, filesystem access, or credential -lookup. Reuse existing source lookup and conversion machinery where practical; -no public registry, indexing strategy, or constant-time guarantee is required. - -The failed-load lookup returns records from `LoadError.Sources()`'s partial -inventory and returns false for nil or zero-valued errors as well. Preserve -existing failed-load redaction and the evidence available at failure, including -incomplete placement information. Lookup neither completes acquisition nor -promises a record for every attempted source. This incorporates N5 into A1. - -## Work Required From The Baseline - -### Public Declarations And Facade Conversions - -Update the public declarations, comments, wrappers, and conversions as a coherent -surface. Useful existing owners are: - -| Area | Baseline owners and required work | -| --- | --- | -| Entry metadata, sensitivity, version mode, and import defaults | [lifecycle.go](../../lifecycle.go), [layered.go](../../layered.go), and [conversion.go](../../conversion.go): update both ordinary and layered loading paths, callback wrappers, and definition/default conversions. | -| Source evidence | [records.go](../../records.go) and [conversion.go](../../conversion.go): rename records/fields and remove the empty-selection outcome. Cover source records returned through results, callbacks, and load errors. | -| Recording intents | [lifecycle.go](../../lifecycle.go) and [engine resolution](../../internal/engine/resolution.go): provide the two methods, route them to the existing recording/auditing machinery, and remove the public kind-selecting method. | -| Discovery and bootstrap | [discovery.go](../../discovery.go) and [secrets.go](../../secrets.go): replace constructor/function names and migrate their callers without changing acquisition or environment mutation behavior. | -| Provenance and diagnostic values | [records.go](../../records.go), [engine records](../../internal/engine/records.go), producers, conversions, and renderers: apply the selected labels and codes consistently. | -| Direct lookup | [lifecycle.go](../../lifecycle.go), [diagnostic.go](../../diagnostic.go), and their engine collaborators: expose the existing retained source evidence through the new result/error methods. | - -Use explicit root/engine conversion as needed; no public aliases or engine types -are needed to implement these replacements. Private naming and helper placement -belong to implementation planning. Avoid unrelated internal renames merely to -make every private identifier match the facade. - -Preserve distinctions that the new names clarify: - -- `EntryInfo` remains a fixed, detached entry-state lookup with its current - unknown/invalid/shape-mismatch behavior. `IsExplicit` retains its own existing - validation behavior; the rename does not unify those two query contracts. -- `Sensitive` remains the classification predicate, separate from typed secret - references. Existing tag/predicate/dependency classification and redaction - still apply. -- `DestinationIsFallback` still describes the nearest resolved enclosing - container when the full destination is unavailable. It does not promise an - item's exact coordinates or upgrade evidence retained at an earlier stage. -- Empty-selection records keep their existing scope, order, and declaration-local - coordinates. Removing `Outcome` does not create a complete import ledger. -- `FollowSymlinks` and `IncludeHidden` retain pointer defaulting and load-preflight - snapshot behavior. Do not mechanically rename YAML `follow` or `hidden` keys. - -### Recording Entry Points And Obsolete Diagnostics - -Implement the two recording methods over the existing resolution lifecycle. -Migrate manual-record callers according to the intent they currently supply; -keep assignment helper calls as helpers. Tests of container coverage, overlapping -records, dependencies, equal-valued intent, and net-change auditing continue to -protect the same behavior through the new entry points. - -Complete the N4 check after replacing `Record`: trace supported recording routes, -then remove the obsolete exported invalid-kind code if its public trigger is -gone. Update tests of the removed input mechanism without weakening unrelated -record validation. Do not introduce a different public diagnostic solely to -represent an unreachable kind. - -For D8, confirm both producer references and any documented intentional role for -`CodeOptionsUnavailable`, then remove the unused declaration and associated -active documentation. A concrete supported role discovered during this check -should be brought back for discussion. These are verification tasks for accepted -removals, not permission to delete other diagnostics that happen to be uncommon. - -### Direct Lookup And Source Evidence - -Implement the result and error methods against each receiver's retained source -inventory. Reuse source-copying and facade-conversion helpers where practical so -lookup and enumeration agree, including nested locations, version evidence, -empty-selection evidence, and detached paths. Follow existing result-query -nil/zero conventions; failed-load nil/zero behavior is specified above. - -Keep `Sources()` enumeration and `SourceContext(id)` responsibilities intact. -Lookup must not repeat acquisition, finalize failure evidence, or change source -IDs. A slice search or a suitable existing index can satisfy the contract; no -new lookup complexity promise or shared lazy cache is selected. - -### Observable Strings And Their Producers And Readers - -Replace the main-tier category's `"base"` spelling with `"main"` wherever that -category crosses the public boundary or is rendered. Imported contributions keep -their owning tier and ordinary source-location attribution. Other uses of the -word "base", such as filesystem base directories, are unrelated. - -Change every retained-history-limit diagnostic producer and relevant reader to -`"history.limit_exceeded"`, including inheritance and final load admission. -Preserve the quota, accounting, and failure timing. This rename does not make -history optional or add a new limit control. - -Review Go constant references and literal comparisons/expected output separately: -compilation catches old symbols but not stale diagnostic or provenance strings. -Other codes and layer values retain their existing meaning and spelling. - -### Consumer Guidance And Migration Information - -Update declaration comments, applicable sections of [config.md](../config.md), -the [consumer guide](../consumers/README.md), README examples, and maintained -consumer code/fixtures under `examples/`. Use the new recording methods and source -lookups in relevant existing examples without expanding unrelated workflows. -Update internal documentation only where implemented ownership or terminology -changes. Accepted ADR decisions do not need revision for this cleanup. - -Record the old-to-new Go names, removed outcome fields/codes, recording-call -replacements, and the two observable string changes as migration information. -Downstream applications may need to update literal comparisons and serialized -expectations as well as Go references. Version-specific release notes and -publication remain part of the normal [release process](../release.md); this -roadmap does not select a release version or authorize publication. - -Keep revision-pinned review evidence and historical release records accurate for -their original revisions. Old names in those records are not stale active API -usage. Do not rewrite history or require external consumer migrations to claim -the repository implementation is complete. - -## Validation And Completion - -Follow the [testing policy](../policy/testing.md) and -[development guide](../development.md#baseline-validation). Reuse existing -behavioral tests for mechanical renames; new tests should protect the changed -entry points and added lookups, rather than duplicate implementation details. - -- Check the root API exposes every selected replacement/addition and no obsolete - public declarations from the accepted removals. Preserve root-owned type - identity and the existing public/dependency boundary checks. Historical - references and useful private implementation names do not invalidate this check. -- Exercise both recording intents through the new public methods. Preserve - helper assignment behavior, validation, recorded origins, and history under - the existing resolution tests; update the old invalid-kind case when its input - mechanism is removed. -- Cover successful and failed source lookup against each receiver's enumeration, - missing IDs, applicable nil/zero cases, detachment of nested evidence, and - unchanged records after caller mutation. Reuse existing source/version/resource - fixtures for redaction, partial failure evidence, and passive access behavior. - Do not add source-completeness, performance, or concurrency guarantees. -- Retain the existing entry-shape, sensitivity, version-mode, empty-selection, - placement, import-default snapshot, discovery, and secrets-bootstrap coverage. - In particular, Go field renames must leave YAML declarations and tri-state - defaulting behavior intact. -- Update relevant provenance and diagnostic assertions for the selected strings. - Existing history-budget cases should continue to check rejection before secret - lookup and application validation, using the new code and unchanged accounting. -- Run the applicable focused checks during development and the everyday check - for the integrated code changes. Apply existing race-check guidance if the - implementation changes ownership/concurrency behavior. Validate documentation - links and maintained examples. No benchmark campaign or additional release - gate is introduced. - -Completion means all accepted proposals are implemented, the diagnostic-removal -checks are resolved, maintained consumers and current documentation use the new -surface, and applicable checks pass with behavior preserved except for the -expressly selected changes. Update roadmap/review status with the actual evidence; -passing the current baseline suite is not evidence of future implementation. - -No significant open design decision remains. D8's role check and D6/N4's public -reachability check are implementation verification tasks; unexpected evidence of -a legitimate public use should be discussed before changing that use. Private -helper choices and lookup mechanics are selected in the implementation plan. -Public path comparison (A2) remains deferred and outside this scope. diff --git a/docs/roadmap/audit.md b/docs/roadmap/audit.md deleted file mode 100644 index 9639aa8..0000000 --- a/docs/roadmap/audit.md +++ /dev/null @@ -1,201 +0,0 @@ -# Additional Public API Review - -Reviewed revision: `3ebb848eadf6e61275ecec6e35838ad6576cdd3a`. -Status: N1–N6 accepted, with N4 incorporated into D6 and N5 into A1. -Implementation has not begun. - -## Scope And Assessment - -This review accepts the inventory in [review.md](review.md) and independently -assesses the public interface through consumer workflows, declaration comments, -the [configuration contract](../config.md), and targeted implementation tracing. -It covers loading/discovery, source and resource ownership, paths and collection -registration, callbacks, inspection/diffs, secrets, and diagnostics. It does not -repeat the symbol inventory or conduct a correctness/performance audit. - -[api.md](api.md) owns the accepted D1–D8, A1, and N1–N6 changes. Those decisions remain -settled; public path comparison (A2) remains deferred. The findings here are -additional opportunities, including consequences of the accepted cleanup. - -The principal API boundaries remain useful. No broader consolidation of loading -options, filesystem roles, callbacks, or the two history projections is warranted -by this review. The remaining opportunities concern vocabulary, removal of an -obsolete input-error category, and a missing convenience on failed loads. - -| Finding | Recommendation | Compatibility impact if accepted | -| --- | --- | --- | -| N1: discovery `Paths` (accepted) | Rename to `SearchPaths`; decision recorded in [api.md](api.md). | Go function rename. | -| N2: main-tier provenance (accepted) | Rename `LayerBase` to `LayerMain`, with value `"main"`; decision recorded in [api.md](api.md). | Go constant rename and observable string change. | -| N3: shared-history quota (accepted) | Rename `CodeExplainLimitExceeded` to `CodeHistoryLimitExceeded`, with value `"history.limit_exceeded"`; decision recorded in [api.md](api.md). | Go constant rename and machine-readable code change. | -| N4: recording-kind diagnostic (accepted through D6) | Incorporated into [D6 in api.md](api.md#recording-methods-d6). | Conditional constant removal. | -| N5: failed-load source lookup (accepted through A1) | Incorporated into [A1 in api.md](api.md#direct-source-lookup-a1). | Compatible addition. | -| N6: import-default field names (accepted) | Rename to `FollowSymlinks` and `IncludeHidden`; decision recorded in [api.md](api.md). | Go field renames; YAML keys unchanged. | - -These accepted design changes are not defect severities or additional release -gates. The string-value changes in N2 and N3 are explicitly accepted. - -## N1. Distinguish Discovery Paths From Semantic Paths - -Status: accepted. [api.md](api.md) owns the selected change; the discussion below -preserves its rationale and alternatives. - -Evidence: [path.go](../../path.go) defines `Path`, `Keys`, and `ParsePath` for -configuration-value coordinates. [discovery.go](../../discovery.go) defines -`Paths(paths ...string) Search` for literal host-file candidates. In the same -package, the plural of `Path` therefore constructs a different concept rather -than a collection of semantic paths. The preliminary catalog recommends retaining -the name and explaining the distinction. - -Recommendation: rename the discovery constructor to -`SearchPaths(paths ...string) Search`. This aligns it with `SearchDirectory` and -makes a call such as `SearchPaths("./app.yml", "/etc/app.yml")` recognizable -without inspecting its return type. Keep literal-path interpretation, candidate -order, deferred acquisition, and all search behavior unchanged. No rename of -`Path`, new path type, or discovery redesign is needed. - -Alternative: retain `Paths`. It is concise inside `SearchConfig` and its comment -is accurate, but package completion and standalone calls still mix filesystem -and configuration-path vocabulary. `FileCandidates` is another viable name, -although it is less consistent with `SearchDirectory`. - -## N2. Use Main Consistently In Provenance - -Status: accepted, including the string-value change. [api.md](api.md) owns the -selected change; the discussion below preserves its rationale and alternatives. - -Evidence: `LoadLayered(main, ...)`, `SourceInfo.UnderlayIndex`, the configuration -contract, and the accepted `SourceVersionModeMainOnly` name distinguish main input -from lower-priority underlays. [records.go](../../records.go) instead exposes -`LayerBase = "base"` for the main tier. A consumer presenting provenance must -learn that this base is above underlays and is distinct from application defaults. -The preliminary catalog treats this as a documentation concern. - -Recommendation: use `LayerMain = "main"` so API calls, provenance records, and -rendered layer labels use the same term. Preserve the existing layer category and -all precedence rules; imported contributions retain their owning tier. This is -a vocabulary change, not a new layer or a change to source acquisition order. - -Changing the literal affects applications that compare, serialize, or display -layer strings as well as those referring to the Go constant. If selected, migrate -those uses and document the change together before v1. - -Alternative: rename only the Go constant and keep `"base"`. That reduces string -compatibility impact but preserves two names for the same concept in application -code and rendered evidence. Keeping both the existing symbol and value is also -reasonable if the modest clarity gain does not justify migration; the preferred -pre-v1 outcome is one consistent term. - -## N3. Name The Retained-History Limit For Its Actual Scope - -Status: accepted, including the diagnostic-code string change. [api.md](api.md) -owns the selected change; the discussion below preserves its rationale and alternatives. - -Evidence: [records.go](../../records.go) exposes -`CodeExplainLimitExceeded = "explain.limit_exceeded"`. Its production sites include -`executeLoadRequest` in [load.go](../../internal/engine/load.go) and -`inheritanceHistoryBudgetDiagnostic` in -[inheritance_walk.go](../../internal/engine/inheritance_walk.go). It can fail a load -without an application calling `Explain`. The completed shared model supplies -metadata, explanation, historical membership, and internal attribution readers. - -Recommendation: rename the constant to `CodeHistoryLimitExceeded` and its value -to `"history.limit_exceeded"`. This describes the resource being bounded and -avoids suggesting that skipping explanation would avoid the load failure. -Preserve the quota, retained-node accounting, diagnostic locations, and admission -timing. This does not add a limit setting or change which configurations load. - -Alternative: rename the Go constant but retain the existing code string, or keep -both names unchanged and clarify the diagnostic documentation. Either preserves -more consumer compatibility, but a retained `"explain.limit_exceeded"` remains -misleading to applications that work directly with diagnostic codes. A coordinated -pre-v1 change is preferable if the new terminology is accepted. - -## N4. Review The Diagnostic Made Obsolete By D6 - -Status: accepted and incorporated into [D6](api.md#recording-methods-d6). The -discussion below preserves the rationale and alternative. - -Evidence: the production use of `CodeResolveInvalidKind` is the kind guard in -`resolutionState.addRecord` in -[resolution.go](../../internal/engine/resolution.go). Today, public `Record` can -pass arbitrary `ChangeKind` values into it. Accepted D6 replaces that method with -two methods that select supported intents; `Default` and `Normalize` also supply -fixed supported kinds. This is distinct from D8's already unproduced -`CodeOptionsUnavailable`. - -Recommendation: as part of D6 implementation, confirm that no supported public -input can produce `resolve.invalid_kind`, then remove its exported constant. -Retain the other recording diagnostics and the inspection `ChangeKind` values. -Internal defensive checks can remain where useful; their representation does -not require an unreachable public configuration-error category. - -Alternative: retain the constant for consistency with previous diagnostics or -a concrete future use. That costs little code but leaves an unexplained public -error category after its input mechanism has disappeared. Do not add a public -trigger simply to justify keeping it. The removal remains conditional on the -final D6 implementation, rather than assuming a guard must be deleted now. - -## N5. Offer Direct Source Lookup On Failed Loads - -Status: accepted and incorporated into [A1](api.md#direct-source-lookup-a1). The -discussion below preserves the rationale and alternative. - -Evidence: [diagnostic.go](../../diagnostic.go) exposes `LoadError.Diagnostics()` -and `LoadError.Sources()`. Diagnostic primary and related locations refer to -source IDs. Accepted A1 adds direct lookup on successful results, while -`Resolution.Source` already supports it during callbacks. A custom error reporter -still has to scan or index the failed load's source slice to follow the same IDs. - -Recommendation: add this companion to A1: - -```go -func (e *LoadError) Source(id string) (SourceInfo, bool) -``` - -It should return a detached record from the same partial inventory as -`LoadError.Sources()`, with false for empty/unknown IDs and nil/zero errors. -Preserve the existing failed-load redaction and available evidence; a lookup -does not complete acquisition or upgrade a partial destination/version record. -This is passive lookup, with no filesystem access or new source-completeness -guarantee. Existing source lookup/conversion helpers can be shared privately; -no public registry, interface hierarchy, or constant-time guarantee is needed. - -Alternative: keep only `Sources()`. That is sufficient for applications using -the standard diagnostic renderer or building an index once. The addition can -also wait until after v1, since it is compatible. Adding it with A1 is a small, -coherent way to remove the same consumer bookkeeping on both outcomes of a load. - -## N6. Make The Go Import Defaults More Self-Describing - -Status: accepted. [api.md](api.md) owns the selected change; the discussion below -preserves its rationale and alternative. - -Evidence: [lifecycle.go](../../lifecycle.go) exposes `ImportDefaults.Follow` and -`ImportDefaults.Hidden`. Their comments explain symbolic-link traversal and -inclusion of hidden files/directories, but an options literal alone does not -name the object being followed or say whether hidden entries are included. - -Recommendation, at lower priority: consider `FollowSymlinks` and `IncludeHidden` -for those Go fields. Preserve `*bool`, nil defaulting, explicit false, the current -search scope, and the YAML keys `follow` and `hidden`. This would make the public -Go configuration more descriptive without changing the import language. Leave -`Required`, `Recursive`, and `Pattern` alone; they do not have the same ambiguity -in context. - -Alternative: retain the current Go names to mirror YAML exactly. That is a real -benefit for readers moving between authored imports and application defaults, -and existing comments already resolve the ambiguity. This is an optional naming -refinement, weaker than N1's collision between two different path concepts. - -## Discussion And Validation - -All six findings have been accepted and incorporated into [api.md](api.md), with -N4 folded into D6 and N5 into A1. This audit retains the review evidence and -rationale; it is not an implementation plan or a permanent second API reference. - -The review used the trusted catalog, root declarations and comments, current -contracts and consumer guidance, and graph-assisted production-reference checks. -No production code, tests, accepted decisions, or deferred features were changed. -No independent behavioral audit or performance benchmarks were run. Validation -for this document consists of the local documentation-link check and whitespace -review; it is not evidence that the proposed API changes are implemented. diff --git a/docs/roadmap/future.md b/docs/roadmap/future.md index 7b9252e..6cabfe3 100644 --- a/docs/roadmap/future.md +++ b/docs/roadmap/future.md @@ -6,14 +6,6 @@ plans, or release commitments. Adopting a candidate requires an explicit design decision and a separate plan. Significant architectural exclusions and their rationale belong in ADRs; this document does not maintain a list of rejected proposals. -## 1. Separate glob entry preparation from traversal - -Implemented in the [internal-boundary roadmap](internal-boundaries.md#glob-entry-preparation) (C03). - -## 2. Simplify redundant facade-to-engine transport - -Implemented in the [internal-boundary roadmap](internal-boundaries.md#facade-to-engine-transport) (F07). - ## 3. Recursive body merging for identified records Status: deferred. The implemented @@ -91,3 +83,22 @@ evidence are preserved in Git history at `8e91a2b` under `docs/roadmap/`. across loads without introducing aliasing? - What cache lifetime and concurrency design would make schema reuse worthwhile without excessive retained memory? + +## 6. Public path comparison + +Status: deferred pending a concrete consumer need. This preserves A2 from the +completed API review at `68400ff:docs/roadmap/review.md`. + +`Path.String()` is a JSON Pointer spelling, not a complete semantic identity: +an explicit numeric mapping key and a sequence index can share it. The public +type is opaque and currently non-comparable. + +If consumers need comparison, consider a small exact-representation operation +rather than exposing mutable segments or introducing a pattern language. A +proposal must distinguish exact segment representation, including parsed versus +explicit segments and invalid state, from resolution against a document. Pointer +string equality must not be presented as semantic equality. + +Keeping `Path` solely as an opaque query/building value remains an alternative. +No current contract changes or implementation commitment follow from this entry; +a comparison operation can be added compatibly later. diff --git a/docs/roadmap/implementation.md b/docs/roadmap/implementation.md deleted file mode 100644 index 3ddc1bf..0000000 --- a/docs/roadmap/implementation.md +++ /dev/null @@ -1,498 +0,0 @@ -# Pre-v1 Public API Cleanup Implementation Plan - -Status: complete; Stages 1–11 are complete. -Baseline: `07e4ae5d9236935389e81b207d4a8f00543fafce`. -Audience: a Luna coding agent executing the stages sequentially. - -## Scope And Execution - -Implement the accepted changes in the [API roadmap](api.md). That document owns -the target, rationale, and behavioral boundaries. The revision-pinned -[catalog](review.md) and [additional review](audit.md) preserve evidence and -alternatives; they are not additional work lists. The preceding completed -interface-remediation plan is preserved at -`07e4ae5:docs/roadmap/implementation.md`. References in older audit records to -that completed plan refer to its historical version, not this sprint. - -This is one end-to-end sprint. Intermediate stages need not compile or leave a -coherent repository. Do not create compatibility aliases, temporary public -adapters, or intermediate releases to make individual stages green. Run focused -checks when buildable and record deferred checks until they can actually run. -Commit focused nonempty units under the [agent policy](../policy/agents.md), -recording actual completion and validation below. A stage is not complete merely -because its commands passed against the unchanged baseline. - -Read `AGENTS.md`, the [development guide](../development.md), and applicable -[architecture](../policy/architecture.md), [testing](../policy/testing.md), and -[documentation](../policy/documentation.md) policies. Use graph-first discovery -and check current references before editing. Preserve unrelated work. - -The sprint changes Go names, removes selected public state, adds two passive -queries, and changes exactly the two selected public strings. It does not change -loading/composition, YAML syntax, version policy, callback auditing, history -ownership, sensitivity, source acquisition, or secrets-bootstrap semantics. -Do not add performance promises, restrictions, dependencies, release gates, or -tests for incidental mechanics. Public path comparison (A2) remains deferred. -Do not select a release version, write version-specific release notes, publish, -or update external consumers in this sprint. - -### Coverage Map - -| Accepted item | Implementation owner | -| --- | --- | -| D1: `EntryInfo`; D2: `Sensitive` | Stage 1 | -| D3: version policy/evidence vocabulary | Stage 2 | -| D4: empty-selection records; D5: destination fallback name | Stage 3 | -| N6: descriptive Go import defaults | Stage 4 | -| N1: `SearchPaths`; D7: `LoadSecretsIntoEnvironment` | Stage 5 | -| D6/N4: recording methods and invalid-kind diagnostic removal | Stage 6 | -| N2: main-tier provenance name and string | Stage 7 | -| N3: history-limit name/string; D8: unproduced diagnostic removal | Stage 8 | -| A1/N5: successful and failed source lookup | Stage 9 | -| Current guidance, migration information, integrated completion | Stages 10–11 | - -### Private Implementation Choices - -- Keep the public facade root-owned, with explicit forwarding and conversion. - For facade-only renames, retain existing engine names unless steps below - require a change. Root `Sensitive` can forward to engine `Secret`, and root - `EntryInfo` can call engine `InputInfo`. Engine source-version records, - import-default fields, discovery/bootstrap functions, and destination-fallback - helpers need no cosmetic renames. -- Remove empty-selection outcome state through its small private transport path. - Keep candidate selection and admission decisions distinct from empty-selection - evidence. -- Replace root and engine kind-selecting `Resolution.Record` entry points with - fixed-intent methods. Keep common `addRecord` and assignment helpers; remove - the unreachable invalid-kind category after tracing remaining routes. -- Use one engine-local slice lookup helper for new result/error queries. Scan - the retained inventory and clone only a matching source with existing - `cloneSourceInfo`; do not clone `Sources()` in full to search it. Keep the - callback's existing indexed entry-state lookup unchanged. This is a private - implementation choice, not a public complexity guarantee. -- Rename main-layer and history-limit constants in both root and engine, so - producers, conversion, and rendering use the selected strings directly. - -### Validation Conventions - -Commands run from the repository root using an installed compatible Go toolchain -and already acquired dependencies. For Go checks use: - -```sh -export GOTOOLCHAIN=local -export GOPROXY=off -export GOTELEMETRY=off -``` - -Keep disposable inventories outside the repository. Reuse behavioral tests for -mechanical renames. Do not add negative-compilation fixtures or a permanent -exported-symbol inventory test simply to prove old names disappeared; Stage 11 -performs a one-time review. Focused commands name existing test families and the -proposed new lookup/recording cases. Include differently named new tests -explicitly. A no-test match is not validation. - -Update affected declaration comments and maintained Go callers, including -examples, with each change. Stage 10 owns the integrated prose/migration sweep; -it is not a reason to leave obvious neighboring comments wrong. - -## Stage 1 — Clarify Resolution Entry Metadata And Sensitivity - -Status: complete. Implements D1 and D2. - -1. In [lifecycle.go](../../lifecycle.go), rename root `Resolution.InputInfo` to - `EntryInfo` and `Definition.Secret` to `Sensitive`, without old aliases. - Update `toEngineDefinition` to adapt the new predicate into engine `Secret`. - Both loading entry points already share this conversion; retain that route. -2. Keep `EntryInfo`'s existing `(FieldInfo, bool)` forwarding and silent - unknown/invalid/shape-mismatch behavior. Its fixed entry view includes retained - typed defaults and CLI assignments. Do not substitute final metadata or - change `IsExplicit`'s separate schema-path validation behavior. -3. Migrate root tests and maintained consumer calls. Update inactive-context - cases in `facade_context_test.go`. Leave engine `InputInfo`, private - sensitivity names, the `configkit:"secret"` tag, and typed `Secret` references - unchanged. Do not broadly replace every identifier `Secret`. -4. Reuse `facade_resolution_presence_test.go`, `facade_candidate_test.go`, - `facade_context_test.go`, and existing secret/provenance owners. Preserve - ordinary/container classification and its union with tags/dependencies. - -Focused validation: - -```sh -go test . -run 'Resolution|InactiveContexts|FacadeLoad|Secret|Inspection|Diff' -count=1 -go test ./internal/engine -run 'Resolution|Provenance|Secret|Sensitivity' -count=1 -``` - -Completion: selected root names are in use; entry queries, lifecycle guards, and -classification behavior are unchanged. - -## Stage 2 — Rename Version Policy And Evidence - -Status: complete. Implements D3. - -1. Replace root `SourceVersionModeDefault` with `SourceVersionModeMainOnly` - in the same zero slot in [lifecycle.go](../../lifecycle.go). Preserve numeric - values and behavior of `IfPresent`/`Required` and the numeric engine bridge - used by ordinary/layered loads. -2. In [records.go](../../records.go), rename `SourceVersionCheck` to - `SourceVersionEvidence` and `SourceInfo.VersionCheck` to `VersionEvidence`. - Update [conversion.go](../../conversion.go); name its root-returning helper - `fromEngineSourceVersionEvidence`, still accepting engine - `SourceVersionCheck`. Preserve fields, nil handling, detached paths/locations, - and sanitized text. -3. Migrate Go callers/tests/examples and version comments together. `MainOnly` - checks the effective path only with a configured rule; broader modes use - source-local paths and require a usable rule. Evidence means observation, - not acceptance or checker invocation. Add neither acceptance flags nor rules. -4. Reuse `records_test.go` and `facade_source_version_mode_test.go`: zero-mode - compatibility, missing-main checker invocation, absent non-main skipping, - `NoSource`, explicit null, consumption, mounted/item coordinates, redaction, - failure evidence, and detachment. Rename test names directly naming the - replaced declaration; do not duplicate their case matrix. - -Focused validation: - -```sh -go test . -run 'SourceVersion|VersionCheckConversion|VersionEvidenceConversion|Facade.*Version' -count=1 -go test ./internal/engine -run 'SourceVersion' -count=1 -``` - -Completion: all three replacements cross every source-record exposure correctly, -with existing version behavior intact. - -## Stage 3 — Simplify Empty-Selection And Destination Evidence - -Status: complete. Implements D4 and D5. - -1. Replace root `ImportSelection` with `EmptyImportSelection` and - `SourceInfo.ImportSelections` with `EmptyImportSelections`. Retain `Path`, - `Location`, and `Selector`; remove `Outcome`, `ImportSelectionOutcome`, - and `ImportSelectionNone`. Keep `ImportSelector` and its three constants. -2. Update root conversion/tests. In engine `records.go`, remove `Outcome` - from `ImportSelectionForFacade`, the private evidence `outcome` field, - and `importSelectionOutcomeString`. Remove its initializer in - `import_acquisition.go` and the now-unused selection-outcome enum in - `import_selection.go` after checking references. Its unused - `importSelectionHasCandidates` member does not justify creating evidence. - Preserve actual admission outcomes such as `importAdmissionOptionalMissing`, - candidate handling, and accounting. - Existing private transport/helper names may stay unchanged. -3. Rename root `SourceInfo.DestinationUnresolved` to `DestinationIsFallback`. - Conversion still reads existing private fallback evidence. Keep destination - coordinates and placement modes; do not derive exact item coordinates where - existing evidence does not contain them. -4. Migrate root/internal assertions and maintained Go callers. Preserve empty - selection scope/order, declaration-local paths, and copies on results, - callbacks, and errors. Remove assertions of the retired outcome only, keeping - real selection assertions. Reuse nested-item fallback/partial-failure cases; - do not invent new placement states. - -Focused validation: - -```sh -go test . -run 'ImportSelection|EmptyImport|Destination|SourceInfo|Fallback|RelativeImport' -count=1 -go test ./internal/engine -run 'ImportSelection|ImportEvidence|Import|Destination|Item' -count=1 -``` - -Completion: source records use the reduced record/new fields; selection, -placement, partial evidence, and detachment retain established behavior. - -## Stage 4 — Make Go Import Defaults Self-Describing - -Status: complete. Implements N6. - -1. Replace root `ImportDefaults.Follow` with `FollowSymlinks` and `Hidden` - with `IncludeHidden`, preserving `*bool` and the other three fields. - Update comments and `toEngineImportDefaults` to map to existing engine - `Follow`/`Hidden`. -2. Migrate root option literals, examples, and tests by type/context. Do not - rename YAML `follow`/`hidden`, declaration parser fields, or unrelated - hidden-file controls. Do not snapshot earlier in the facade: existing engine - preflight owns the once-per-load snapshot before acquisition/callbacks. -3. Reuse `facade_import_defaults_test.go` for nil, explicit false/true, - transitive/underlay application, declaration overrides, pattern selection, - and callback-mutation isolation. Preserve traversal/search scope and nil - versus non-nil empty `Pattern`. - -Focused validation: - -```sh -go test . -run 'ImportDefaults|ImportSelection|EmptyImport' -count=1 -go test ./internal/engine -run 'ImportDefaults|Import.*(Hidden|Follow|Symlink|Pattern)' -count=1 -``` - -Completion: descriptive Go fields feed unchanged YAML policy and snapshots in -ordinary/layered loading. - -## Stage 5 — Clarify Discovery And Environment Bootstrap Names - -Status: complete. Implements N1 and D7. - -1. In root [discovery.go](../../discovery.go), rename `Paths` to `SearchPaths`. - Forward the same literal candidates to existing engine machinery. Migrate - callers without touching semantic `Path`, `Keys`, or `Searches`. Retain - order, deferred acquisition, and selected-file terminal failures. -2. In root [secrets.go](../../secrets.go), rename `LoadSecretsDir` to - `LoadSecretsIntoEnvironment`, preserving arguments, return values, error - conversion, and engine bootstrap call. Comments should plainly describe - explicit process-environment mutation, already-set/empty preservation, - reports, and existing partial-application behavior. -3. Migrate maintained calls, affected executable example identifiers, and nearby - documentation. Do not couple bootstrap to `Load`, rename report/error types, - or add filesystem/transactional guarantees. Process-mutating tests remain - serial and restore state. Private engine function names may remain unchanged. - -Focused validation: - -```sh -go test . -run 'Search|Discovery|SecretsBootstrap|LoadSecrets|Example_weatherStartup' -count=1 -go test ./internal/engine -run 'Search|Discovery|LoadSecretsDir' -count=1 -go test ./examples/consumers -run 'Discovery|WorkingDirectoryPreset|SequenceCatalog|Weather' -count=1 -``` - -Completion: old root functions are replaced outright; acquisition and safe -bootstrap behavior remain unchanged. - -## Stage 6 — Replace Kind-Selecting Recording With Intent Methods - -Status: complete. Implements D6 and N4. - -1. Replace root and engine `Resolution.Record` with: - - ```go - RecordDerivedDefault(path Path, reason string, dependencies ...Path) - RecordNormalization(path Path, reason string, dependencies ...Path) - ``` - - Root methods forward through existing live-context guards/path conversion. - Engine methods call `activeState().addRecord` with their fixed - `ChangeDerivedDefault` or `ChangeNormalization` and no helper expected - value. Share existing validation/coalescing/auditing; neither method assigns - values or tests absence. -2. Migrate manual callers according to their existing supported kind in root - tests, engine tests, examples, and current snippets. Leave `Default` and - `Normalize` as assignment-and-recording helpers using existing `addRecord` - routes. Retain `ChangeKind` and all inspection constants. -3. Check every remaining `addRecord` caller supplies a supported fixed kind. - Then remove the invalid-kind guard and `CodeResolveInvalidKind` from both - root/engine. Remove only the obsolete arbitrary-kind case in - `resolution_test.go`; retain invalid path, target, reason, dependency, - projected-value, and unrecorded-change cases. If a legitimate supported - public trigger remains, report it before changing its behavior. -4. Update nil/zero/closed-context coverage for both methods. Add/adapt a small - root case, `TestFacadeResolutionRecordingIntents`, proving each method's - resulting origin/history kind after application assignment. Include explicit - input changed and recorded as a derived default, so this cannot accidentally - become the absence-checking helper. Reuse container coverage, overlapping - records, repeated/equal intent, dependencies, and net-change tests elsewhere. - -Focused validation: - -```sh -go test . -run 'Resolution|InactiveContexts|FacadeLoad|Historical|History' -count=1 -go test ./internal/engine -run 'Resolution|Provenance|Callback|History' -count=1 -``` - -Completion: root exposes only the selected manual recording methods; supported -input cannot produce `resolve.invalid_kind`. Other auditing/lifetime behavior -is unchanged. - -## Stage 7 — Use Main Consistently In Provenance - -Status: complete. Implements N2. - -1. Replace `LayerBase = "base"` with `LayerMain = "main"` in root/engine - records. Migrate producer references across acquisition, composition, - decoding, inheritance origins, overrides, discovery, and history/rendering. - Keep existing tier assignments. -2. Review literal `"base"` provenance comparisons/rendered expectations, not just - Go references. Update only the main-tier category. Filesystem base - directories, domain values, fixture labels, and unrelated words are unchanged. - Retain other layer spellings and source IDs/order. -3. Update existing assertions in root, engine, and examples. Add a focused root - records assertion for `string(LayerMain) == "main"`; reuse a public - provenance/rendering fixture to verify main/imported-main attribution reaches - consumers with that label. Imports retain their owning tier; other categories, - precedence, and acquisition order are unchanged. - -Focused validation: - -```sh -go test . -run 'Facade|Historical|History|Layer|PublicValues' -count=1 -go test ./internal/engine -run 'Compose|Overlay|Decode|Provenance|History|Explain|Grouped|Discovery' -count=1 -``` - -Completion: main-tier evidence/rendering consistently uses the selected constant -and spelling without attribution/precedence changes. - -## Stage 8 — Rename The History Diagnostic And Remove The Unused Code - -Status: complete. Implements N3 and D8. - -1. Replace root/engine `CodeExplainLimitExceeded` with - `CodeHistoryLimitExceeded = "history.limit_exceeded"`. Update every producer, - including `executeLoadRequest` in engine `load.go` and - `inheritanceHistoryBudgetDiagnostic` in `inheritance_walk.go`, plus - readers/literal assertions. Other explanation errors are unchanged. -2. Reuse `history_budget_test.go` and inheritance-budget cases to preserve exact - retained-node accounting and failure before effective secret lookup/application - validation. Change expected code, not quota, fixtures, accounting, or timing. - Add the new literal to focused root constant-value assertions, not a general - API snapshot test. -3. Reconfirm `CodeOptionsUnavailable`/`options.unavailable` has no producer or - documented supported/reserved role. Baseline references are the two - declarations and planning/review evidence. Remove both declarations. If a - concrete supported role is discovered, bring it back for discussion rather - than remove behavior or create a trigger. Other diagnostic codes stay intact. - -Focused validation: - -```sh -go test ./internal/engine -run 'HistoryBudget|RetainedNode|InheritanceExpansionEnforcesRetainedHistoryBudget' -count=1 -go test . -run 'PublicValues|LoadReturnsFacadeLoadError|Diagnostics|Historical' -count=1 -``` - -Completion: history failures carry the new code under unchanged conditions; -D8's role check is resolved and its unproduced declaration is removed. - -## Stage 9 — Add Detached Source Lookup On Results And Load Errors - -Status: complete. Implements A1 and N5. - -1. Add engine-local `lookupSourceInfo(sources []SourceInfo, id string) - (SourceInfo, bool)` beside source cloning helpers. Return zero/false for - empty/unknown IDs; otherwise scan the retained slice and clone the match. - Add nil-safe engine `Inspection.Source` and `LoadError.Source` using it. - Do not change inventories, query-time evidence, or the callback's source map. -2. Add the selected root methods in [lifecycle.go](../../lifecycle.go) and - [diagnostic.go](../../diagnostic.go): - - ```go - func (r *Result[T]) Source(id string) (SourceInfo, bool) - func (e *LoadError) Source(id string) (SourceInfo, bool) - ``` - - Return zero/false for nil or zero receivers/absent delegated state. Convert a - found engine record through the Stage 2–3 `fromEngineSourceInfo` path. - Keep enumeration, passive `SourceContext`, and callback `Source` intact. -3. Document passive lookup, load-local IDs, detachment, and partial failed-load - evidence. Read `LoadError`'s already-sanitized retained sources, not raw - acquisition/version state. Do not complete placement, reinterpret absence - as acceptance, open resources, or perform credential lookup. -4. Add focused external-package tests `TestResultSourceLookup` and - `TestLoadErrorSourceLookup`, or extend equivalent facade owners. Compare found - records to enumeration; check empty/unknown IDs and nil/zero receivers. - Mutate nested returned evidence and verify repeated lookup/enumeration are - unchanged. Reuse import/version fixtures for `ImportedFrom`, - `VersionEvidence.Location`, and empty-selection slices. Cover representative - layered sources and failure evidence with redaction/partial placement. - Reuse counting-resource fixtures where useful to demonstrate no query I/O; - do not add a full load-mode matrix, timing assertions, or concurrency promises. - -Focused validation: - -```sh -go test . -run 'SourceLookup|SourceVersionEvidence|ImportSelection|EmptyImport|SourceContext|Fallback' -count=1 -go test ./internal/engine -run 'SourceVersion|ImportSelection|Resource|ResolutionInput' -count=1 -``` - -Completion: both lookups agree with enumeration, return detached records, and -preserve success/failure evidence without new load activity. - -## Stage 10 — Complete Guidance And Consumer Migration Information - -Status: complete. Consumer guidance, current contracts, declaration comments, and -maintained examples use the selected API; historical report references identify -their completed plan revision. - -1. Review declaration comments and [config.md](../config.md)'s owning sections: - discovery, import defaults/evidence, source versions, layered metadata, - sensitivity, bootstrap, resolution, and history limits. Update relevant names - and strings while preserving semantics. Keep detailed Go contracts beside - declarations rather than reproducing them throughout prose. -2. Migrate README, [consumer guidance](../consumers/README.md), root executable - examples, and maintained `examples/` code/fixtures. Use new source lookup where - an existing example enumerates solely to find an ID; retain enumeration where - that is the task. Use new intent methods for manual recording while retaining - assignment helpers where they do the work. -3. Add a concise **Pre-v1 public API cleanup** subsection to the consumer guide's - existing migration section. Include every old-to-new name from the roadmap, - removed empty-selection outcome field/type/constant and diagnostic constants, - both `Record` call rewrites, new lookups, and `"base"` → `"main"` / - `"explain.limit_exceeded"` → `"history.limit_exceeded"`. Explain unchanged - YAML `follow`/`hidden` and `configkit:"secret"`; literal comparisons and - serialized expectations may need migration as well as Go code. Do not claim - external migrations or select a version for this breaking pre-v1 work. -4. Update internal docs only for changed descriptions. Do not rewrite accepted - ADR decisions, pinned reviews, or historical release notes. If retained - completed reports link to this overwritten plan, make their old-plan reference - revision-specific rather than implying it concerns this sprint. Preserve - unrelated proposals/status. - -Validation: - -```sh -go test ./examples/consumers -count=1 -go test . -run '^Example|TestLocalDocumentationLinks' -count=1 -git diff --check -``` - -Validation passed with the commands above. - -Completion: current guidance/examples use the selected API, migration information -is complete, and historical evidence remains accurate. - -## Stage 11 — Verify The Integrated Surface And Finish The Sprint - -Status: complete. The root export audit matches the accepted table, D6/N4 and -D8 reference checks are resolved, and the required focused and repository-wide -checks passed. - -1. Compare actual root exports/comments against the roadmap's complete accepted - table using `go doc -all .` and, if needed, a disposable AST/type inventory - outside the checkout. Check fields/method sets/constants as well as top-level - names. Confirm both recording and lookup signatures and no removed root - names/compatibility aliases. -2. Search active Go callers/current documentation for old names and the two old - strings. Review matches by context: retained private engine names, domain - text, migration instructions, pinned reviews, and historical records are valid. - Do not globally rewrite `base`, `Secret`, `Paths`, `Follow`, `Hidden`, or - `Record`, or enforce a blanket zero-match rule. Recheck D6/N4 and D8 reference - evidence against the final tree. -3. Run deferred focused tests and existing boundary/link checks: - - ```sh - go test . -run 'TestPublicFacadeBoundary|TestDependencyImportBoundary|TestLocalDocumentationLinks' -count=1 - ./scripts/check.sh - ``` - - Validation passed: the focused boundary/link test and `./scripts/check.sh`. - `go doc -all .` confirmed the selected root fields, constants, methods, and - signatures; removed root names and old diagnostic strings were absent. - Graph tracing showed `addRecord` is called only by the two fixed-intent - methods and the assignment helpers. No Go declaration or use of either - removed diagnostic remains. - - The everyday check already runs the full default suite, vet, and build, - including examples. No additional full-suite pass or benchmarks are needed - solely for this plan. If implementation changed mutable ownership/concurrency - beyond forwarding/existing clone paths, follow the development guide with - `go test -race ./...`; record the reason/result. This introduces no permanent - release gate and requires no CI waiting. -4. Inspect the final diff for unselected semantics, ownership changes, - dependencies, artifacts, and strengthened guarantees. Record actual stage - completion/validation here. Mark the API roadmap implemented only after its - selected surface and behavior preservation are verified. Keep completed - planning/review records until normal release preparation. Commit completed - nonempty units with plain-English messages; report remaining work accurately. - -Completion: all accepted items are implemented, diagnostic role/reachability -checks are resolved, applicable checks pass, and current docs/examples agree. -No unselected public or semantic changes remain. This is implementation -completion, not release publication or external consumer verification. - -## Decision Status - -No open design question is required to execute this plan. D8's role check and -D6/N4's reachability check are verification tasks with an explicit discussion path -if unexpected supported use is found. Private helper naming may be adjusted for -cohesion without changing the selected surface or behavior. diff --git a/docs/roadmap/interface-remediation.md b/docs/roadmap/interface-remediation.md deleted file mode 100644 index 81c7930..0000000 --- a/docs/roadmap/interface-remediation.md +++ /dev/null @@ -1,337 +0,0 @@ -# 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. diff --git a/docs/roadmap/interfaces.md b/docs/roadmap/interfaces.md deleted file mode 100644 index 76d337d..0000000 --- a/docs/roadmap/interfaces.md +++ /dev/null @@ -1,213 +0,0 @@ -# API and Interface Contract Audit - -Reviewed baseline: `01b08e76a3fecc22c4414437bfeaa07b4b7a85aa`. - -Status: all four findings resolved. This remains an audit record, not an -implementation plan; resolution evidence is recorded with each finding. - -The [interface remediation roadmap](interface-remediation.md) describes the -settled target and its dependencies, including a shared internal history model -for metadata history and explanation. The completed implementation and -integrated validation are recorded at -`07e4ae5:docs/roadmap/implementation.md`. -This audit retains the findings and original reproduction evidence. - -## Scope and evidence - -Reviewed the root-owned public facade and its conversions, engine loading and -callback boundaries, source/discovery and filesystem ownership, schema and typed -value handling, metadata/history, safe rendering and comparison, secrets, and -the document/YAML boundary against declaration comments, the -[configuration contract](../config.md), and applicable architectural decisions. -The root delegates to the synchronous engine lifecycle; parser-independent -documents support source assembly and inheritance, and completed results retain -detached configuration and frozen inspection state. - -The findings below were reproduced through the public API with a disposable Go -program outside the repository, using labeled byte sources and `fstest.MapFS`. -The full existing suite passed with Go 1.26.10 on Linux: - -```sh -GOTOOLCHAIN=local GOTELEMETRY=off GOPROXY=off go test ./... -count=1 -``` - -The existing tests cover many ownership, lifetime, error, and zero-value cases, -but do not detect these cross-interface discrepancies. No benchmarks, external -service calls, or new test files were needed. The recommendations restore -existing contracts rather than introduce new API restrictions or guarantees. - -## 1. Resolution presence queries confuse explicit keys with sequence indexes - -Priority: P2. Status: resolved. Evidence: `TestFacadeResolutionPresenceUsesDynamicEntryShape`, -the engine resolution/provenance/default regressions, and the integrated checks -recorded at `07e4ae5:docs/roadmap/implementation.md`. - -Relevant code: - -- [`Resolution.IsExplicit`](../../internal/engine/resolution.go), lines 75–82. -- `Resolution.InputInfo` and `resolutionInput.lookup` in - [resolution.go](../../internal/engine/resolution.go) and - [load_metadata.go](../../internal/engine/load_metadata.go). -- `resolveSchemaPath` in [schema.go](../../internal/engine/schema.go). - -The public [path contract](../../path.go) distinguishes explicit mapping keys -from sequence indexes. The [resolution contract](../config.md#recorded-application-resolution) -defines surviving-input presence and a fixed entry-state metadata view; -[metadata queries](../config.md#layered-metadata) preserve container shapes. - -`IsExplicit` validates against the schema but then looks up presence using only -`path.String()`. In dynamic `any` state, schema validation cannot establish the -actual container shape. Consequently these distinct queries collide: - -```go -key := configkit.Keys("shape", "0") -index := configkit.Keys("shape").Index(0) -``` - -Load `map[string]any` from `shape: [input]` with a resolver that queries both -paths, without changing the configuration. Observed results: - -| Query | `Resolution.IsExplicit` | `Resolution.InputInfo` found | Final `Metadata.IsExplicit` | -| --- | --- | --- | --- | -| Explicit mapping key | true | false | false | -| Explicit sequence index | true | true | true | - -The mapping-key path never existed and should not be reported as explicit. -Applications branching on presence receive inconsistent answers depending on -which public query they use, even during an otherwise unchanged load. - -Potential remediation: use the retained entry-state semantic shape when checking -resolution presence, consistently with `InputInfo` and `Metadata.IsExplicit`. -Review the same string-only presence lookup in `Default` while preserving its -existing assignment and callback-lifetime rules. Add focused public coverage for -both mapping-to-index and index-to-mapping mismatches under dynamic values, -including contextually parsed pointers. This does not require changing the -documented treatment of invalid schema paths. - -## 2. Resolution can transfer explicitness to a different semantic path - -Priority: P2. Status: resolved. Evidence: the engine resolution/metadata/provenance -regressions, the full engine suite, and the integrated checks recorded at -`07e4ae5:docs/roadmap/implementation.md`. - -Relevant code: `applyResolutionMetadata`, `applyResolutionFieldOrigins`, and -`refreshResolutionFields` in -[load_metadata.go](../../internal/engine/load_metadata.go), especially lines -371–387; final shape checking in -[`Metadata.effectiveField`](../../internal/engine/metadata.go). - -The [metadata contract](../config.md#layered-metadata) distinguishes key/index -shapes and keeps derived values for absent input non-explicit. Recorded -resolution changes origins without creating input explicitness. - -Reproduction: - -1. Load `map[string]any` from `shape: {'0': old}`. -2. In resolution, replace `configuration["shape"]` with `[]any{"new"}` and - record normalization at `Keys("shape")`. -3. Query final metadata using the two paths from finding 1. - -The old explicit mapping-key path correctly stops being effective, but the newly -created sequence index reports `Explicit: true`. That sequence index was never -supplied by input. `refreshResolutionFields` reuses the old `FieldInfo` whenever -its textual pointer survives; it updates null state without checking whether -the old and new containers give that pointer different semantic shapes. Final -lookup validates only the new shape, so it cannot recover the lost distinction. - -This can mislead consumers inspecting whether a final value was explicitly -configured rather than introduced by application resolution. The converse -sequence-to-numeric-key transition has the same underlying collision. - -Potential remediation: retain entry/effective shape evidence across resolution -and carry input explicitness forward only for the matching semantic path. -Preserve existing explicitness for same-shape paths and the recorded resolution -origin; do not clear every descendant's presence merely because an ancestor was -normalized. Cover both shape transitions and agreement among `Lookup`, -`IsExplicit`, and explanation. Fixing finding 1's callback query alone does not -repair this publication path. - -## 3. Metadata history mixes numeric-key and sequence-index definitions - -Priority: P2. Status: resolved. Evidence: `TestPublicHistoryUsesResolutionShapeAttribution`, -`TestPublicHistoricalPathsEnumerateContextualShapes`, shared history-selection -and explanation regressions, and the integrated checks recorded at -`07e4ae5:docs/roadmap/implementation.md`. - -Relevant code: - -- [`Metadata.History`](../../internal/engine/metadata.go), lines 56–65. -- `metadataFromDecoded`, `applyOverlayMetadata`, and history event construction - in [load_metadata.go](../../internal/engine/load_metadata.go). -- Shape-aware replay in [`Inspection.Explain`](../../internal/engine/explain.go). - -The [metadata contract](../config.md#layered-metadata) promises semantic -key/index distinctions for historical as well as effective queries. The -historical-path index retains both shapes, but `History` uses that index only to -check that the query was ever known. It then retrieves the entire history bucket -by textual pointer, without selecting the query's shape. - -Reproduction: load `shape: {'0': old}` into `map[string]any`, then apply a CLI -override at `Keys("shape")` with `[]any{"new"}`. Both explicit shapes were -observed and share `/shape/0`. - -`History(Keys("shape").Index(0))` incorrectly includes the original base -definition of the mapping key, and identifies that base origin as the previous -origin of the CLI operation. The old input defined a key, not an index. In the -same successful result, `Explain` for the explicit index correctly shows only -its CLI introduction. `History` for the key and index returns identical records -despite their different lifetimes. - -Consequences include false source attribution and disagreement between the two -public history interfaces when consumers inspect removed or introduced paths. - -Potential remediation: retain enough container-shape evidence with stored -history events, or select relevant events from existing shape-aware replay -evidence. Filter exact definitions and previous origins by the queried semantic -shape while retaining genuine ancestor replacement/removal events. Filtering -only on the current `Change.Path` is insufficient where construction already -replaced concrete segment kinds with parsed tokens. Preserve the intentional -one-pointer-per-entry `HistoricalPaths` enumeration and contextual behavior of -`ParsePath`; neither public contract needs redesign. Cover both direction changes -and compare metadata history with explanation. - -## 4. Diagnostic canonicalization breaks composition claimant ordering - -Priority: P3. Status: resolved. Evidence: `TestFacadeCompositionConflictKeepsClaimantOrder`, -the engine diagnostic-order regression, and the integrated checks recorded at -`07e4ae5:docs/roadmap/implementation.md`. - -Relevant code: `compositionConflictDiagnostic` in -[compose.go](../../internal/engine/compose.go) and `canonicalDiagnostics` in -[diagnostic.go](../../internal/engine/diagnostic.go), particularly lines 88–92. -The latter runs both while constructing `LoadError` and in `WriteDiagnostics`. - -The [strict-composition contract](../config.md#strict-document-composition) -specifies the first claim in scoped assembly order as primary and every remaining -claim in the same order as related locations. - -A native-filesystem fixture demonstrates the discrepancy: - -```text -main.yml imports a.yml, b.yml, c.yml -a.yml imports z.yml -b.yml, c.yml, and z.yml each define x -``` - -Acquisition assigns main=source-1, a=source-2, z=source-3, b=source-4, -c=source-5. Dependency-ready assembly considers the three conflicting claims -in b, c, z order. The composer correctly chooses b as primary and constructs -related locations c, z. Generic canonicalization sorts those related locations -by acquisition ID, publishing z, c instead. The reproduced public diagnostic -has primary source-4 and related source-3, source-5. - -All claimants remain present and the conflict still fails loading, so this is -lower impact than the presence/history defects. It nevertheless violates the -documented reporting order and gives consumers two different ordering rules -within one conflict report. - -Potential remediation: preserve the selected semantic ordering for composition -claimant lists through accumulation and rendering, while retaining canonical -ordering of diagnostic records and the established sorting of related locations -for diagnostics without a semantic-order contract. Add one public failure-path -case whose acquisition and claimant orders differ and verify both structured -diagnostics and rendered output. No scheduler or precedence change is needed. diff --git a/docs/roadmap/internal-boundaries.md b/docs/roadmap/internal-boundaries.md deleted file mode 100644 index 46ad1af..0000000 --- a/docs/roadmap/internal-boundaries.md +++ /dev/null @@ -1,186 +0,0 @@ -# Import Discovery And Facade Boundary Simplification - -Status: target defined; execution is tracked in the [implementation plan](implementation.md). -Baseline: `80cf23e7102fca2c5d7c7621e75f930a1ef1ecad`. - -This roadmap brings together the two refactors deferred until after the audit -remediation sprint: glob entry preparation (**C03**) and facade-to-engine -transport simplification (**F07**). Their original motivation is retained in the -[glob](future.md#1-separate-glob-entry-preparation-from-traversal) and -[transport](future.md#2-simplify-redundant-facade-to-engine-transport) future-work -entries and the [consolidated audit](https://gitea.maximumdirect.net/eric/ConfigKit/src/commit/453da684d7c7d3843a5f505f5c468b4c889dc6e2/docs/roadmap/audit.md). - -The goal is clearer responsibility and ownership with fewer redundant internal -representations. Both changes preserve the public API and configuration model. -They are independent work areas within one maintenance effort, not a new loading -workflow. The [implementation plan](implementation.md) owns concrete edits, -stages, validation commands, and execution progress. - -## Baseline And Targeted End State - -The preceding sprint repaired directory-failure classification and shared link -dependency evaluation, clarified acquisition handoffs, and removed unused -placement bridges. Those changes provide the baseline; this work does not repeat -or redesign them. - -| Area | Current baseline | Target | -| --- | --- | --- | -| Glob discovery | `selectGlobImportCandidates` interleaves traversal and set operations with substantial per-entry preparation. | The selector coordinates discovery; a cohesive private operation prepares a wildcard-discovered entry. | -| Facade transport | Import defaults pass through root conversion, an exported engine transport record, an equivalent private engine record, and repeated snapshots. Placement construction also uses setter-style bridges. | Root-owned public records convert explicitly to one engine defaults representation, with straightforward engine option/input construction and identifiable ownership copies. | - -The [architecture policy](../policy/architecture.md), [configuration -contract](../config.md), and public declarations remain authoritative. [ADR -0006](../adr/0006-public-facade.md) continues to govern the facade boundary. These -refactors do not require a new architectural decision or changes to its accepted -decision. The [component overview](../internal/overview.md) and [import -internals](../internal/imports.md) own implemented structure. - -## Glob Entry Preparation - -### Target Responsibility Split - -In [glob.go](../../internal/engine/glob.go), the selector continues to own pattern -validation, component advancement, literal-segment handling, directory -enumeration and its accounting, candidate union and sorting, final target -validation, exclusions, and requiredness. - -A private glob-specific preparation operation owns the patterned branch's work -for one directory entry: matching and hidden-entry filtering, lexical/access path -construction, containment checks, target inspection and discovered-link policy, -intermediate-directory eligibility, host/native access-path selection, labels, -and candidate construction. Its result distinguishes a selected candidate, an -ordinary skip, and a diagnostic. It receives the facts needed for that entry -rather than mutating the selector's candidate set or surrounding traversal state. - -Use the existing filesystem access and link-resolution helpers. Directory -selection already has `prepareImportDirectoryEntry`; it remains a separate owner -because its recursion, matching, absence handling, and traversal state differ. -Share a smaller operation only where it actually expresses the same semantics -and reduces duplication. No common visitor or discovery strategy is needed for -this extraction. - -### Behavior To Preserve - -Preserve the current distinctions and processing order documented by -[explicit imports](../config.md#explicit-imports) and exercised by the selection -tests: - -- Literal components remain explicit selections; discovery filters apply at - their existing wildcard boundaries. Moving matching or hidden filtering after - filesystem inspection would change which errors are observable. -- Keep lexical candidate identity separate from resolved access identity. - Native source identity and relative imports retain namespace spelling; host - access retains its existing canonicalization and containment behavior. -- Keep discovered-link follow rules, intermediate-directory eligibility, - missing-target handling, and structured diagnostic sites/classifications. - Do not make glob and directory rules identical as a side effect of extraction. -- Preserve deterministic candidate and error order, union/deduplication, - directory-entry accounting, and the existing point of source admission. - Final selected-file validation remains before exclusions where it is today; - exclusions do not hide discovery or resource errors. -- Retain the preceding sprint's distinction between missing-on-open directories - and failures after opening, along with operation-local completed-link reuse. - -The intended benefit is that a maintainer can understand traversal separately -from entry eligibility and identity preparation. This is a readability change; -it does not select a different search algorithm or claim a speedup. - -## Facade-To-Engine Transport - -### Target Representations - -The root keeps its public `ImportDefaults`, `PlacementMode`, `Options`, `Input`, -and `LayeredOptions` types. Explicit conversions remain appropriate at this -boundary; consumers must not acquire engine type identity, fields, or methods. - -Inside the engine, consolidate the equivalent `ImportDefaults` transport and -private `importDefaults` records into one presence-aware input-defaults -representation. Carry that representation through the ordinary and layered -options, normalized request, and acquisition policy instead of translating the -same five fields between equivalent engine records. - -Keep `resolvedImportPolicy` distinct: it represents concrete per-declaration -choices, including selector-specific rules, rather than another copy of optional -load-wide input. Likewise, registration compilation and the normalized loading -request have real responsibilities and are not replacement targets. - -Make placement and defaults straightforward to construct on the existing engine -option/input records. Remove decorator-style bridges made unnecessary by that -construction surface. Exporting fields or types within `internal/engine` where -needed by the root converter does not make them part of ConfigKit's supported -public API. Keep explicit enum conversion and preserve unknown values until the -existing option validation can report them; conversion must not silently choose -a default or move validation into facade setters. - -### Ownership And Copying - -Make the actual mutable ownership boundaries identifiable. Public load calls -continue to snapshot pointed-to default booleans and the pattern slice before -source acquisition, as specified by the [public -declaration](../../lifecycle.go). Preserve omitted, explicit false, nil pattern, -and non-nil empty pattern as distinct inputs where they are distinct today. - -Review the entire handoff, not just the duplicate type definitions: root -conversion in [conversion.go](../../conversion.go), ordinary construction in -[lifecycle.go](../../lifecycle.go), layered construction in -[layered.go](../../layered.go), engine options and normalization, acquisition -policy construction, and declaration resolution in -[import_spec.go](../../internal/engine/import_spec.go). - -Remove snapshots that merely recopy already-owned read-only defaults as they -cross those private representations. Keep copies needed when establishing -independent mutable ownership, including any independently owned resolved -policy. The target is explainable ownership and less redundant work, not an exact -copy count or removal of copying throughout the library. Shared defaults within -one load can be read-only by implementation discipline; this creates no new -caller immutability or concurrency requirement. - -Preserve ordinary and layered option preflight before source reads and application -hooks, independent declaration overrides, transitive/underlay propagation, -selected-overlay behavior, destination normalization, and invalid-enum -diagnostics. Keep callback, source, path, secret, and returned-record conversions -whose ownership or domain meaning is not redundant transport. - -The intended benefit is fewer concepts, field mappings, and construction paths -to maintain. Reduced copying is useful but does not establish an end-to-end -performance guarantee. - -## Documentation Ownership - -Internal documentation describes the resulting discovery and transport -boundaries once implemented. Public declarations continue to own the existing -snapshot and type contracts; this effort selects no new public behavior. -The corresponding C03/F07 future-work entries serve as navigation to this -roadmap rather than parallel target or progress inventories. Unrelated future -candidates remain separate. The implementation plan owns the documentation -updates and retirement of replaced private representations and helpers. - -## Validation And Completion - -Follow the [testing policy](../policy/testing.md) and [development -guide](../development.md#baseline-validation). Reuse the existing glob selection, -directory/link failure, import-default, placement, public facade, and dependency -boundary coverage. Add a focused behavioral case only where a concrete -extraction or ownership risk lacks protection. - -For discovery, verify literal versus wildcard filters, host/native identity, -contained and escaping links, optional absence versus read/close failures, -ordering, exclusions, regular-file checks, and existing resource accounting. For -transport, verify nil/empty and explicit-false distinctions, mutation of caller -default storage after the load snapshot through a supported callback, invalid -options before input consumption, both loading APIs, and transitive/underlay -defaults. Existing conversion-focused tests may change with private construction; -preserve the useful ownership behavior rather than fossilizing a helper's shape. - -Completion requires an easier-to-follow selector, one engine input-defaults -representation, fewer redundant mapping/construction sites, justified remaining -copies, and preserved public behavior under the relevant checks. Review source -and ownership flow as well as passing tests; moving complexity into a helper or -renaming a duplicate record alone is insufficient. - -No benchmark campaign, complexity score, allocation threshold, exact filesystem -call trace, new resource limit, or additional release gate is required. This work -adds no dependencies, caches, background activity, or new consumer controls, and -does not reopen other audit findings or deferred performance experiments. Routine -helper signatures, names, and file placement belong to the implementation plan; -no significant product or architectural decision remains open for this scope. diff --git a/docs/roadmap/remediation.md b/docs/roadmap/remediation.md deleted file mode 100644 index c9dab23..0000000 --- a/docs/roadmap/remediation.md +++ /dev/null @@ -1,523 +0,0 @@ -# Implementation Remediation Review Findings - -Current follow-up: the public API cleanup finding (9) is complete. -The earlier interface-remediation review and completed findings 1–8 below are -retained as historical evidence. The current review's scope and validation are -recorded with finding 9. - -Reviewed commit: `98c8b7c8423a816acdfceace36165366b13719ee`. -Algorithmic follow-up: `91769e9` (unchanged production code). -Comparison baseline: `0dd16aa8a935645ae2847d32edd86461dc4618f0` -(the planning commit, before the implementation sprint). - -Scope: the completed implementation plan at -`07e4ae5:docs/roadmap/implementation.md` and -[interface remediation roadmap](interface-remediation.md). This report records -remaining defects and newly introduced scalability problems; it does not change -the selected architecture or public contracts. All eight findings are complete; -implementation and validation evidence is recorded per finding. - -## Architecture And Review Evidence - -The public root-owned facade still delegates to the synchronous engine. A -per-load history builder now normalizes baseline, source, inheritance, typed -checkpoint, CLI, and resolution evidence. Metadata and inspection share its -frozen model, with separate public projections and detached query results. The -resolution callback retains a separate fixed entry-state view. Retained-root -admission remains before effective secret lookup and application validation. -The old independent metadata history and published inheritance copies are gone. - -The original presence/explicitness corrections and composition claimant-order -correction are present. The shared-history migration is substantial, but the -findings below prevent treating its behavioral preservation and indexed-query -targets as fully satisfied. No additional public restriction, guarantee, or -prohibition requiring reversal was identified in the reviewed changes. In -particular, private builder lifecycle guards are not new consumer obligations. - -Review combined code/plan inspection, graph-based producer/consumer tracing, -existing tests, and disposable public-API probes run against both revisions. -The first three findings were reproduced; the remaining five follow from the -new loops and index contents, without performance measurements. Probe programs -and the baseline checkout were kept outside the repository. Production code and -repository tests were not changed during the review. - -The following checks passed with Go 1.26.10 on Linux, using the installed toolchain -and cached dependencies: - -```sh -GOTOOLCHAIN=local GOTELEMETRY=off GOPROXY=off go test ./... -count=1 -GOTOOLCHAIN=local GOTELEMETRY=off GOPROXY=off go test -race ./... -GOTOOLCHAIN=local GOTELEMETRY=off GOPROXY=off ./scripts/check.sh -git diff --check -``` - -The passing suite does not detect the reproduced regressions. Suggested coverage -below targets those gaps rather than adding a general test matrix or new gates. - -The algorithmic follow-up checked normalization, lookup/index construction, -selection, replay dispatch, sensitivity preparation, and retained-root traversal. -It expanded finding 4 to the inheritance/resolution adapters and added findings -7–8. The existing focused engine history, explanation, inheritance, and secret -tests passed with `go test ./internal/engine -run 'History|Explain|Inheritance|Secret' -count=1`. -No benchmark data was collected. The selected shared model, ordered-ID sorting, -net resolution grouping, scratch-state normalization, whole-tree explanation -replay, and fixed-point sensitivity closure are not being reconsidered. The -deferred contributor-trace and branch-local replay experiments remain separate. - -## 1. Final Dynamic Field Tags Can Be Lost From Redaction - -Priority: P1. Status: complete. - -Completion: final inventory/sensitivity is registered unconditionally before -freezing. `TestFinalDynamicTagRedactsEqualValuedResolution` failed before the fix -and passes afterward, protecting effective and historical public rendering. -Validation: the full ordinary suite and `git diff --check` passed. - -Relevant code: `resolveLoadInput` in -[load_result.go](../../internal/engine/load_result.go), lines 56–73; -`freezeMetadataHistory` in [metadata.go](../../internal/engine/metadata.go), -lines 146–155; and `sensitivityCandidates` in -[history_select.go](../../internal/engine/history_select.go), lines 201–253. - -Resolution evidence is observed only when resolution produces snapshots. The -final projection is assigned to `model.final` but never unconditionally observed -for path/sensitivity facts. Classification now consumes model observations, -instead of also traversing the final projection as the baseline did. - -Reproduction through the public API: - -```go -type classified struct { - Token string `yaml:"token" configkit:"secret"` -} -type configuration struct { - Value any `yaml:"value"` -} - -result, err := configkit.Load[configuration]( - configkit.Bytes("input", []byte("value: {token: AUDIT-CANARY}\n")), - configkit.Options[configuration]{ - Definition: configkit.Definition[configuration]{ - Resolve: func(c *configuration, _ *configkit.Resolution) error { - c.Value = classified{Token: "AUDIT-CANARY"} - return nil - }, - }, - }, -) -``` - -The map-to-struct assignment leaves canonical values unchanged, so the existing -net-change audit accepts the load without a recorded normalization. The final -projection carries the concrete struct's secret tag, but no snapshot adds that -classification to the model. `WriteEffective` and `Explain(Keys("value", -"token"))` expose the canary, including historical explanation values. At the -baseline they redact it and report `Redacted: true`. - -Expected behavior follows the existing -[secret-classification contract](../config.md#secret-classification-and-effective-rendering): -concrete dynamic tags and final/historical classification remain effective. -The plan also expressly requires observing the final projection. This concerns -ordinary tagged configuration, not evidence of resolved credential bytes being -stored in history. - -Remediation: register final concrete path and sensitivity facts before freezing -and classification, independently of whether resolution recorded a value -change. Preserve pointer-conservative historical masking and dependency closure. -Do not require extra resolver records or reject equal-valued dynamic type -changes as a workaround. Add a public lifecycle regression for this case that -checks effective and historical redaction. - -## 2. Intermediate Source Definitions Are Duplicated In Metadata History - -Priority: P2. Status: complete. - -Completion: displaced definitions are omitted only when a preceding operation -already introduced that concrete path with the same represented origin. -`TestTierHistoryRecordsEachContributionOnce` failed before the fix and passes -for both differing and equal-valued tiers. The full ordinary suite and -`git diff --check` passed, including the existing shape-change regressions. - -Relevant code: `observeSourceEvidence` in -[history_source.go](../../internal/engine/history_source.go), lines 82–117; -and previous-origin construction in `selectHistory` in -[history_select.go](../../internal/engine/history_select.go), lines 108–117. - -Each overlay record unconditionally registers its displaced definitions. A -definition already represented by the preceding replacement operation is -therefore emitted again when the next tier displaces it. Selection chains -`Previous` through both records. The baseline's `appendInputOriginEvent` -suppressed an already represented origin; the new normalization does not apply -that rule to displaced definitions. - -Reproduction: load `map[string]any` with two ordered underlays containing -`x: low` and `x: mid`, and main input containing `x: high`. Query -`Metadata().History(Keys("x"))`. - -- Expected and baseline: three contributions, low → mid → high. -- Actual: four contributions, low → mid → mid → high. The second mid record - names the same source location as its own previous origin. - -This is not a genuine additional equal-valued write. It is one source -contribution registered twice, contrary to the plan's explicit preservation of -origin containment and duplicate suppression. - -Remediation: normalize displaced definitions against already represented -semantic operations in the shared model. Preserve genuine independent writes, -including equal values, and keep key/index shapes distinct. Add a three-tier -history regression checking source chronology and previous-origin links. - -## 3. Container Explanations Take The Last Descendant Replacement's Origin - -Priority: P2. Status: complete. - -Completion: explanation uses the model's direct query-scoped contribution when -available and reserves operation fallback for paths without that contribution. -`TestOverlayExplanationUsesQueryScopedOrigin` failed before the fix and now -checks root, enclosing mapping, and leaf locations. The full ordinary suite and -`git diff --check` passed, including removed/replaced descendant coverage. - -Relevant code: `historySelectionForApplication` in -[explain.go](../../internal/engine/explain.go), lines 183–207; and descendant -selection in [history_select.go](../../internal/engine/history_select.go). - -The explanation helper obtains a direct application selection, then overwrites -it with every selected replacement linked to that application. For a container -query, multiple child replacements can be relevant; the last one supplies the -origin for the entire application event, regardless of the query's scope. - -Reproduction: main YAML is: - -```yaml -a: base -group: {x: X, y: Y} -``` - -Select an overlay containing: - -```yaml -a: overlay -group: {x: O} -``` - -The overlay event in `Explain(Keys("group"))` is attributed to the `O` scalar -(line 2, column 12), instead of the contributing mapping (baseline: line 2, -column 8). The root explanation also attributes the whole overlay event to -that scalar, despite the independent change to `a` (baseline: document root, -line 1, column 1). Values are correct; the contribution location is not. - -The roadmap requires retaining source attribution and using common semantic -selection, while allowing different record grouping between public projections. -It does not authorize replacing enclosing attribution with an arbitrary leaf. - -Remediation: resolve application attribution at the requested semantic scope in -the common selection/attribution path. Prefer a valid direct contribution for -an enclosing query; retain replacement fallback where it is needed for removed -or replaced descendants. Do not restore a second independent explanation -attribution system. Cover enclosing root/mapping and precise leaf attribution -in one focused overlay regression. - -## 4. History Construction Repeatedly Performs Unindexed Mapping Searches - -Priority: P2. Status: complete. - -Completion: checkpoints, inheritance sides, and resolution before/final states -use one immutable document lookup per stable state, rather than scanning broad -mappings for every path. Lookups are rebuilt after scratch replay changes. -Removed the unused summary contribution list and its searches; the existing -test still verifies composite metadata and individual explanation attribution. -The full ordinary suite and `git diff --check` passed, including collection, -inheritance, resolution shape, and typed-checkpoint cases. - -Relevant code: `observeTypedCheckpoint` in -[history_cli.go](../../internal/engine/history_cli.go), lines 38–49; -`sourceContributions` in [history_source.go](../../internal/engine/history_source.go), -lines 185–218; `observeInheritanceTransfer` and `documentAtInheritancePath` in -[history_inheritance.go](../../internal/engine/history_inheritance.go), -lines 87–141 and 211–217; `historyConcretePathAt` and its callers in -[history_resolution.go](../../internal/engine/history_resolution.go); and the lookup implementation in -[document_walk.go](../../internal/engine/document_walk.go). - -For every checkpoint path, construction calls `documentAtPath` on both trees. -That helper supplies no lookup index, so each mapping step linearly searches -its entries. For a flat mapping with n fields, locating all n children requires -Theta(n²) entry comparisons. Both checkpoints run even on ordinary loads with -no CLI overrides. Source-summary construction introduces the same pattern: -it calls `sourceContributions` for surviving paths, and that helper searches -each raw application again without an index. - -The new inheritance adapter similarly enumerates every before/parent/effective -path and then calls `documentAtInheritancePath` for each one. That helper falls -back to the same unindexed search: one inheritance transfer of a broad n-field -mapping incurs Theta(n²) sibling comparisons. Resolution normalization calls -`historyConcretePathAt` before and after for each record; its hand-written -mapping scan costs O(rn) for r records under an n-field mapping, or Theta(n²) -when all n fields are independently recorded. These are the same lookup problem, -not separate findings about the selected inheritance or resolution algorithms. - -These are new repeated sibling scans, not the deliberately retained whole-tree -explanation replay. Existing document lookup machinery or paired traversal can -avoid them. Existing sorting and genuinely repeated producer evidence still -have their own costs; no universal linear-time guarantee is implied. - -Remediation: build/reuse phase-local lookups or collect corresponding nodes -during shared traversal instead of independently searching a broad mapping -for every child or record. Respect scratch-tree mutation boundaries: do not -reuse a stale lookup after replacing or editing its indexed state. Concrete -resolution paths must still reflect the corresponding before/after containers. -Review the summary membership work as well: the new -`historySourcePayload.contributions` list is populated and tested but currently -has no production reader. Preserve any membership the selected design needs, -but avoid computing unused data through repeated searches. Retain behavior -coverage; do not add timing thresholds or implementation-specific scan counts. - -## 5. Prefix Indexing Multiplies One Operation By All Its Descendants - -Priority: P2. Status: complete. - -Completion: indexes locate concrete affected paths rather than placing every -descendant at the reported ancestor. Exact path buckets drive source-origin -containment (with early success); enclosing queries visit only their affected -subtree. Logical operations remain payload references, deduplicated once by the -common selector. Thus a leaf no longer scans sibling facts of an ancestor -replacement. The full ordinary suite and `git diff --check` passed, including -parsed key/index sides, removed descendants, inheritance, and enclosing queries. - -Relevant code: `addObservation` in -[history_model.go](../../internal/engine/history_model.go), lines 116–139; -replacement observation construction in -[history_source.go](../../internal/engine/history_source.go), lines 101–116; -`sourceOriginRepresentedByOperation` in that file, lines 245–263; and candidate -collection in [history_select.go](../../internal/engine/history_select.go), -lines 35–53. CLI and resolution effects use the same operation index. - -An ancestor replacement registers before/after facts for every affected -descendant, indexing every distinct observation ID at the same reported -operation pointer. The prefix index consequently holds O(n) entries for one -logical replacement of an n-node subtree, not one indexed operation plus -shape-specific affected-path lookup. - -During source normalization, each surviving descendant calls -`sourceOriginRepresentedByOperation`. It visits all those ancestor entries and -re-resolves the same operation attribution, even after finding a match. One -large subtree replacement therefore creates Theta(n²) visits. Later, a single -leaf `History` query gathers and sorts all n entries at its enclosing operation -before rejecting unrelated affected paths. Querying n such leaves performs -O(n² log n) candidate work despite each leaf's short returned history. - -Remediation: distinguish logical operation indexing from exact affected-path -and before/after-shape indexing. Visit operation attribution once, then locate -the query's relevant shape facts rather than expanding every sibling fact. -Preserve parsed-query deduplication, removed descendants, ancestor replacement -effects, and enclosing queries. This should implement the selected localized -candidate-query design, not replace the existing prefix mechanism wholesale or -coarsen semantic identity. - -## 6. Sensitivity Candidate Deduplication Cannot Match Parsed Paths - -Priority: P3. Status: complete. - -Completion: candidate and dependency deduplication uses structural segment -equality, including parsed tokens, without conflating explicit keys and indexes. -`TestSensitivityCandidatesMergeRepeatedParsedPaths` failed before the fix and -now verifies unique semantic/dependency candidates and merged sensitivity facts. -The full ordinary suite and `git diff --check` passed. - -Relevant code: `sensitivityCandidates` and `appendDistinctHistoryPath` in -[history_select.go](../../internal/engine/history_select.go), especially -lines 207–234; and `sameConcreteHistoryPath` in -[history_model.go](../../internal/engine/history_model.go), lines 271–294. - -The candidate collector adds a parsed version of every observation's pointer, -then uses a concrete-only comparator to deduplicate candidates. That comparator -returns false for a parsed segment, including comparison of a non-root parsed -path with itself. Repeated observations of one path therefore append repeated -parsed candidates instead of merging them. Parsed resolution dependencies have -the same problem in `appendDistinctHistoryPath`. - -For k observations of one non-root pointer, the per-pointer candidate list grows -with k and insertion repeatedly scans it: Theta(k²) comparisons and unnecessary -retained temporary paths/dependency entries. The outer classifier later uses -structural equality to suppress duplicate predicate candidates; this finding -does not establish repeated public predicate calls or another redaction leak. - -Remediation: use structural path equality that handles parsed segments for -candidate/dependency deduplication, while retaining concrete-only comparison -where concrete inventory requires it. Preserve distinct parsed, explicit-key, -and explicit-index candidates until classification is unioned by pointer. -Extend focused candidate/dependency coverage for repeated parsed paths without -asserting timing or promising a new public call-count contract. - -## 7. Explanation Dispatch Repeatedly Searches All Selections And Payloads - -Priority: P2. Status: complete. - -Completion: a query-local payload-reference index and application-fallback map -are built in one pass over ordered selections. Source/CLI, inheritance, and -resolution replay use direct lookups while retaining model chronology and the -query-scoped attribution correction. The tier-history regression now also -checks explanation contribution order/origins. The full ordinary suite and -`git diff --check` passed; no replay algorithm or shared-state lifetime changed. - -Relevant code: `Inspection.Explain`, `historySelectionForPayload`, -`historySelectionForApplication`, and `historySelectionForResolutionGroup` in -[explain.go](../../internal/engine/explain.go), especially lines 64–71, -106–113, and 174–232; and `appendInheritanceExplanation` in -[inheritance_explain.go](../../internal/engine/inheritance_explain.go). - -After the model has selected relevant evidence, each source/CLI application -linearly searches the selection list, scans the entire phase payload list to -find linked replacement records, and searches selections again for each linked -record. Inheritance steps and resolution group members also repeatedly search -the same selection slice. Payload references already provide stable, -comparable keys; these are lookup operations, not semantic comparisons. - -For A applications, P phase payloads, S selections, and R linked replacements, -source/CLI dispatch alone costs O(A(P+S) + RS). With m successive underlay tiers -each setting the same scalar in a constant-size configuration, A, P, and S all -grow with m: this introduces Theta(m²) dispatch work even though the existing -value replay and rendered history for that query have O(m) size/work. It is -independent of finding 5's candidate expansion and remains after that index is -repaired. The inheritance/resolution lookup loops have the corresponding -operation-count times selection-count cost. - -Remediation: index the ordered selections by `historyPayloadRef` once per query -and index or group replacement-to-application links once, rather than rediscover -them for every application. Reuse that lookup for inheritance substeps and -original resolution record IDs. Dispatch can then be linear in the selected -records and traversed relationships, excluding the deliberately selected sorting, -replay, cloning, and rendering costs. Keep replay chronology unchanged and -resolve the attribution precedence correctly with finding 3 rather than -preserving its last-descendant bug. Use query-local or eagerly frozen indexes; -no lazy mutation of published history is needed. Existing explanation owners -should protect output equivalence without a timing assertion. - -## 8. Sensitivity Preparation Expands One Dependency List Per Affected Path - -Priority: P2. Status: complete. - -Completion: ordinary dependency lists are expanded once per recorded target/ -attribution, and dependency candidates once per attribution, without descendant -dependency-list copies. Classification supplies already computed inheritance -links to candidate preparation, avoiding a second ordinary-edge construction. -`TestResolutionSensitivityKeepsLogicalEdgesWithoutSubtreeDuplication` failed -before the fix and now verifies the original edges, including a coalesced child -record, and sensitivity isolation from unrelated paths. The full ordinary -suite, full race suite, everyday check, and `git diff --check` passed. The -original public redaction/history probes also now produce the expected results. - -Relevant code: `sensitivityCandidates`, lines 238–247, and -`sensitivityDependencies`, lines 272–286, in -[history_select.go](../../internal/engine/history_select.go); -`observeResolutionEvidence` and `observeResolutionSubtree` in -[history_resolution.go](../../internal/engine/history_resolution.go); and -`classifySecretPathsWithEvidence` in [secret.go](../../internal/engine/secret.go). - -One original resolution record owns one dependency list, and all its before/ -after subtree observations share its attribution reference and reported target. -Nevertheless, `sensitivityDependencies` resolves and clones that attribution -and emits its full dependency list for every observation. For a single recorded -normalization of an n-node subtree with d distinct dependencies, this creates -Theta(nd) ordinary edges although there are only d logical target/dependency -pairs. With n and d growing together, edge materialization alone is quadratic -in the O(n+d) underlying evidence. Those duplicates also become separate -triggers in the unchanged sensitivity-closure implementation. - -The candidate collector repeats the attribution dependency loop for every -affected path and retains a dependency list on each concrete candidate, although -its consumer uses those lists to register dependency paths, not distinct -descendant-target edges. It also calls `sensitivityDependencies` a second time -for inheritance links, rebuilding and discarding the ordinary edge list. These -costs remain even if finding 6's parsed-path comparator is corrected. - -Remediation: enumerate ordinary dependencies once per logical recorded target/ -attribution, or directly from the original resolution payload, and register each -required dependency candidate without broadcasting its list to all descendant -observations. A single record's edge preparation should require O(n+d) traversal -and at most O(d) ordinary edges, apart from path/annotation sizes and unrelated -evidence. Avoid rebuilding ordinary edges merely to obtain inheritance links. -Preserve original records omitted by explanation grouping, concrete/parsed -predicate candidates, dependency order where observable, and existing -pointer-conservative propagation. The fixed-point algorithm and inheritance -link semantics need not change. Reuse transitive/cyclic sensitivity regressions -and add focused coverage for a subtree record with several dependencies; no -performance threshold or new public dependency restriction is warranted. - -## 9. Source Lookup Test Compares Canonical Paths With Symlinked Fixture Paths - -Priority: P2. Status: complete. This is a test portability defect, not a production -source-lookup defect. - -Completion: `TestResultSourceLookup` canonicalizes its fixture directory with -`filepath.EvalSymlinks` before creating files, preserving all source-attribution, -redaction, and detachment assertions. The focused test failed with a symlinked -temporary root before the fix. Both source-lookup tests now pass with `TMPDIR` -pointing to either the real directory or its symlink. The everyday -`./scripts/check.sh` also passed. Production code and behavior are unchanged. - -Reviewed revision: `69bd9ead53b0bd1d4b325de2f357b566291a4803`. -Comparison baseline: `556b10b2cd32cbe3240ced79f72bfcc2fe1ebc35`. -Scope: the completed [API cleanup plan](implementation.md) and -[accepted roadmap](api.md), including facade conversions, recording intents, -source lookup, observable string migrations, maintained examples, and current -documentation. Existing findings 1–8 were not reopened. - -Relevant code: `TestResultSourceLookup` in -[source_lookup_test.go](../../source_lookup_test.go), lines 23–26 and 62–74. - -The test identifies imported and underlay sources by comparing `SourceInfo.Path` -with the original paths returned by its fixture writer. Host source paths are -canonicalized by ConfigKit, but `t.TempDir()` can return a path containing a -symlink when the temporary-directory environment names one. The same files then -have different lexical path spellings. Both records are present with the correct -layers and version evidence, but neither comparison matches, so the test fails -with "imported or underlay source evidence missing". - -This is reproducible on Linux with a symlinked `TMPDIR`; it does not depend on -access to macOS or establish a new platform-validation gate. The failure-path -lookup test in the same file already uses `filepath.EvalSymlinks` for its fixture -path and does not have this particular comparison defect. - -Reproduction from the repository root, with disposable files outside the checkout: - -```sh -probeRoot=$(mktemp -d /tmp/configkit-source-review.XXXXXX) -mkdir "$probeRoot/real" -ln -s "$probeRoot/real" "$probeRoot/link" -TMPDIR="$probeRoot/link" GOTOOLCHAIN=local GOPROXY=off GOTELEMETRY=off \ - go test . -run '^TestResultSourceLookup$' -count=1 -``` - -Expected: the focused test passes for either spelling of a valid temporary -directory, retaining its source-evidence assertions. Actual: it fails at line 74 -with canonical records under the real directory and expected paths under its -symlink. The same focused test passes with `TMPDIR` set to the real directory. -This false failure can prevent ordinary development/release checks from completing -in an otherwise valid environment; it does not indicate lost source evidence. - -Remediation: canonicalize the test's fixture directory before creating its files, -or resolve the imported/underlay expected paths with `filepath.EvalSymlinks`, as -the failed-load case already does. Preserve record attribution and detachment -assertions. Do not change production canonical-path behavior, forbid symlinked -temporary directories, or add a platform/CI gate. Verify the existing focused -test with both real and symlinked temporary roots after correction. - -Other review results: the root inventory contains the expected 370 exported -declarations/members, with exactly the accepted name/signature/value changes -against the prior 372-entry catalog. The two fixed recording methods and the -existing assignment helpers are the only `addRecord` callers. Source lookup -clones retained records using existing ownership/sanitization machinery. No other -required architectural gap, production regression, or inadvertently strengthened -public contract was identified in this sprint. - -Validation passed on Linux with Go 1.26.10 and cached dependencies: - -```sh -GOTOOLCHAIN=local GOPROXY=off GOTELEMETRY=off ./scripts/check.sh -GOTOOLCHAIN=local GOPROXY=off GOTELEMETRY=off go test -race ./... -GOTOOLCHAIN=local GOPROXY=off GOTELEMETRY=off \ - go test . -run 'SourceLookup|ResolutionRecordingIntents|TestPublicFacadeBoundary|TestDependencyImportBoundary|TestLocalDocumentationLinks' -count=1 -``` - -The additional symlink-root invocation above failed as described. Production -code and repository tests were not changed during this review. diff --git a/docs/roadmap/review.md b/docs/roadmap/review.md deleted file mode 100644 index 095ad19..0000000 --- a/docs/roadmap/review.md +++ /dev/null @@ -1,793 +0,0 @@ -# Public API Catalog And v1.0 Review - -Reviewed revision: `fb71421956ba6b431bdc8745dcf7ab02607dac3a`. -Status: discussion draft; no API changes are selected or implemented by this review. - -## Scope And Method - -The supported consumer surface is the root package -`gitea.maximumdirect.net/eric/ConfigKit`, package name `configkit`. -[ADR 0006](../adr/0006-public-facade.md) owns root type identity and the private -implementation boundary. Exported engine names are not consumer API. - -This catalog includes every exported root type, function, method, struct field, -and constant in the current build: **372 entries** comprising **54 types, -31 functions, 36 methods, 116 fields, and 135 constants**. There are no exported -package variables, named interfaces, type aliases, or anonymously embedded public -fields. Of the constants, 104 are diagnostic codes: the symbol count does not -mean that consumers must learn 372 independent operations. - -Inventory was extracted from the root non-test Go declarations, cross-checked -against the package's build-selected files and `go doc -all .`, and reviewed -against declaration comments, the [configuration contract](../config.md), -[consumer examples](../consumers/README.md), the -[architecture policy](../policy/architecture.md), and relevant accepted ADRs. -This is an API-design review, not another correctness or performance audit. - -Each catalog row has an assessment addressing clarity, ergonomics, and/or -consolidation. **Retain** means no revision is currently warranted, not a new -invariant or a promise to preserve private mechanics. **Discuss Dn** identifies -a possible pre-v1 naming/removal/signature decision below; **An** is an optional -additive idea. Struct declarations show their kind without private fields; all -exported fields are separate rows. Callable signatures include generic constraints -and receiver types. Constants include their current types and values. - -Go declaration comments and the configuration contract remain the canonical -behavior owners. This roadmap is a revision-pinned review record, not a second -permanent API reference. Nothing here creates new release gates or consumer -requirements. Under the [release policy](../policy/release.md#versioning-and-compatibility), -v1 establishes compatibility expectations; it does not prevent later compatible -additions. The highest-value decisions now are potential renames/removals that -would otherwise require a later incompatible release. - -## Overall Assessment - -The facade has a coherent core: explicit source selection, one complete load, -application-owned rules, separate presence/provenance, and safe frozen inspection. -Its private implementation can evolve without changing public type ownership. -Most of the API should remain. The useful cleanup opportunities are concentrated -in vocabulary and small redundant state, not a wholesale loading framework. - -Prioritize D1–D4: these names can encourage an incorrect interpretation of the -state a consumer receives. D5–D7 are useful additional naming/signature discussions; -D8 is a small surface-hygiene question. A1–A2 can wait unless a concrete consumer -need makes them useful now. These are recommendations for discussion, not approved -changes or a declaration that v1 must wait for every suggestion. - -## Pre-v1 Discussion Candidates - -### D1. Name the fixed resolution view as entry state - -Affected: `Resolution.InputInfo`; the documentation of `Resolution` and -`FieldInfo`. - -The lookup includes retained typed defaults and remains fixed while the resolver -changes the working configuration. “Input” can therefore suggest either raw -authored values or the mutable current value, neither of which is its complete -meaning. - -Recommendation: consider renaming the method to `EntryInfo` and consistently -calling this the resolution-entry view. Keep its return type and silent -unknown/shape-mismatch result. Keep `IsExplicit` as the convenient surviving-input -question with its existing path-validation behavior. - -Alternative: retain `InputInfo`, whose comment already explains the contract, -and improve the nearby example. That avoids consumer migration and is defensible -if the terminology is already well understood. Do not expose the growing history -model through the callback or collapse entry and final metadata to solve a naming -problem. - -### D2. Distinguish sensitivity classification from secret references - -Affected: `Definition.Secret`; related terminology around `Secret` and -`ValueSnapshot.Redacted`. - -The function marks arbitrary semantic paths sensitive, including ordinary strings -and containers. It neither creates nor resolves the typed environment references -represented by `Secret`. - -Recommendation: consider `Definition.Sensitive func(Path) bool`. This makes a -definition such as `Sensitive: func(path Path) bool { ... }` describe its purpose -without implying credential lookup. Preserve the existing tag/predicate union, -historical masking, and reference semantics. - -Alternative: keep `Secret` on the field because it aligns with the existing -`configkit:"secret"` tag and the established documentation. The current name is -not functionally wrong. No tag rename, new classification policy, credential -provider, or declassification option follows from this discussion. - -### D3. Make version-policy and version-evidence names literal - -Affected: `SourceVersionModeDefault`, `SourceVersionCheck`, and -`SourceInfo.VersionCheck`. - -“Default” describes an implementation/history relationship rather than the -selected main-only behavior. A “Check” record can exist for an absent declaration -without proving that its checker ran or accepted a value. - -Recommendation: consider `SourceVersionModeMainOnly` for the existing zero mode, -`SourceVersionEvidence` for the record, and `SourceInfo.VersionEvidence` for the -field. Keep all existing mode values, invocation/absence rules, source-local -coordinates, consumption, and redaction behavior. In particular, an if-present -mode still has the documented main-absence checker behavior; naming cleanup -must not silently change it. - -Alternative: retain the names and make their precise meanings prominent in -declaration comments and examples. “Default” is familiar for zero enums, and -existing consumers may already understand the evidence qualification. Do not add -an “accepted” boolean unless ConfigKit actually chooses to retain that fact under -a separately agreed contract. - -### D4. Describe the empty-import-selection subset and remove redundant state - -Affected: `ImportSelection`, `SourceInfo.ImportSelections`, -`ImportSelection.Outcome`, `ImportSelectionOutcome`, and `ImportSelectionNone`. - -These records describe optional declarations that selected no source. They are -not a complete selection ledger. Their outcome has exactly one supported value, -`"none"`; successful acquisition is represented elsewhere by source records. - -Recommendation: consider `EmptyImportSelection` and -`SourceInfo.EmptyImportSelections`, retaining path, location, and selector. -With that explicit scope, remove the constant-only outcome field/type/constant. -This reduces the public model without losing information that these records -currently communicate. - -Alternative: retain the broad names and explicit outcome. It leaves room for -compatible additional outcomes and avoids migration, but consumers must learn -that the collection currently reports empty selections only. Future flexibility -alone should not imply an approved complete event-history feature. - -### D5. Describe destination fallback evidence more precisely - -Affected: `SourceInfo.DestinationUnresolved`. - -The destination can be a useful nearest resolved enclosing container. The flag -does not mean every aspect of placement is unresolved, and a source's own item -container can be an exact destination without publishing its item index here. - -Recommendation: consider `DestinationIsFallback` with the existing meaning. -Retain `Destination` and `PlacementMode`; another placement object/state machine -is not needed just to clarify this boolean. - -Alternative: retain `DestinationUnresolved` and its detailed comment. It is -already specific enough for informed consumers. Neither option adds exact item -coordinates, identity tracking, or extra source-location guarantees. - -### D6. Separate manual recording intents from inspection-only operation kinds - -Affected: `Resolution.Record` and its use of `ChangeKind`. - -Four named operation values are exported, but only derived-default and -normalization are valid manual recording intents. Decoding and inheritance -describe library processing. A caller can choose an apparently suitable exported -constant only to receive a load-time invalid-kind diagnostic. - -Recommendation for discussion: consider replacing manual `Record` with -`RecordDerivedDefault(path Path, reason string, dependencies ...Path)` and -`RecordNormalization(path Path, reason string, dependencies ...Path)` methods. -Keep the generic assignment helpers `Default` and `Normalize`, which do different -work, and keep all inspection `ChangeKind` values. The explicit methods remove a -misleading input choice without changing the two accepted intents. - -Alternative: retain the single method and prominently document its accepted -subset. A separate input-only kind type is another viable design, but adds a -second enum and conversion vocabulary; it is less attractive unless consumers -need to select recording intent dynamically. Do not change net-change auditing -or impose new recording obligations through this cleanup. - -### D7. Make environment mutation evident in the bootstrap function name - -Affected: `LoadSecretsDir`. - -Unlike ordinary `Load`, this explicit startup operation writes previously unset -process environment variables and can report partial application failure. - -Recommendation: consider `BootstrapSecretsDir`. It follows the repository's -existing bootstrap terminology and makes the distinction from configuration -loading easier to notice. Retain its report-plus-error return and all current -preflight, skip, partial-failure, and ownership behavior. - -Alternative: retain `LoadSecretsDir`, whose comment clearly states the side -effects. Avoid a broad new bootstrap options framework or merging this operation -into ordinary loading. The purpose is naming clarity, not stronger filesystem -or process-transaction guarantees. - -### D8. Resolve the intent of the unproduced options diagnostic code - -Affected: `CodeOptionsUnavailable`. - -The scoped implementation search found its root and engine declarations but no -named production producer. That is a reason to ask about its intended role, not -evidence that an exported symbol has no consumers. - -Recommendation: establish whether there is an intentional supported/reserved case; -remove the constant before v1 if there is none. Do not invent a new failure merely -to give it a producer. - -Alternative: retain it as deliberately reserved classification if there is a -concrete reason. Document that intent rather than automatically deleting it. -The other diagnostic families should not be collapsed just to reduce the count. - -## Optional Additive Ideas - -### A1. Direct source lookup on successful results - -`Resolution.Source(id)` and both `SourceContext(id)` methods accept a source ID, -while a final result offers only `Sources()` for source records. A consumer -following many origin locations must scan or index that slice itself. - -Consider `Result.Source(id string) (SourceInfo, bool)` with the same detached, -passive lookup meaning as the resolution query. Prefer it over a general source -registry abstraction if representative consumers repeatedly need this lookup. -Keeping only the ordered slice is a viable smaller API for consumers that -enumerate once. This is an addition that can follow v1; it need not block the -existing API freeze. - -### A2. Path comparison without losing segment meaning - -`Path.String()` is a JSON Pointer spelling, not a complete semantic identity: -an explicit numeric mapping key and an index can share it. The public type is -opaque and currently non-comparable, so consumers cannot directly compare its -segment choices without relying on reflection or private representation. - -If consumers need path comparison, consider an explicitly documented comparison -operation. Decide whether it means exact segment representation (including -parsed-versus-explicit choice and invalid state), or resolution against a -document; those are different operations. Prefer a small exact-representation -operation over exposing mutable segments or adding another pattern language. -Do not label pointer-string equality semantic equality. - -Alternatively keep Path solely as an opaque query/building value and clarify -the limitation in examples. No concrete need currently justifies a public -segment-inspection framework. A comparison method can be added compatibly later. - -## Consolidation Assessment - -| Area | Assessment | -| --- | --- | -| C1: ordinary versus layered loading | Keep two entry points. Ordinary options combine one input's choices with load-wide rules; layered Input owns the former and LayeredOptions the latter. Reusing one options type would introduce ignored/competing fields. Anonymous embedding also makes keyed composite literals less convenient. Existing Definition, ImportDefaults, Input, and Override sharing is useful consolidation already. | -| C2: filesystem vocabulary | Keep Filesystem (borrowed identity), FilesystemContext (input roots), and ResourceContext (passive returned access) distinct. FS means different concrete types because the roles differ. Clarify with paired host/native examples, rather than introducing one universal context or changing ownership. | -| C3: history versus explanation | Keep Change and ValueChange, Metadata.History and Result.Explain distinct. Metadata has composite/previous attribution; explanation has safe values and decoding/net replay. Their shared private evidence is the correct consolidation boundary. | -| C4: semantic path versus sequence selector | Keep Path and SequenceSelector distinct. One describes concrete/contextual queries, the other limited mapping-key wildcard registration. Unifying them would broaden grammar and validation for little current benefit. | -| Errors and renderers | Keep LoadError and SecretsError separate because bootstrap can accompany partial external effects. Keep structured diagnostics plus safe rendering. SecretsReport.WriteTo follows io.WriterTo; other renderers need not return byte counts merely for symmetry. | -| Presence records | Keep FieldInfo, Version and ValueSnapshot's purpose-specific booleans. A generic optional-value/tri-state framework would add vocabulary without simplifying the application configuration structs. | -| Result ownership | Keep caller-owned Config and frozen inspection. A copying accessor would not make arbitrary T language-enforced immutable and would add work; emphasize the established snapshot distinction instead. | - -## Related Consumer Integration Contracts - -The Go symbols are not the entire compatibility commitment. These established -integration surfaces also matter to v1, but are defined by their canonical -contracts rather than counted as additional exported Go declarations: - -| Surface | Owner and assessment | -| --- | --- | -| Configuration structs, YAML field tags, standard text codecs, defaults and typed decoding | [Schema binding](../config.md#application-schema-binding), [defaults](../config.md#defaults-and-typed-decoding), and [projection](../config.md#typed-projection-and-ownership). Retain ordinary application types and supported standard codec integration; do not add parser-specific public hooks. | -| YAML declarations, composition, imports, overlays and sibling inheritance | [Configuration contract](../config.md) and accepted ADRs linked there. Retain the established coherent language; this review does not propose reserved-key or merge-policy changes. | -| Presence, provenance, recorded resolution and validation | [Metadata](../config.md#layered-metadata), [resolution](../config.md#recorded-application-resolution), and [validation](../config.md#semantic-validation). Retain the distinctions that remove consumer bookkeeping; naming discussions do not change these semantics. | -| Secret references, classification and explicit environment bootstrap | [Secret references](../config.md#typed-secret-references), [classification](../config.md#secret-classification-and-effective-rendering), and [bootstrap](../config.md#explicit-secrets-directory-bootstrap). D2/D7 concern API vocabulary, not a new credential or security model. | -| One-shot completion, resource ownership, safe rendering and comparison | [Completion](../config.md#completion-and-resource-lifetime), [source contexts](../config.md#source-resource-contexts), [explanation](../config.md#historical-value-explanation), and [diffs](../config.md#effective-configuration-diffs). Retain established lifetimes and safe snapshot boundaries rather than adding watching, lazy work or a required result Close operation. | - -## Complete Catalog - -All rows below assess the current declarations, not the proposed replacement -names. No proposed symbol is counted in the 372-entry inventory. - -### Loading and application policy - -Declarations: [layered.go](../../layered.go), [lifecycle.go](../../lifecycle.go). - -| Entry | Declaration/type | Assessment | -| --- | --- | --- | -| `Input` | `type Input struct` | Retain a source plus placement/import/access settings; zero Source is invalid, unlike explicit NoSource. | -| `Input.Source` | `Source` | Retain the opaque source union; no provider interface or parser node exposure. | -| `Input.Destination` | `Path` | Retain the same root-relative placement meaning as ordinary options. | -| `Input.PlacementMode` | `PlacementMode` | Retain the same explicit placement enum as ordinary options. | -| `Input.Imports` | `bool` | Retain per-input traversal choice; it differs from load-wide declaration defaults. | -| `Input.Filesystem` | `*FilesystemContext` | Retain per-input access context, not a global context for unrelated namespaces. | -| `Underlay` | `type Underlay struct` | Retain a named strict tier; flattening all inputs would lose the peer-versus-precedence boundary. | -| `Underlay.Inputs` | `[]Input` | Retain ordered inputs in one strict group; an empty group remains a no-op. | -| `LayeredOptions` | `type LayeredOptions[T any] struct` | Retain separate layered entry-point options; input-level settings already belong to Input (C1). | -| `LayeredOptions.Definition` | `Definition[T]` | Retain the same Definition type as ordinary loading. | -| `LayeredOptions.SourceVersionMode` | `SourceVersionMode` | Discuss D3's mode name; retain one rule across the load rather than per-source policy switches. | -| `LayeredOptions.ImportDefaults` | `ImportDefaults` | Retain the shared type and one load-wide default policy. | -| `LayeredOptions.Underlays` | `[]Underlay` | Retain ordered low-to-high strict groups; no separate precedence numbers are needed. | -| `LayeredOptions.Overlay` | `string` | Retain the same selected-overlay concept as ordinary loading. | -| `LayeredOptions.OverlayFilesystem` | `*FilesystemContext` | Retain its explicit overlay-only purpose; merging it into main input context would change behavior (C1/C2). | -| `LayeredOptions.Overrides` | `[]Override` | Retain shared Override records and the same CLI semantics. | -| `LoadLayered` | `func LoadLayered[T any](main Input, supplied ...LayeredOptions[T]) (*Result[T], error)` | Retain explicit tiers and independent main input; do not make composition strategy a switch. See consolidation assessment C1. | -| `Definition` | `type Definition[T any] struct` | Retain reusable application rules separate from per-load selections; zero disables hooks and uses Go-zero defaults. | -| `Definition.Defaults` | `func() T` | Retain a fresh-value factory; zero-value configuration and shared mutable defaults are different concerns. | -| `Definition.Resolve` | `func(*T, *Resolution) error` | Retain audited mutation callback and error return; do not move application normalization outside loading. | -| `Definition.Validate` | `func(*T, *Validation)` | Retain read-only audited validation despite pointer parameter; a value parameter would not make maps/pointers immutable. | -| `Definition.Version` | `*VersionRule` | Retain optional application version policy; nil disables the rule. Do not confuse it with the ConfigKit release version. | -| `Definition.Secret` | `func(Path) bool` | Discuss D2: rename the classification predicate to Sensitive, distinguishing it from environment-backed Secret references. | -| `Definition.SequenceIdentities` | `[]SequenceIdentity` | Retain authoritative identity registrations; no new per-consumer merge strategy is needed. | -| `PlacementMode` | `type PlacementMode uint8` | Retain small explicit enum; zero is mapping placement, not automatic inference. | -| `SourceVersionMode` | `type SourceVersionMode uint8` | Discuss D3: the zero behavior deserves a descriptive MainOnly name rather than a historical Default label. | -| `ImportDefaults` | `type ImportDefaults struct` | Retain independent tri-state defaults; zero leaves library defaults in effect. Do not replace pointers with false-as-unspecified. | -| `ImportDefaults.Required` | `*bool` | Retain *bool: nil is unspecified, false is an explicit optional selection. | -| `ImportDefaults.Follow` | `*bool` | Retain *bool: explicit false must override the true library default. | -| `ImportDefaults.Hidden` | `*bool` | Retain *bool: explicit false must override the true library default. | -| `ImportDefaults.Recursive` | `*bool` | Retain *bool: distinguishes declaration defaulting from an explicit choice. | -| `ImportDefaults.Pattern` | `[]string` | Retain nil-versus-empty selection meaning; a new pattern language is unwarranted. | -| `Options` | `type Options[T any] struct` | Retain ordinary-load options with input-specific settings; do not consolidate by anonymously embedding another public options struct (C1). | -| `Options.Definition` | `Definition[T]` | Retain the shared Definition type; avoids duplicating callback configuration. | -| `Options.Destination` | `Path` | Retain literal mapping placement independent of filesystem paths; root is the zero Path. | -| `Options.PlacementMode` | `PlacementMode` | Retain explicit mapping/item choice; avoid inferring it from destination spelling. | -| `Options.Imports` | `bool` | Retain explicit opt-in; import defaults alone must not enable traversal. | -| `Options.ImportDefaults` | `ImportDefaults` | Retain one shared load-wide default type, not duplicate boolean fields here. | -| `Options.Overlay` | `string` | Retain one selected overlay name; empty selects none. | -| `Options.Filesystem` | `*FilesystemContext` | Retain context for ordinary source/import/overlay handling; distinguish from the layered overlay-only context (C1/C2). | -| `Options.Overrides` | `[]Override` | Retain explicit CLI assignments; do not generalize these into another arbitrary source tier. | -| `Options.SourceVersionMode` | `SourceVersionMode` | Discuss D3's zero-mode name; retain load-wide version policy here. | -| `FilesystemContext` | `type FilesystemContext struct` | Retain input access scope, distinct from returned ResourceContext. Consider documentation terminology rather than merging lifetimes (C2). | -| `FilesystemContext.BaseDir` | `string` | Retain host-absolute versus namespace-relative coordinates; clarify with paired examples (C2). | -| `FilesystemContext.Root` | `string` | Retain explicit scope separate from the source-relative base; do not silently strengthen filesystem security policy. | -| `FilesystemContext.FS` | `*Filesystem` | Retain *Filesystem identity, not raw fs.FS: nil selects host context and namespace identity matters (C2). | -| `Override` | `type Override struct` | Retain parser-neutral explicit CLI input, distinct from defaults and ordinary sources. | -| `Override.Path` | `Path` | Retain application-root semantic coordinates. | -| `Override.Value` | `any` | Retain built-in neutral values; accepting arbitrary typed objects would add another conversion contract. | -| `Override.Flag` | `string` | Retain safe display attribution, not the raw CLI argument or value. | -| `Load` | `func Load[T any](source Source, supplied ...Options[T]) (*Result[T], error)` | Retain the source-only call and one typed options record; the optional form avoids a mandatory empty options value or request builder. | - -### Sources and filesystem namespaces - -Declarations: [resource_context.go](../../resource_context.go), [source.go](../../source.go). - -| Entry | Declaration/type | Assessment | -| --- | --- | --- | -| `ResourceContext` | `type ResourceContext struct` | Retain passive post-acquisition resource access distinct from input scope; zero has no filesystem (C2). | -| `ResourceContext.FS` | `fs.FS` | Retain borrowed fs.FS. Callers close files they open; a successful load itself needs no Close. | -| `ResourceContext.BaseDir` | `string` | Retain slash-relative source base; do not replace it with a host path that excludes embedded filesystems. | -| `Source` | `type Source struct` | Retain opaque acquisition descriptions and deferred validation; zero is invalid and NoSource is deliberate absence. | -| `Filesystem` | `type Filesystem struct` | Retain a reusable borrowed fs.FS namespace with identity; zero is not a valid namespace. | -| `NewFilesystem` | `func NewFilesystem(label string, filesystem fs.FS) Filesystem` | Retain label plus borrowed fs.FS, returning a namespace whose argument failures surface during load. | -| `Filesystem.File` | `func (f Filesystem) File(name string) Source` | Retain native namespace file selection as a method; replacing it with a raw fs.FS Source overload would lose identity/context. | -| `File` | `func File(path string) Source` | Retain the short reusable host-file constructor. | -| `Bytes` | `func Bytes(label string, data []byte) Source` | Retain immediate byte ownership snapshot and a safe label; delayed constructor diagnostics match other sources. | -| `Reader` | `func Reader(label string, input io.Reader) Source` | Retain explicit single-use borrowed-reader semantics; do not add automatic Close or hidden buffering for reuse. | -| `Mapping` | `func Mapping(label string, values map[string]any) Source` | Retain immediate neutral-map snapshot; nil root is invalid and empty mapping is meaningful. | -| `NoSource` | `func NoSource() Source` | Retain explicit absence separate from zero Source and an empty document. | - -### Explicit main-file discovery - -Declarations: [discovery.go](../../discovery.go). - -| Entry | Declaration/type | Assessment | -| --- | --- | --- | -| `Search` | `type Search struct` | Retain immutable explicit host-search description; zero contributes no candidates. No independent discovery/execution stage is needed. | -| `FilenameGroup` | `type FilenameGroup struct` | Retain a small opaque description; zero contributes no names. It is not a general glob matcher. | -| `SearchOptions` | `type SearchOptions struct` | Retain whole-search absence policy separate from per-directory filename choices. | -| `SearchOptions.Required` | `bool` | Retain optional-by-default search exhaustion; selected-file errors remain terminal. | -| `DirectorySearchOptions` | `type DirectorySearchOptions struct` | Retain the filename-group option record; preserve nil/default versus empty/no-candidates distinction. | -| `DirectorySearchOptions.Filenames` | `[]FilenameGroup` | Retain ordered groups; no candidate list or candidate-attempt history is necessary. | -| `NamedFilenames` | `func NamedFilenames() FilenameGroup` | Retain application-named YAML filename preset; it keeps common consumer setup short. | -| `GenericFilenames` | `func GenericFilenames() FilenameGroup` | Retain generic YAML filename preset; separate selection avoids implicit convention changes. | -| `CustomFilenames` | `func CustomFilenames(names ...string) FilenameGroup` | Retain explicit filename components and order, not a second glob/template mechanism. | -| `SearchDirectory` | `func SearchDirectory(directory string, supplied ...DirectorySearchOptions) Search` | Retain directory search plus optional filenames; argument problems remain load-time diagnostics. | -| `Paths` | `func Paths(paths ...string) Search` | Retain literal host-candidate description. Document that this is discovery, not semantic Path construction. | -| `Searches` | `func Searches(searches ...Search) Search` | Retain ordered concatenation; it expresses one search, not repeated loads or fallback after parse failure. | -| `UnixSystemDirectories` | `func UnixSystemDirectories() Search` | Retain explicit platform preset; do not turn its presence into implicit cross-platform guarantees. | -| `UserConfigDirectory` | `func UserConfigDirectory() Search` | Retain explicit OS user-config location preset. | -| `HomeDirectory` | `func HomeDirectory() Search` | Retain explicit home-root search preset, distinct from user-config location. | -| `WorkingDirectory` | `func WorkingDirectory() Search` | Retain explicit working-directory preset; do not activate it automatically. | -| `SearchConfig` | `func SearchConfig(application string, search Search, supplied ...SearchOptions) Source` | Retain deferred source selection inside loading. Optional exhaustion and selected-file failure are intentionally different. | - -### Paths and sequence identity registration - -Declarations: [path.go](../../path.go), [sequence_identity.go](../../sequence_identity.go). - -| Entry | Declaration/type | Assessment | -| --- | --- | --- | -| `Path` | `type Path struct` | Discuss A2: preserve opaque semantic paths; consider exact segment comparison only if consumers need it. Zero is root; current type is not comparable. | -| `Keys` | `func Keys(keys ...string) Path` | Retain concise literal-key construction and zero-argument root; do not interpret numeric keys as indexes. | -| `ParsePath` | `func ParsePath(pointer string) (Path, error)` | Retain contextual numeric tokens and an error return; pointer spelling alone is not full semantic identity (A2). | -| `Path.Key` | `func (p Path) Key(key string) Path` | Retain immutable literal-key append, including empty and numeric keys. | -| `Path.Index` | `func (p Path) Index(index int) Path` | Retain explicit sequence indexing; negative indexes produce an invalid path rejected by configuration operations. | -| `Path.String` | `func (p Path) String() string` | Retain canonical JSON Pointer formatting, but explicitly avoid calling it a complete semantic identity (A2). | -| `SequenceSelector` | `type SequenceSelector []SequenceSelectorSegment` | Retain the named slice of literal/wildcard mapping-key segments; zero selects root. Do not merge it with general Path syntax (C4). | -| `SequenceSelectorSegment` | `type SequenceSelectorSegment struct` | Retain the opaque segment; zero is the literal empty key, not a wildcard. | -| `SequenceLiteralKey` | `func SequenceLiteralKey(key string) SequenceSelectorSegment` | Retain explicit literal construction, including '*' as a real key. | -| `SequenceAnyMappingKey` | `func SequenceAnyMappingKey() SequenceSelectorSegment` | Retain an explicit wildcard constructor; textual '*' must not acquire a second meaning. | -| `SequenceIdentity` | `type SequenceIdentity struct` | Retain immutable registration value, not a pluggable merge strategy. Zero lacks required identity fields and is rejected during load. | -| `SequenceIdentityAt` | `func SequenceIdentityAt(path Path, fields ...string) SequenceIdentity` | Retain the exact-path convenience; it avoids building a selector for ordinary static destinations. | -| `SequenceIdentityMatching` | `func SequenceIdentityMatching(selector SequenceSelector, fields ...string) SequenceIdentity` | Retain wildcard mapping destinations for repeated namespaces; constructor copies inputs. | - -### Resolution, validation, and application schema versions - -Declarations: [lifecycle.go](../../lifecycle.go), [resource_context.go](../../resource_context.go). - -| Entry | Declaration/type | Assessment | -| --- | --- | --- | -| `Resolution` | `type Resolution struct` | Retain a library-issued callback capability; nil/zero/closed contexts are invalid and panic. Do not add a general resolver service interface. | -| `VersionRule` | `type VersionRule struct` | Retain one application-owned version path/checker. Zero Path means root; a non-nil rule needs a checker. | -| `VersionRule.Path` | `Path` | Retain mode-defined effective/source-local coordinates; clarify the distinction in examples, not separate version systems. | -| `VersionRule.Check` | `func(Version, *Validation) error` | Retain Version plus diagnostic context and error return; application acceptance remains application-owned. | -| `Version` | `type Version struct` | Retain the compact absent/null/text record rather than imposing a semver or numeric version type. | -| `Version.Present` | `bool` | Retain: omission cannot be inferred from Text being empty. | -| `Version.Null` | `bool` | Retain: an explicit null is different from absence and an empty string. | -| `Version.Text` | `string` | Retain exact accepted spelling; do not expose YAML scalar kinds when string/integer equivalence is intentional. | -| `Validation` | `type Validation struct` | Retain the callback-only diagnostic capability; no public constructor or reusable context is needed. | -| `Resolution.IsExplicit` | `func (r *Resolution) IsExplicit(path Path) bool` | Retain surviving-input presence semantics, including inherited input; document its schema-path diagnostics versus a lookup miss. | -| `Resolution.InputInfo` | `func (r *Resolution) InputInfo(path Path) (FieldInfo, bool)` | Discuss D1: EntryInfo more accurately names the fixed pre-resolution view, including defaults. | -| `Resolution.Source` | `func (r *Resolution) Source(id string) (SourceInfo, bool)` | Retain direct acquired-source lookup by ID; optional Result parity is A1. | -| `Resolution.Record` | `func (r *Resolution) Record(path Path, kind ChangeKind, reason string, dependencies ...Path)` | Discuss D6: its ChangeKind argument admits inspection-only constants that loading rejects; consider intent-specific record methods. | -| `Resolution.Error` | `func (r *Resolution) Error(path Path, message string, related ...Path)` | Retain safe diagnostic collection; do not expose raw application errors automatically. | -| `Resolution.Warning` | `func (r *Resolution) Warning(path Path, message string, related ...Path)` | Retain warning/error symmetry and related semantic paths. | -| `Default` | `func Default[V any](resolution *Resolution, path Path, target *V, value V, reason string, dependencies ...Path)` | Retain the generic typed assignment helper; explicit zero/false/null must suppress absence-based assignment. | -| `Normalize` | `func Normalize[V any](resolution *Resolution, path Path, target *V, value V, reason string, dependencies ...Path)` | Retain unconditional typed assignment plus audit intent; not redundant with absence-based Default. | -| `Validation.Error` | `func (v *Validation) Error(path Path, message string, related ...Path)` | Retain value-free errors collected at semantic paths, distinct from unwrapping arbitrary callback errors. | -| `Validation.Warning` | `func (v *Validation) Warning(path Path, message string, related ...Path)` | Retain matching warning API; no generic severity switch is necessary. | -| `Resolution.SourceContext` | `func (r *Resolution) SourceContext(id string) (ResourceContext, bool)` | Retain passive source-context lookup during the callback, with no I/O. | - -### Results, presence, and source/provenance evidence - -Declarations: [inspection.go](../../inspection.go), [lifecycle.go](../../lifecycle.go), [metadata.go](../../metadata.go), [records.go](../../records.go), [resource_context.go](../../resource_context.go). - -| Entry | Declaration/type | Assessment | -| --- | --- | --- | -| `Result.Explain` | `func (r *Result[T]) Explain(path Path) (Explanation, bool)` | Retain structured safe history plus found boolean; query semantics differ from Metadata.History (C3). | -| `Result.WriteEffective` | `func (r *Result[T]) WriteEffective(output io.Writer) error` | Retain frozen redacted YAML output and error-only writer convention; nil/zero snapshot or nil writer is an error. | -| `Result.WriteExplanation` | `func (r *Result[T]) WriteExplanation(output io.Writer, path Path) error` | Retain human-readable output. Clarify that unknown paths return an error without output; this is not a new behavior. | -| `Result` | `type Result[T any] struct` | Retain the successful configuration plus frozen inspection boundary; zero/nil has no usable loaded snapshot. | -| `Result.Config` | `T` | Retain caller-owned mutable T. Clarify that later mutation does not update inspection; replacing it with a cloning getter adds cost without immutability. | -| `Result.Metadata` | `func (r *Result[T]) Metadata() Metadata` | Retain detached effective/history query access; do not translate Explain output into metadata. | -| `Result.Sources` | `func (r *Result[T]) Sources() []SourceInfo` | Retain ordered detached acquisition records; optional direct Source lookup is A1. | -| `Result.Diagnostics` | `func (r *Result[T]) Diagnostics() []Diagnostic` | Retain successful-load nonfatal diagnostics, parallel to structured load failures. | -| `Metadata` | `type Metadata struct` | Retain opaque detached queries; zero contains no known paths. | -| `Metadata.Lookup` | `func (m Metadata) Lookup(path Path) (FieldInfo, bool)` | Retain effective FieldInfo plus found boolean; this handles absence without optional wrappers in application structs. | -| `Metadata.IsExplicit` | `func (m Metadata) IsExplicit(path Path) bool` | Retain ergonomic convenience over Lookup; inherited input is explicit and defaults/resolution do not invent presence. | -| `Metadata.History` | `func (m Metadata) History(path Path) []Change` | Retain attribution-only history, including removed paths; it intentionally differs from value replay (C3). | -| `Metadata.HistoricalPaths` | `func (m Metadata) HistoricalPaths() []Path` | Retain canonical-pointer enumeration, including removed/default/control paths; no extra per-shape enumeration API is justified. | -| `Layer` | `type Layer string` | Retain string-valued provenance categories; base/main terminology merits documentation clarity, not an automatic wire-value rename. | -| `SourceKind` | `type SourceKind string` | Retain acquisition kind independently of precedence layer. | -| `ChangeKind` | `type ChangeKind string` | Discuss D6's recording input; retain all inspection operations and the empty/default definition state. | -| `Location` | `type Location struct` | Retain source ID plus optional line/column coordinates; zero coordinates mean unavailable/synthetic. | -| `Location.SourceID` | `string` | Retain a load-local acquired-source key, not a host filename. | -| `Location.Line` | `int` | Retain line coordinate; zero means unavailable. | -| `Location.Column` | `int` | Retain column coordinate; zero means unavailable. | -| `SourceInfo` | `type SourceInfo struct` | Retain structured acquisition and placement evidence; simplify misleading evidence names in D3–D5, not source identity. | -| `SourceInfo.ID` | `string` | Retain load-local ID independent of physical path or label. | -| `SourceInfo.Label` | `string` | Retain safe display label separate from path and stable identity. | -| `SourceInfo.Path` | `string` | Retain host-canonical/native-relative location where present; do not imply every source has a path. | -| `SourceInfo.Destination` | `Path` | Retain effective mapping/sequence-container placement; it is not always an item's exact value path. | -| `SourceInfo.PlacementMode` | `PlacementMode` | Retain explicit placement evidence, independent of acquisition kind. | -| `SourceInfo.DestinationUnresolved` | `bool` | Discuss D5: DestinationIsFallback would describe the enclosing-container evidence more precisely. | -| `SourceInfo.Layer` | `Layer` | Retain provenance tier independent of acquisition order and source kind. | -| `SourceInfo.Kind` | `SourceKind` | Retain acquisition category rather than inferring it from path syntax. | -| `SourceInfo.UnderlayIndex` | `int` | Retain one-based original tier position, zero otherwise; do not renumber around empty tiers. | -| `SourceInfo.ImportedFrom` | `*Location` | Retain optional declaring location rather than a second parent-source object graph. | -| `SourceInfo.VersionCheck` | `*SourceVersionCheck` | Discuss D3: VersionEvidence avoids implying that the checker ran or accepted anything. | -| `SourceInfo.ImportSelections` | `[]ImportSelection` | Discuss D4: name the empty-selection-only scope explicitly. | -| `ImportSelector` | `type ImportSelector string` | Retain path/directory/glob selector classification; do not combine it with source acquisition kind. | -| `ImportSelectionOutcome` | `type ImportSelectionOutcome string` | Discuss D4: this public enum has only one supported outcome and may be redundant in an explicitly empty-selection record. | -| `ImportSelection` | `type ImportSelection struct` | Discuss D4: EmptyImportSelection would accurately name the currently reported subset. | -| `ImportSelection.Path` | `Path` | Retain declaration-local path if the record is renamed; not the selected source's destination. | -| `ImportSelection.Location` | `Location` | Retain declaration mapping/source coordinates. | -| `ImportSelection.Selector` | `ImportSelector` | Retain the authored selector kind; empty path, directory, and glob choices differ. | -| `ImportSelection.Outcome` | `ImportSelectionOutcome` | Discuss D4: remove the constant-only none field if empty-only scope is made explicit. | -| `SourceVersionCheck` | `type SourceVersionCheck struct` | Discuss D3: SourceVersionEvidence better describes observed declaration state without implying validation success. | -| `SourceVersionCheck.Path` | `Path` | Retain requested source-local version path, including absent evidence. | -| `SourceVersionCheck.Version` | `Version` | Retain absent/null/text observation, not an acceptance result. | -| `SourceVersionCheck.Location` | `*Location` | Retain optional actual/nearest authored location. | -| `SourceVersionCheck.Redacted` | `bool` | Retain explicit withheld-text indication; absence cannot be inferred from empty Text. | -| `InheritanceStep` | `type InheritanceStep struct` | Retain explicit transfer/control provenance; it adds no acquired source or precedence layer. | -| `InheritanceStep.From` | `Path` | Retain contributor semantic path. | -| `InheritanceStep.To` | `Path` | Retain destination semantic path. | -| `InheritanceStep.Reference` | `Path` | Retain controlling extends path, distinct from the inherited value path. | -| `InheritanceStep.Locations` | `[]Location` | Retain directive coordinates rather than mislabeling them as value coordinates. | -| `Origin` | `type Origin struct` | Retain composite locations, precedence, operation and transfer trace; no generic untyped metadata bag. | -| `Origin.Layer` | `Layer` | Retain provenance classification; inheritance does not invent another layer. | -| `Origin.Locations` | `[]Location` | Retain ordered composite origins; one SourceID field would lose strict composition evidence. | -| `Origin.Operation` | `ChangeKind` | Retain inspection ChangeKind, including decoding/inheritance (D6 concerns recording input only). | -| `Origin.Inheritance` | `[]InheritanceStep` | Retain concrete derivation steps separately from acquired source locations. | -| `FieldInfo` | `type FieldInfo struct` | Retain compact effective presence/null/origin record. | -| `FieldInfo.Explicit` | `bool` | Retain input presence independent of the final Go zero value. | -| `FieldInfo.Null` | `bool` | Retain explicit null independently of missing lookup and scalar zero. | -| `FieldInfo.Origin` | `Origin` | Retain final effective attribution. | -| `Change` | `type Change struct` | Retain attribution-only history and Previous origins; not redundant with ValueChange (C3). | -| `Change.Path` | `Path` | Retain reported operation path, which may enclose the queried affected path. | -| `Change.Origin` | `Origin` | Retain this definition/operation's attribution. | -| `Change.Previous` | `[]Origin` | Retain preceding selected origins after shape filtering. | -| `Change.Reasons` | `[]string` | Retain ordered recorded reasons without embedding configuration values. | -| `Change.Dependencies` | `[]Path` | Retain original recorded dependency paths independently of replay grouping. | -| `Result.SourceContext` | `func (r *Result[T]) SourceContext(id string) (ResourceContext, bool)` | Retain passive lookup by origin source ID; do not open or cache resource files implicitly. | - -### Safe value inspection and comparison - -Declarations: [diff.go](../../diff.go), [inspection.go](../../inspection.go). - -| Entry | Declaration/type | Assessment | -| --- | --- | --- | -| `Difference` | `type Difference struct` | Retain a separate changed-region record; effective comparison is not a provenance/history diff. | -| `Difference.Path` | `Path` | Retain canonical changed-region coordinates. | -| `Difference.Left` | `ValueSnapshot` | Retain the safe left snapshot under combined classification, not independently rendered context. | -| `Difference.Right` | `ValueSnapshot` | Retain left/right symmetry; before/after belongs to chronological history. | -| `Difference.HiddenChanged` | `bool` | Retain actual concealed-change indication distinct from Redacted. | -| `Diff` | `func Diff[T any](left, right *Result[T]) ([]Difference, error)` | Retain same-type frozen-result comparison; nil/zero results are errors and credential contents are not compared. | -| `ValueSnapshot` | `type ValueSnapshot struct` | Retain detached Present/Redacted/YAML state, shared by explanation and diff without exposing parser nodes. | -| `ValueSnapshot.Present` | `bool` | Retain absence/deletion versus explicit null; YAML text alone is insufficient. | -| `ValueSnapshot.Redacted` | `bool` | Retain a structured mask flag; consumers must not interpret marker text. | -| `ValueSnapshot.YAML` | `string` | Retain deterministic safe text, not a reloadable or mutable node promise. | -| `ValueChange` | `type ValueChange struct` | Retain value-bearing replay record distinct from metadata Change; do not force both projections into one record (C3). | -| `ValueChange.Before` | `ValueSnapshot` | Retain the earlier safe value with explicit presence. | -| `ValueChange.After` | `ValueSnapshot` | Retain chronological before/after symmetry. | -| `ValueChange.Origin` | `Origin` | Retain attribution from shared evidence, without requiring identical metadata/explanation event lists. | -| `ValueChange.Reasons` | `[]string` | Retain ordered application intent; consumers remain responsible for safe prose. | -| `ValueChange.Dependencies` | `[]Path` | Retain recorded dependencies and their semantic paths. | -| `SecretReferenceInfo` | `type SecretReferenceInfo struct` | Retain explicit resolved-reference annotation; it is different from redaction classification. | -| `SecretReferenceInfo.Name` | `string` | Retain public environment name only; do not add credential content. | -| `Explanation` | `type Explanation struct` | Retain effective state plus safe chronological values; not a second mutable configuration object. | -| `Explanation.Path` | `Path` | Retain the queried semantic path. | -| `Explanation.Effective` | `ValueSnapshot` | Retain final safe value even for a path now absent. | -| `Explanation.Origin` | `Origin` | Retain effective provenance separate from each historical event's origin. | -| `Explanation.History` | `[]ValueChange` | Retain ValueChange replay, not a converted Metadata.History list (C3). | -| `Explanation.SecretReference` | `*SecretReferenceInfo` | Retain optional resolved-reference name; a classified path can withhold this annotation. | - -### Typed credentials and explicit bootstrap - -Declarations: [secret.go](../../secret.go), [secrets.go](../../secrets.go). - -| Entry | Declaration/type | Assessment | -| --- | --- | --- | -| `Secret` | `type Secret struct` | Retain opaque environment reference and explicit credential access. Zero is unresolved/unspecified; a surviving non-pointer Secret is required. | -| `SecretEnv` | `func SecretEnv(name string) Secret` | Retain an explicit reference constructor; no plain-string credential constructor. | -| `Secret.EnvName` | `func (s Secret) EnvName() string` | Retain public reference access separate from credential access. | -| `Secret.Value` | `func (s Secret) Value() string` | Retain deliberate credential accessor; empty means unresolved. Do not combine it with formatting. | -| `Secret.IsResolved` | `func (s Secret) IsResolved() bool` | Retain cheap explicit resolution-state query despite nonempty-value validation. | -| `Secret.Format` | `func (s Secret) Format(state fmt.State, _ rune)` | Retain fmt.Formatter safety for all formatting verbs; do not replace it with only String. | -| `Secret.MarshalJSON` | `func (s Secret) MarshalJSON() ([]byte, error)` | Retain name-only json.Marshaler behavior, not a credential serialization API. | -| `SecretFileStatus` | `type SecretFileStatus string` | Retain distinct loaded/skipped/not-applied/failed states; partial bootstrap needs more than success/failure. | -| `SecretFileResult` | `type SecretFileResult struct` | Retain value-free per-entry bootstrap outcome, distinct from load sources. | -| `SecretFileResult.Name` | `string` | Retain entry/environment name, never content. | -| `SecretFileResult.Path` | `string` | Retain safe source-file attribution. | -| `SecretFileResult.Status` | `SecretFileStatus` | Retain structured status, not interpretation of report text. | -| `SecretFileResult.ExistingEmpty` | `bool` | Retain empty-existing winner evidence for skipped entries; zero does not mean that an entry was loaded. | -| `SecretsReport` | `type SecretsReport struct` | Retain detached bootstrap outcomes; zero/nil has no entries. A report may accompany an error. | -| `SecretsReport.Entries` | `func (r *SecretsReport) Entries() []SecretFileResult` | Retain copied filename-ordered outcomes. | -| `SecretsReport.WriteTo` | `func (r *SecretsReport) WriteTo(output io.Writer) (int64, error)` | Retain io.WriterTo-compatible count/error convention; do not force other renderers to return counts just for symmetry. | -| `SecretsError` | `type SecretsError struct` | Retain separate bootstrap failure type: reports partial external effects, unlike a failed configuration load. | -| `SecretsError.Error` | `func (e *SecretsError) Error() string` | Retain safe value-free summary and errors.As support through its type. | -| `SecretsError.Diagnostics` | `func (e *SecretsError) Diagnostics() []Diagnostic` | Retain detached bootstrap diagnostics; no automatic raw filesystem/environment error unwrapping. | -| `LoadSecretsDir` | `func LoadSecretsDir(directory string) (*SecretsReport, error)` | Discuss D7: BootstrapSecretsDir makes process-environment mutation clearer than another Load name. | - -### Diagnostics and failed loads - -Declarations: [diagnostic.go](../../diagnostic.go), [records.go](../../records.go). - -| Entry | Declaration/type | Assessment | -| --- | --- | --- | -| `LoadError` | `type LoadError struct` | Retain a concrete inspectable load error and errors.As workflow; zero/nil renders a safe generic failure. | -| `LoadError.Error` | `func (e *LoadError) Error() string` | Retain value-free summary, not detailed raw parser/application errors. | -| `LoadError.Diagnostics` | `func (e *LoadError) Diagnostics() []Diagnostic` | Retain detached structured diagnostics; do not add Unwrap that defeats safe error boundaries. | -| `LoadError.Sources` | `func (e *LoadError) Sources() []SourceInfo` | Retain acquired-source evidence available before failure. | -| `WriteDiagnostics` | `func WriteDiagnostics(output io.Writer, diagnostics []Diagnostic) error` | Retain writer-first safe deterministic rendering; do not promise stable prose beyond established contracts. | -| `Severity` | `type Severity string` | Retain the two diagnostic severities; no arbitrary numeric priority surface. | -| `Code` | `type Code string` | Retain detailed stable diagnostic codes; collapsing categories would lose useful consumer handling. | -| `Diagnostic` | `type Diagnostic struct` | Retain a common safe problem record across loading, hooks, and bootstrap. | -| `Diagnostic.Code` | `Code` | Retain stable machine classification rather than matching prose. | -| `Diagnostic.Severity` | `Severity` | Retain explicit severity, independent of diagnostic code spelling. | -| `Diagnostic.Path` | `Path` | Retain semantic configuration path, not a filesystem location. | -| `Diagnostic.Message` | `string` | Retain human-readable safe context; no automatic secret inference or blanket prose-stability guarantee. | -| `Diagnostic.Primary` | `*Location` | Retain optional primary source position; synthetic reports may lack one. | -| `Diagnostic.Related` | `[]Location` | Retain ordered related locations; do not homogenize semantically meaningful composition claimant order. | - -### Constants and their values - -Each constant is separately listed. Retaining a diagnostic code preserves a -machine-readable failure distinction; consumers should not infer new guarantees -from a name or assume that the current set can never grow. D8 notes the one -unproduced named code identified for discussion. This catalog does not prescribe -new diagnostics or merge existing failure categories. - -#### PlacementMode constants - -| Entry | Type and value | Assessment | -| --- | --- | --- | -| `PlacementModeMapping` | `PlacementMode = 0` | Retain explicit mapping default. | -| `PlacementModeSequenceItem` | `PlacementMode = 1` | Retain explicit record placement. | - -#### SourceVersionMode constants - -| Entry | Type and value | Assessment | -| --- | --- | --- | -| `SourceVersionModeDefault` | `SourceVersionMode = 0` | Discuss D3: MainOnly names the existing zero behavior more precisely. | -| `SourceVersionModeIfPresent` | `SourceVersionMode = 1` | Retain; keep the documented main-absence checker behavior. | -| `SourceVersionModeRequired` | `SourceVersionMode = 2` | Retain participating-document requirement and existing NoSource behavior. | - -#### Layer constants - -| Entry | Type and value | Assessment | -| --- | --- | --- | -| `LayerDefault` | `Layer = "default"` | Retain distinct provenance layer. | -| `LayerBase` | `Layer = "base"` | Retain established base/main attribution; no automatic wire-value rename. | -| `LayerUnderlay` | `Layer = "underlay"` | Retain distinct provenance layer. | -| `LayerOverlay` | `Layer = "overlay"` | Retain distinct provenance layer. | -| `LayerCLI` | `Layer = "cli"` | Retain distinct provenance layer. | -| `LayerResolution` | `Layer = "resolution"` | Retain distinct provenance layer. | - -#### SourceKind constants - -| Entry | Type and value | Assessment | -| --- | --- | --- | -| `SourceKindFile` | `SourceKind = "file"` | Retain distinct acquisition kind, independent of layer. | -| `SourceKindFilesystem` | `SourceKind = "filesystem"` | Retain distinct acquisition kind, independent of layer. | -| `SourceKindBytes` | `SourceKind = "bytes"` | Retain distinct acquisition kind, independent of layer. | -| `SourceKindReader` | `SourceKind = "reader"` | Retain distinct acquisition kind, independent of layer. | -| `SourceKindMapping` | `SourceKind = "mapping"` | Retain distinct acquisition kind, independent of layer. | -| `SourceKindCLI` | `SourceKind = "cli"` | Retain distinct acquisition kind, independent of layer. | - -#### ChangeKind constants - -| Entry | Type and value | Assessment | -| --- | --- | --- | -| `ChangeDecoding` | `ChangeKind = "decoding"` | Retain inspection value; D6 addresses its misleading availability as a Record argument. | -| `ChangeInheritance` | `ChangeKind = "inheritance"` | Retain inspection value; D6 addresses its misleading availability as a Record argument. | -| `ChangeDerivedDefault` | `ChangeKind = "derived_default"` | Retain recorded/inspected intent; discuss the Record input shape in D6. | -| `ChangeNormalization` | `ChangeKind = "normalization"` | Retain recorded/inspected intent; discuss the Record input shape in D6. | - -#### Severity constants - -| Entry | Type and value | Assessment | -| --- | --- | --- | -| `SeverityError` | `Severity = "error"` | Retain explicit diagnostic severity. | -| `SeverityWarning` | `Severity = "warning"` | Retain explicit diagnostic severity. | - -#### Code constants - -| Entry | Type and value | Assessment | -| --- | --- | --- | -| `CodeSourceInvalid` | `Code = "source.invalid"` | Retain distinct source / invalid classification. | -| `CodeSourceMissing` | `Code = "source.missing"` | Retain distinct source / missing classification. | -| `CodeSourcePermission` | `Code = "source.permission"` | Retain distinct source / permission classification. | -| `CodeSourceRead` | `Code = "source.read"` | Retain distinct source / read classification. | -| `CodeSourceNotRegular` | `Code = "source.not_regular"` | Retain distinct source / not regular classification. | -| `CodeSourceLimitExceeded` | `Code = "source.limit_exceeded"` | Retain distinct source / limit exceeded classification. | -| `CodeSourceReaderUsed` | `Code = "source.reader_used"` | Retain distinct source / reader used classification. | -| `CodeYAMLInvalid` | `Code = "yaml.invalid"` | Retain distinct yaml / invalid classification. | -| `CodeYAMLEmpty` | `Code = "yaml.empty"` | Retain distinct yaml / empty classification. | -| `CodeYAMLMultipleDocuments` | `Code = "yaml.multiple_documents"` | Retain distinct yaml / multiple documents classification. | -| `CodeYAMLRootType` | `Code = "yaml.root_type"` | Retain distinct yaml / root type classification. | -| `CodeYAMLDuplicateKey` | `Code = "yaml.duplicate_key"` | Retain distinct yaml / duplicate key classification. | -| `CodeYAMLTag` | `Code = "yaml.tag"` | Retain distinct yaml / tag classification. | -| `CodeYAMLAnchor` | `Code = "yaml.anchor"` | Retain distinct yaml / anchor classification. | -| `CodeYAMLAlias` | `Code = "yaml.alias"` | Retain distinct yaml / alias classification. | -| `CodeYAMLMergeKey` | `Code = "yaml.merge_key"` | Retain distinct yaml / merge key classification. | -| `CodeYAMLScalar` | `Code = "yaml.scalar"` | Retain distinct yaml / scalar classification. | -| `CodeYAMLDepthLimit` | `Code = "yaml.depth_limit"` | Retain distinct yaml / depth limit classification. | -| `CodeYAMLNodeLimit` | `Code = "yaml.node_limit"` | Retain distinct yaml / node limit classification. | -| `CodeSchemaRoot` | `Code = "schema.root"` | Retain distinct schema / root classification. | -| `CodeSchemaUnsupportedType` | `Code = "schema.unsupported_type"` | Retain distinct schema / unsupported type classification. | -| `CodeSchemaAmbiguousField` | `Code = "schema.ambiguous_field"` | Retain distinct schema / ambiguous field classification. | -| `CodeSchemaTag` | `Code = "schema.tag"` | Retain distinct schema / tag classification. | -| `CodeSchemaTextCodec` | `Code = "schema.text_codec"` | Retain distinct schema / text codec classification. | -| `CodeSchemaInline` | `Code = "schema.inline"` | Retain distinct schema / inline classification. | -| `CodeSchemaCycle` | `Code = "schema.cycle"` | Retain distinct schema / cycle classification. | -| `CodeSchemaProjectionLimit` | `Code = "schema.projection_limit"` | Retain distinct schema / projection limit classification. | -| `CodeSchemaCodec` | `Code = "schema.codec"` | Retain distinct schema / codec classification. | -| `CodeSchemaNonFinite` | `Code = "schema.non_finite"` | Retain distinct schema / non finite classification. | -| `CodeSchemaDynamicType` | `Code = "schema.dynamic_type"` | Retain distinct schema / dynamic type classification. | -| `CodeDecodeType` | `Code = "decode.type"` | Retain distinct decode / type classification. | -| `CodeDecodeNull` | `Code = "decode.null"` | Retain distinct decode / null classification. | -| `CodeDecodeRange` | `Code = "decode.range"` | Retain distinct decode / range classification. | -| `CodeDecodeDuration` | `Code = "decode.duration"` | Retain distinct decode / duration classification. | -| `CodeDecodeText` | `Code = "decode.text"` | Retain distinct decode / text classification. | -| `CodeDecodeUnknownField` | `Code = "decode.unknown_field"` | Retain distinct decode / unknown field classification. | -| `CodeDecodeArrayLength` | `Code = "decode.array_length"` | Retain distinct decode / array length classification. | -| `CodeOptionsInvalid` | `Code = "options.invalid"` | Retain distinct options / invalid classification. | -| `CodeOptionsUnavailable` | `Code = "options.unavailable"` | Discuss D8: confirm intentional reservation or remove the unproduced classification. | -| `CodeImportDisabled` | `Code = "import.disabled"` | Retain distinct import / disabled classification. | -| `CodeImportInvalid` | `Code = "import.invalid"` | Retain distinct import / invalid classification. | -| `CodeImportMissing` | `Code = "import.missing"` | Retain distinct import / missing classification. | -| `CodeImportPermission` | `Code = "import.permission"` | Retain distinct import / permission classification. | -| `CodeImportRead` | `Code = "import.read"` | Retain distinct import / read classification. | -| `CodeImportOutsideRoot` | `Code = "import.outside_root"` | Retain distinct import / outside root classification. | -| `CodeImportCycle` | `Code = "import.cycle"` | Retain distinct import / cycle classification. | -| `CodeImportRepeated` | `Code = "import.repeated"` | Retain distinct import / repeated classification. | -| `CodeImportLimitExceeded` | `Code = "import.limit_exceeded"` | Retain distinct import / limit exceeded classification. | -| `CodeImportVersion` | `Code = "import.version"` | Retain distinct import / version classification. | -| `CodeSequenceInvalid` | `Code = "sequence.invalid"` | Retain distinct sequence / invalid classification. | -| `CodeSequenceConflict` | `Code = "sequence.conflict"` | Retain distinct sequence / conflict classification. | -| `CodeUnderlayVersion` | `Code = "underlay.version"` | Retain distinct underlay / version classification. | -| `CodeOverlayInvalid` | `Code = "overlay.invalid"` | Retain distinct overlay / invalid classification. | -| `CodeOverlayMissing` | `Code = "overlay.missing"` | Retain distinct overlay / missing classification. | -| `CodeOverlayOutsideRoot` | `Code = "overlay.outside_root"` | Retain distinct overlay / outside root classification. | -| `CodeOverlayRepeated` | `Code = "overlay.repeated"` | Retain distinct overlay / repeated classification. | -| `CodeOverlayDirective` | `Code = "overlay.directive"` | Retain distinct overlay / directive classification. | -| `CodeOverlayVersion` | `Code = "overlay.version"` | Retain distinct overlay / version classification. | -| `CodeOverlayLimitExceeded` | `Code = "overlay.limit_exceeded"` | Retain distinct overlay / limit exceeded classification. | -| `CodeComposeInvalid` | `Code = "compose.invalid"` | Retain distinct compose / invalid classification. | -| `CodeComposeConflict` | `Code = "compose.conflict"` | Retain distinct compose / conflict classification. | -| `CodeVersionType` | `Code = "version.type"` | Retain distinct version / type classification. | -| `CodeVersionError` | `Code = "version.error"` | Retain distinct version / error classification. | -| `CodeVersionWarning` | `Code = "version.warning"` | Retain distinct version / warning classification. | -| `CodeVersionHookFailed` | `Code = "version.hook_failed"` | Retain distinct version / hook failed classification. | -| `CodeResolveError` | `Code = "resolve.error"` | Retain distinct resolve / error classification. | -| `CodeResolveWarning` | `Code = "resolve.warning"` | Retain distinct resolve / warning classification. | -| `CodeResolveHookFailed` | `Code = "resolve.hook_failed"` | Retain distinct resolve / hook failed classification. | -| `CodeResolveInvalidPath` | `Code = "resolve.invalid_path"` | Retain distinct resolve / invalid path classification. | -| `CodeResolveInvalidKind` | `Code = "resolve.invalid_kind"` | Retain distinct resolve / invalid kind classification. | -| `CodeResolveInvalidRecord` | `Code = "resolve.invalid_record"` | Retain distinct resolve / invalid record classification. | -| `CodeResolveInvalidTarget` | `Code = "resolve.invalid_target"` | Retain distinct resolve / invalid target classification. | -| `CodeResolveInvalidValue` | `Code = "resolve.invalid_value"` | Retain distinct resolve / invalid value classification. | -| `CodeResolveUnrecordedChange` | `Code = "resolve.unrecorded_change"` | Retain distinct resolve / unrecorded change classification. | -| `CodeValidateError` | `Code = "validate.error"` | Retain distinct validate / error classification. | -| `CodeValidateWarning` | `Code = "validate.warning"` | Retain distinct validate / warning classification. | -| `CodeValidateInvalidPath` | `Code = "validate.invalid_path"` | Retain distinct validate / invalid path classification. | -| `CodeValidateMutation` | `Code = "validate.mutation"` | Retain distinct validate / mutation classification. | -| `CodeSecretsInvalidPath` | `Code = "secrets.invalid_path"` | Retain distinct secrets / invalid path classification. | -| `CodeSecretsMissing` | `Code = "secrets.missing"` | Retain distinct secrets / missing classification. | -| `CodeSecretsPermission` | `Code = "secrets.permission"` | Retain distinct secrets / permission classification. | -| `CodeSecretsRead` | `Code = "secrets.read"` | Retain distinct secrets / read classification. | -| `CodeSecretsNotRegular` | `Code = "secrets.not_regular"` | Retain distinct secrets / not regular classification. | -| `CodeSecretsInvalidName` | `Code = "secrets.invalid_name"` | Retain distinct secrets / invalid name classification. | -| `CodeSecretsLimitExceeded` | `Code = "secrets.limit_exceeded"` | Retain distinct secrets / limit exceeded classification. | -| `CodeSecretsInvalidValue` | `Code = "secrets.invalid_value"` | Retain distinct secrets / invalid value classification. | -| `CodeSecretsApplyFailed` | `Code = "secrets.apply_failed"` | Retain distinct secrets / apply failed classification. | -| `CodeSecretRequired` | `Code = "secret.required"` | Retain distinct secret / required classification. | -| `CodeSecretInvalidName` | `Code = "secret.invalid_name"` | Retain distinct secret / invalid name classification. | -| `CodeSecretUnset` | `Code = "secret.unset"` | Retain distinct secret / unset classification. | -| `CodeSecretEmpty` | `Code = "secret.empty"` | Retain distinct secret / empty classification. | -| `CodeOverrideInvalidPath` | `Code = "override.invalid_path"` | Retain distinct override / invalid path classification. | -| `CodeOverrideInvalidLabel` | `Code = "override.invalid_label"` | Retain distinct override / invalid label classification. | -| `CodeOverrideInvalidValue` | `Code = "override.invalid_value"` | Retain distinct override / invalid value classification. | -| `CodeOverrideDuplicate` | `Code = "override.duplicate"` | Retain distinct override / duplicate classification. | -| `CodeOverrideOverlap` | `Code = "override.overlap"` | Retain distinct override / overlap classification. | -| `CodeOverrideDirective` | `Code = "override.directive"` | Retain distinct override / directive classification. | -| `CodeOverrideVersion` | `Code = "override.version"` | Retain distinct override / version classification. | -| `CodeOverrideLimitExceeded` | `Code = "override.limit_exceeded"` | Retain distinct override / limit exceeded classification. | -| `CodeExplainLimitExceeded` | `Code = "explain.limit_exceeded"` | Retain distinct explain / limit exceeded classification. | -| `CodeInheritanceInvalid` | `Code = "inheritance.invalid"` | Retain distinct inheritance / invalid classification. | -| `CodeInheritanceMissing` | `Code = "inheritance.missing"` | Retain distinct inheritance / missing classification. | -| `CodeInheritanceCycle` | `Code = "inheritance.cycle"` | Retain distinct inheritance / cycle classification. | -| `CodeInheritanceVersion` | `Code = "inheritance.version"` | Retain distinct inheritance / version classification. | - -#### ImportSelector constants - -| Entry | Type and value | Assessment | -| --- | --- | --- | -| `ImportSelectorPath` | `ImportSelector = "path"` | Retain distinct authored selector kind. | -| `ImportSelectorDirectory` | `ImportSelector = "dir"` | Retain distinct authored selector kind. | -| `ImportSelectorGlob` | `ImportSelector = "glob"` | Retain distinct authored selector kind. | - -#### ImportSelectionOutcome constants - -| Entry | Type and value | Assessment | -| --- | --- | --- | -| `ImportSelectionNone` | `ImportSelectionOutcome = "none"` | Discuss D4: redundant if the record explicitly describes empty selections only. | - -#### SecretFileStatus constants - -| Entry | Type and value | Assessment | -| --- | --- | --- | -| `SecretFileLoaded` | `SecretFileStatus = "loaded"` | Retain successful application outcome. | -| `SecretFileSkippedExisting` | `SecretFileStatus = "skipped_existing"` | Retain existing-environment winner, including empty winners. | -| `SecretFileNotApplied` | `SecretFileStatus = "not_applied"` | Retain planned but unapplied outcome, distinct from a failed entry. | -| `SecretFileFailed` | `SecretFileStatus = "failed"` | Retain failed-entry outcome, distinct from unapplied siblings. | - -## Validation And Next Discussion - -The catalog has one row for each of the 372 exported declarations/members -found by the syntax inventory. The callable/type/field signatures were read from -the declarations rather than reconstructed from the graph. The graph supported -navigation and reference checks; its inventory alone was not treated as proof of -completeness. Implicit interfaces include error for `*LoadError` and -`*SecretsError`, fmt.Stringer for Path, fmt.Formatter and json.Marshaler for Secret, -and io.WriterTo for `*SecretsReport`; there are no separate exported interface types. - -Useful existing behavioral owners include the public facade boundary and -compatibility tests, callback-context guards, consumer examples, and metadata, -secret, explanation, source-version, discovery, and sequence tests linked by the -[development guide](../development.md#task-to-test-map). This documentation-only -review adds no tests or production changes. Validation completed: the syntax -comparison found exactly one catalog row for all 372 entries, with no missing -entries. The local documentation-link and public-facade boundary tests passed, -as did the whitespace check. - -Next, decide which D1–D8 candidates merit changes. Prepare a scoped roadmap/plan -only for accepted decisions and update the declaration comments, contract owners, -examples and consumers together during that implementation. Preserve the current -loading, presence, composition, callback-lifetime, redaction and ownership contracts -unless a separately explicit decision changes one. No release or tagging action -is authorized by this review. diff --git a/docs/roadmap/roadmap.md b/docs/roadmap/roadmap.md deleted file mode 100644 index 7c5d975..0000000 --- a/docs/roadmap/roadmap.md +++ /dev/null @@ -1,276 +0,0 @@ -# Audit Remediation Roadmap - -Status: target accepted; completed execution is recorded in the historical plan -at `bb5540b:docs/roadmap/implementation.md`. -Prepared: 2026-10-25. Planning baseline: `e1e94ee`. - -This roadmap describes the target state and related work for the 29 entries -selected in the [consolidated audit review](https://gitea.maximumdirect.net/eric/ConfigKit/src/commit/453da684d7c7d3843a5f505f5c468b4c889dc6e2/docs/roadmap/audit.md#settled-scope-decisions). -It is intended for maintainers reviewing the remediation's scope and architecture. - -## Document Ownership - -- [Archived audit](https://gitea.maximumdirect.net/eric/ConfigKit/src/commit/453da684d7c7d3843a5f505f5c468b4c889dc6e2/docs/roadmap/audit.md) records finding IDs, original evidence, detailed remedies and - acceptance cases, and the settled scope decisions. Its evidence has not been - independently reproduced by writing this roadmap. -- This document owns the combined target state, work areas, and design intent. - Finding IDs below provide traceability without repeating the full audit inventory. -- The historical implementation plan at - `bb5540b:docs/roadmap/implementation.md` records concrete stages, private - implementation choices, edits, validation commands, and completion evidence. - -The [configuration contract](../config.md), public Go declarations, and -[architecture policy](../policy/architecture.md) remain the owners of behavior -and guarantees. Update their applicable implementation documentation when work -lands; this planning document does not redefine their contracts. - -## Current Baseline And Targeted End State - -The existing loading model remains the foundation. Public loading APIs delegate -to the shared engine lifecycle; acquisition retains source scopes for composition, -then precedence, inheritance, decoding, CLI overrides, application resolution, -secret lookup, and validation produce a detached result with inspection metadata. -The [component overview](../internal/overview.md) describes the current owners. - -The audits identify failures at particular traversal, lifetime, error, and -accounting boundaries, alongside redundant representations and repeated work. -The target is to repair those boundaries and simplify their implementation while -preserving ConfigKit's public API and configuration model. - -| Current gap | Targeted outcome | -| --- | --- | -| Recursive descriptors can fail to terminate, failed dynamic builds can leave partial state, and slice aliases can be mistaken for cycles. | Traversal terminates for the reported cases, failed construction leaves reusable state valid, and supported acyclic values remain accepted. | -| Callback and owned-root cleanup can be skipped during panic unwinding; directory errors lose their failure phase. | Cleanup follows the lifetime of each owned resource, and selected-directory failures retain the information needed for correct error handling. | -| Finite symlink dependencies can be expanded repeatedly. | Completed dependencies are reused within a resolution while active cycles remain detectable. | -| Projection reservations and published history can escape existing accounting. | Existing budgets govern the relevant allocations and complete retained document graph. | -| Collection-policy work repeatedly scans or copies unrelated state. | Policy preparation, application, and retained evidence have clear ownership, with work localized to affected policy. | -| Acquisition has redundant paths and difficult handoffs; several private representations have no production consumer. | Production paths and their tests share the active protocol, with cohesive handoffs and unused representations removed. | -| Some lookups repeat full scans, and some tests assert incidental representation or miss meaningful mutations. | Local indexing avoids the identified repeated work, and tests protect observable contracts at useful boundaries. | - -This is a bounded remediation effort, not a redesign of configuration semantics. -Correcting erroneous acceptance, rejection, cleanup, or diagnostics in the -reported cases restores existing contracts. It does not select new loading APIs, -configuration options, limits, background work, or persistent caches. - -## Selected Work - -The following areas account for all 29 selected entries. Detailed examples and -acceptance cases remain with their linked audit group. - -### 1. Safe Typed-Value Traversal And Descriptor Reuse - -Selected entries: **A01, A02, A03, A04, A05** in -[schema and typed values](https://gitea.maximumdirect.net/eric/ConfigKit/src/commit/453da684d7c7d3843a5f505f5c468b4c889dc6e2/docs/roadmap/audit.md#a-schema-and-typed-values). - -Make pointer-only descriptor traversal terminate in decoding and path resolution, -while preserving supported null values and ordinary recursive structures. Make -dynamic descriptor construction transactional: a failed build must not leave -partial descriptors available to subsequent operations. Staging successful -additions or rolling back the complete failed addition set are implementation -choices; both must handle recursive references and preserve prior valid entries. - -Correct active-path cycle detection to distinguish finite slice views sharing -storage from actual cycles. Keep detachment and nil/empty behavior intact across -the callers that share this traversal machinery. - -Enforce existing projection budgets before large container reservations. Include -map-key materialization and overflow-safe lower-bound checks in the review; a -check after the reservation does not address the reported failure. This work -does not introduce a general heap limit or reject large containers independently -of existing budgets. - -Dynamic-type eligibility reuse depends on safe descriptor construction and retains -completed successful checks within the value operation. Keep failure diagnostics -specific to their current path and distinguish completed results from traversal visits. -This removes repeated descriptor-graph walks without a cross-load cache. - -### 2. Callback, Filesystem, And Dependency Lifetimes - -Selected entries: **B01, B02** in [callback lifetime](https://gitea.maximumdirect.net/eric/ConfigKit/src/commit/453da684d7c7d3843a5f505f5c468b4c889dc6e2/docs/roadmap/audit.md#b-callback-and-filesystem-lifetime) -and **C01, C02** in [filesystem traversal](https://gitea.maximumdirect.net/eric/ConfigKit/src/commit/453da684d7c7d3843a5f505f5c468b4c889dc6e2/docs/roadmap/audit.md#c-import-selection-and-link-resolution). - -Tie callback-context deactivation to the callback invocation with deferred cleanup -so it runs on normal return and panic unwinding. Independently give the owned -import root a deferred cleanup fallback. Preserve normal close-error handling, -caller ownership, and propagation of the original callback panic. Fixing either -state lifetime or handle lifetime does not fix the other. - -Preserve the phase of a directory failure. A genuinely absent directory before -successful opening remains subject to existing permissiveness rules; a later -read or close failure must not be treated as absence. Apply this distinction to -both supported directory-reading paths. Strengthen the shared filesystem tests -to check each handle's closure and structured, non-disclosing diagnostics, -including partial reads and failures masked by another successful directory. -Do not change regular-file close-error policy as a side effect. - -Avoid repeatedly expanding completed finite symlink dependencies in both host -and native filesystem resolution. Use operation-local memoization of completed -targets, or an equivalent dependency evaluation, with keys that respect the -resolution namespace. Keep active-path cycle detection separate from completed -work. Preserve containment, supported finite reuse, and existing host-path -semantics. The fix must work for directory and glob selection without a new -arbitrary traversal cap or changed symlink defaults. - -### 3. Retained History And Combination Evidence - -Selected entries: **D01, D02, D03, D04** in -[history and retained state](https://gitea.maximumdirect.net/eric/ConfigKit/src/commit/453da684d7c7d3843a5f505f5c468b4c889dc6e2/docs/roadmap/audit.md#d-retained-history-and-unused-evidence). - -Remove the unconsumed override-result pre-CLI copy and the unqueried published -structural-input copy. Preserve the separate pre-CLI state used by active replay -and the structural input still used before publication for metadata and secret -classification. Remove unused combination-evidence members and the work needed -solely to produce them, retaining projections, mappings, origins, and snapshots -that have active consumers. - -With retained roots clarified, enforce the existing history allowance against -the complete graph published through inspection, including replay and inheritance -state and publication-created copies. Count shared nodes once by pointer identity; -distinct retained copies still consume the allowance. Account for required copies -before creating them where practical. Removing an unused tree alone does not -resolve the accounting omissions. - -Keep required before/after evidence and existing inspection behavior. Validate -the complete publication path with small controlled allowances, including the -interaction of CLI, resolution replay, and inheritance, rather than relying only -on tests of an isolated counting helper. - -### 4. Collection-Policy Preparation And Application - -Selected entries: **E01, E02, E03** in -[collection policy and projection](https://gitea.maximumdirect.net/eric/ConfigKit/src/commit/453da684d7c7d3843a5f505f5c468b4c889dc6e2/docs/roadmap/audit.md#e-collection-processing-and-efficiency). - -Distinguish the existing policy state used for lookup, changes introduced by an -incoming tier, and evidence retained by the assembly. Prepare incoming claims and -declarations against read-only lower state, then apply the owned changes at the -appropriate combination boundary. Avoid copying every unrelated policy for -small tiers and scanning all policies for each independent replacement. - -Use a semantic-path index, localized updates, or equivalent bounded lookup to -find affected policy. Preserve retirement timing, policy established by the -incoming contribution, pending declarations, persistent explicit modes, and -nested record ownership. Copying remains appropriate wherever mutable ownership -requires it. Removing only one duplicate clone or shortening a function does not -address the identified growth patterns. - -Clarify preparation, evidence application, and finalization responsibilities. -Share straightforward history-descriptor translation where useful; evaluate -shared record-selection logic while keeping overlay and inheritance origin and -mode lifetimes distinct. Retain the common sequence-combination owner. This work -does not call for a generic merging framework. - -### 5. Active Acquisition Handoffs And Private Cleanup - -Selected entries: **F01, F02, F03, F04, F05, F06** in -[acquisition, cleanup, and test quality](https://gitea.maximumdirect.net/eric/ConfigKit/src/commit/453da684d7c7d3843a5f505f5c468b4c889dc6e2/docs/roadmap/audit.md#f-remaining-maintenance-and-test-improvements). - -Remove the test-only eager acquisition mode so acquisition retains scopes through -the active production protocol. Tests that need composition should invoke the -actual composition boundary explicitly. Retire the parallel test-only import -parser and route relevant fixtures through the active directive parser or the -appropriate declaration boundary, retaining malformed-input coverage. - -Clarify active acquisition and coordinator handoffs around cohesive tasks: -parsed-input directive/version handling, standalone placement, and repeated tier -application/projection. Use explicit inputs and results consistent with existing -state owners. Extracting helpers that continue to share surrounding mutable state -does not establish a clearer handoff. Preserve ordinary/layered differences, -overlay and diff behavior, source identity, callback timing, diagnostics, and -accounting scopes. - -Remove the disconnected root record-clone family, uncalled engine helpers, and -unread private transport/result members identified in the audit. Reconfirm -references on the implementation baseline, including function values and relevant -build configurations. Preserve live conversion paths and validation side effects; -the audit's absence of field reads is not a general rule for deleting state. -Replace meaningful ownership coverage through the public boundary before removing -tests that only exercise disconnected helpers. - -### 6. Local Lookup Improvements - -Selected entries: **E04, E05** in -[collection policy and projection](https://gitea.maximumdirect.net/eric/ConfigKit/src/commit/453da684d7c7d3843a5f505f5c468b4c889dc6e2/docs/roadmap/audit.md#e-collection-processing-and-efficiency) -and **F08** in [secret-failure lookup](https://gitea.maximumdirect.net/eric/ConfigKit/src/commit/453da684d7c7d3843a5f505f5c468b4c889dc6e2/docs/roadmap/audit.md#f08-index-secret-failure-location-queries-lazily). - -Reuse a local document lookup or equivalent traversal for tier-history projection -instead of rescanning wide mappings for each sequence. Preserve authored -coordinates, sparse projections, and evidence ownership. - -Restore compound identity fields using indexed mapping positions and membership -checks rather than rescanning child and merged mappings for every field. Preserve -child-authored values and locations, mapping order, and detached nodes. - -Build secret-failure location lookup state lazily when failures need it. Preserve -all diagnostics, nearest-location fallbacks, and confidentiality; successful -loads should not incur an unnecessary index merely to support failure reporting. -These changes address specific repeated work and do not promise a measured -startup improvement. - -### 7. Tests That Protect The Intended Behavior - -Selected entries: **F09, F10, F11, F12** in -[acquisition, cleanup, and test quality](https://gitea.maximumdirect.net/eric/ConfigKit/src/commit/453da684d7c7d3843a5f505f5c468b4c889dc6e2/docs/roadmap/audit.md#f-remaining-maintenance-and-test-improvements), -in addition to the regression coverage attached to each reported defect. - -Cover historical-path enumeration through the public API, including removed -paths, queryability, ordering, and detached returned slices. Use those assertions -to replace private representation checks where they address the same risk. - -Strengthen fuzz detachment checks by mutating existing descendants or in-length -elements, so spare capacity cannot hide aliases. Replace exact filesystem stat -call sequences with observable candidate ordering and meaningful failure -selection, retaining relevant assertions such as not opening excluded inputs. - -Consolidate facade scaffolding around public construction, loading, errors, -ownership, and lifecycle behavior. Preserve distinct integration risks and -reader-byte snapshot coverage. Existing coverage percentages or a desired test -count are not acceptance criteria for this work. - -## Implementation Plan - -The historical implementation plan at `bb5540b:docs/roadmap/implementation.md` -defines the sequential, Luna-scoped stages and their dependencies, including -coordinated changes to descriptor state, history ownership/accounting, collection -policy, acquisition, and replacement public coverage. It also records validation -commands and the complete finding-to-stage map. The audit remains the owner of -original evidence and detailed acceptance cases. - -## Validation And Completion - -Validation follows the audit's detailed acceptance cases and the -[testing policy](../policy/testing.md). For fatal or hanging regressions, use bounded -subprocesses; for budget cases, use small controlled limits. For symlink expansion -and repeated lookup work, prefer bounded operation evidence and algorithm review -over timing thresholds. Use real filesystem fixtures where filesystem semantics -matter and narrow fault injection for read/close failures. State platform limits. - -Applicable development checks come from the [development guide](../development.md), -including appropriate race coverage for changed ownership and lifetime boundaries. -The historical implementation plan owns their execution. Existing audit results -are baseline evidence, not validation of the eventual changes. No performance -benchmark campaign is required by this roadmap, and no new benchmark results are -claimed. - -Completion means all 29 selected findings have implementation and acceptance -evidence, the affected public behavior remains consistent with its contracts, -and canonical internal documentation describes the resulting owners. The -historical implementation plan records execution status without duplicate -progress inventories. Retire completed planning records under the normal -documentation lifecycle. - -## Scope Boundaries And Decision Status - -All three audit scope questions are settled. No significant architectural or -product decision remains open for this roadmap. The historical implementation -plan records private design choices and sequencing, subject to the acceptance -cases above. -If that work exposes a need to change a public contract, bring the specific -decision back for review rather than treating it as an implicit part of remediation. - -The glob preparation refactor (C03) and wider facade transport simplification -(F07) have since been selected in the -[internal-boundary roadmap](internal-boundaries.md). Other deferred features, -clone-traversal redesign, and broader performance experiments remain outside -this effort. Keep -existing ownership boundaries and loading APIs; no ADR or architecture-policy -change is proposed by this roadmap.