Files
ConfigKit/docs/roadmap/implementation.md

289 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Import Discovery And Facade Simplification Implementation Plan
Status: in progress.
Baseline: `bb5540b62e382106d59678e2edb7655acfd5c960`.
Audience: a Luna coding agent implementing the stages sequentially.
## Scope And Execution
Implement **C03** and **F07** from the
[internal-boundary roadmap](internal-boundaries.md). That document owns the target
state and preservation requirements; this plan owns concrete private choices,
steps, validation, and progress. The preceding 29-finding remediation sprint is
complete. Its implementation record remains at
`bb5540b:docs/roadmap/implementation.md`; this plan does not reopen it.
Read `AGENTS.md`, [agent policy](../policy/agents.md), the [development
guide](../development.md), and applicable architecture, testing, and documentation
policies. Use graph-first discovery and verify actual callers before deleting
helpers. Preserve unrelated work.
This is one end-to-end sprint. Intermediate stages may fail to compile, especially
the coordinated engine/facade cutover in Stages 2–3. Do not create adapters,
aliases, duplicate paths, or extra abstractions solely to make intermediate
stages green. Run focused checks once their affected surface is buildable; record
pending checks and finish them before final acceptance. Commit focused nonempty
units under repository policy and record stage status, actual validation, and
known commit identifiers here. Completion requires the integrated result, not
just checked-off stages.
### Boundaries
- Preserve public root-owned declarations and signatures, YAML syntax, loading
semantics, ordinary/layered differences, callback order, diagnostics, source
identity, inspection, confidentiality, and ownership.
- Preserve glob literal-versus-discovered rules and directory/glob differences.
Keep completed-link reuse, directory failure phases, containment, and existing
accounting. This is not a discovery algorithm or permission-policy change.
- Keep `resolvedImportPolicy`, registration compilation, normalized request
responsibilities, and conversions with real ownership or domain meaning.
Simplification is limited to defaults transport and placement construction.
- Add no dependencies, caller controls, caches, limits, background work, release
gates, or performance promises. Do not publish a release, update consumers, or
revise accepted ADR decisions.
### Selected Ownership Model
Use the existing engine `ImportDefaults` name and its exported `Required`,
`Follow`, `Hidden`, `Recursive`, and `Pattern` fields as the sole engine
input-defaults type. Root `ImportDefaults` stays separately defined. Root
conversion maps the fields without cloning them; the public loading wrappers
immediately enter engine preflight. `loadSettings` takes the per-load snapshot
before acquisition or callbacks, as it already does. The normalized request and
acquisition policy then borrow that load-owned read-only value.
Declaration resolution reads the owned defaults and retains its existing copies
of selected pattern/exclusion slices and destinations in `resolvedImportPolicy`.
This separates presence-aware load input from independently owned effective
declaration policy. No public copy-count or thread-safety guarantee follows.
For placement, expose the engine's existing enum as `PlacementMode uint8` with
`PlacementModeMapping` and `PlacementModeSequenceItem`, and expose placement
fields on engine input/options records for direct construction. These remain
internal-package implementation surfaces. Root types stay root-owned; direct
casts preserve unknown values for existing preflight validation.
### Validation Conventions
Run from the repository root with an installed compatible Go toolchain and
dependencies already available. Use `GOTOOLCHAIN=local`, `GOPROXY=off`, and
`GOTELEMETRY=off`. Keep disposable artifacts outside the repository and do not
change dependencies for tooling. Focused expressions identify behavior owners;
include a newly named regression explicitly if needed. Reuse unchanged successful
checks. No timing, allocation, exact syscall, or helper-shape assertions are
acceptance criteria.
## Stage 1 — Extract Patterned Glob Entry Preparation
Finding: **C03**. Owners: `internal/engine/glob.go`, existing glob selection,
link, and directory failure tests. Status: **complete**. Validation passed:
`env GOTOOLCHAIN=local GOPROXY=off GOTELEMETRY=off go test ./internal/engine -run 'Glob|Directory|Link|Filesystem|Resource'` and `git diff --check`.
1. Add `prepareGlobEntry` beside the selector. Take the filesystem access,
base path, parent `selectedImportCandidate`, `fs.DirEntry`, pattern segment,
`last` flag, and `resolvedImportPolicy`. Return a candidate, a boolean meaning
selected versus ordinary skip, and a nullable `*Diagnostic`. Use the parent's
existing `site`, which is propagated from the selector's root candidate.
No candidate-set, accounting, or traversal-state mutation belongs here.
2. Move only the patterned branch's per-entry body into this helper. Preserve
its order: `path.Match`; match/hidden filtering; lexical/access path creation
and containment; `importEntryTarget` and existing diagnostic translation;
follow filtering; intermediate-directory eligibility; host/native physical
path selection and canonicalization fallback; candidate label/site creation.
Return existing safe codes, paths, and sites. Do not turn inspection errors
into skips or skip validation merely because exclusion will later match.
3. Replace that inline body with the helper call and candidate insertion.
Leave pattern validation, `explicitGlobCandidate`, directory enumeration,
per-segment union/sorting, final-file validation, exclusions, requiredness,
and source-admission timing in the selector.
4. Keep directory entry preparation separate. Reuse existing filesystem/link
primitives; do not introduce a generic entry strategy or unify differing
missing-target, literal-link, filtering, or recursion rules. Remove imports
only when genuinely unused after extraction.
Acceptance: the selector now expresses traversal without embedding preparation
details. Reuse `glob_selection_test.go`, `glob_test.go`, filesystem-link tests,
and the preceding sprint's directory-phase regressions. They must still cover
literal hidden/link components versus wildcard filtering, aliases and native
spelling, escaping/cyclic links, matched-directory errors before exclusion,
selection order, and bounded accounting. Add a behavioral case only for a gap
identified during extraction, not a unit test asserting the new helper exists.
Focused check:
`go test ./internal/engine -run 'Glob|Directory|Link|Filesystem|Resource'`.
## Stage 2 — Consolidate The Engine Defaults Representation
Finding: **F07**, engine representation. Owners: `internal/engine/import_spec.go`,
`load.go`, `layered.go`, `load_request.go`, `acquisition.go`, `options.go`, and
their callers/fixtures. Status: **not started**.
1. Retain engine `ImportDefaults` with its existing five exported fields.
Remove private `importDefaults`, without a compatibility alias. Update
engine option/request/acquisition fields, helper signatures, readers, and
fixture literals to use the canonical type and exported default-field names.
Private containing fields such as `loadRequest.importDefaults` can keep their
names. Do not rename the distinct fields of `importDeclarationSpec` or
`resolvedImportPolicy` just because they have similar meanings.
2. Expose `ImportDefaults ImportDefaults` on engine `Options[T]` and
`LayeredOptions[T]`. Remove `OptionsWithImportDefaults`,
`LayeredOptionsWithImportDefaults`, and `privateImportDefaults`. Stage 3 changes
root callers; a temporary compile failure is preferable to an interim bridge.
3. Adapt `snapshotImportDefaults` to the canonical type, retaining nil boolean
pointers and nil versus non-nil empty pattern. Keep `cloneBool` and
`cloneStrings` where they still have real consumers. Keep current snapshot
call sites until Stage 3 establishes their final ownership placement.
4. Keep effective declaration policy resolution and validation semantics intact:
explicit false beats defaults, each control resolves independently, and
selector-specific applicability and depth checks stay unchanged. Do not
combine defaults with resolved policy or registration compilation.
Acceptance: one engine input-defaults type reaches every production path and
fixture; no equivalent private record or alias remains. Inspect ordinary,
layered, transitive-import, directory/glob, and test-helper call sites. The root
cutover and removal of redundant snapshots are explicitly pending until Stage 3.
Run the import/default focused tests after Stage 3 if the root cutover prevents
building the package here; do not report that deferred check as passed.
## Stage 3 — Simplify Facade Defaults Construction And Snapshot Ownership
Finding: **F07**, defaults handoff. Owners: root `conversion.go`, `lifecycle.go`,
`layered.go`; engine defaults/preflight/coordinator owners; `facade_import_defaults_test.go`
and `internal/engine/import_spec_test.go`. Status: **not started**.
1. Set engine `ImportDefaults` fields directly in root `toEngineOptions` and
`toEngineLayeredOptions`. Retain `toEngineImportDefaults` as the explicit
root-to-engine type mapping, but map its booleans/pattern without copies.
Remove root `cloneImportDefaultBool` and `cloneImportDefaultStrings` after
confirming no other caller. Keep public root types and declaration contracts.
2. Keep `snapshotImportDefaults` in `loadSettings` as the load-entry ownership
operation for both APIs. Remove repeated snapshots in engine `LoadLayered`
request construction, `executeLoadRequest` acquisition-policy construction,
and `resolveImportPolicy`. Carry the owned defaults read-only thereafter.
Review all callers of those helpers, including test setup; direct declaration
resolution can read its input because it returns its own concrete policy.
3. Keep per-policy copies of the selected pattern, exclusions, and destination.
Do not borrow caller-owned storage beyond preflight, mutate the shared owned
defaults, or remove copying from unrelated option/record paths. Each new load
snapshots its own current inputs; add no reuse cache or ownership flag.
4. Replace the private conversion-snapshot fragment in
`TestImportDefaultsPreserveEmptyPatternAndRequiredness`, which converts options,
mutates inputs, and calls engine `Load`. It should no longer demand a snapshot
from a private conversion helper. Keep that test's public nil/empty,
requiredness, and inspection-evidence checks.
5. Protect the public snapshot contract with a compact ordinary/layered table:
use a main document with a version and imports, enable
`SourceVersionModeIfPresent`, and use a configuration type with version and
imported-value fields. In the main version-check callback, mutate the caller's
recursive default from true to false and its existing pattern element from
`*.cfg` to `*.skip`. This callback runs after preflight but before imported
discovery. Verify the original recursive/pattern selection still supplies a
value from a nested imported file. Use existing fixture helpers and no
concurrent mutation. A subsequent load with those changed defaults must omit
that imported contribution rather than reuse the prior snapshot. Keep fixtures
reusable or create fresh readers for each load.
6. Reuse or extend the small snapshot/policy tests for all four optional booleans,
explicit false, pattern mutation, nil, and non-nil empty pattern. Preserve
existing invalid-pattern-before-read/hook tests and declaration override,
transitive, underlay, and selected-overlay coverage. Do not assert exact copy
counts, pointer identities, or the private conversion boundary as public policy.
Acceptance: the engine/facade cutover builds; public defaults remain detached
before acquisition/callbacks, reused loads remain independent, and only genuine
ownership copies remain. Confirm the absence of full defaults snapshots at the
three removed downstream sites by source review, not production instrumentation.
Focused checks:
`go test ./internal/engine -run 'Import|Glob|Directory|SourceVersion'` and
`go test . -run 'ImportDefaults|FacadeImport|SourceVersion'`.
## Stage 4 — Construct Placement Directly Across The Facade
Finding: **F07**, placement handoff. Owners: engine `destination.go`, `load.go`,
`layered.go`, normalized input and placement consumers; root `lifecycle.go`,
`layered.go`, and placement fixtures. Status: **not started**.
1. Rename the existing engine enum and its constants to `PlacementMode`,
`PlacementModeMapping`, and `PlacementModeSequenceItem`, preserving underlying
`uint8` values and zero semantics. Use the same type throughout engine
placement parsing, normalization, acquisition, source evidence, and version
projection. Do not add a second raw-mode record or type alias.
2. Expose engine `Options[T].Destination` and `.PlacementMode`, and
`Input.PlacementMode`. Normalized `loadInput` and other private owner fields
can retain their names. Populate the normalized input from those fields.
Remove `OptionsWithPlacement` and `InputWithPlacement` and migrate all callers,
including the direct engine construction in root `records_test.go`.
3. In root `toEngineOptions` and `toEngineInput`, set destination using the existing
`toEnginePath` conversion and placement using
`engine.PlacementMode(options.PlacementMode)` or its input equivalent.
Preserve invalid enum values for `normalizeInput` to diagnose before reads.
Do not add validation/defaulting to the facade or a new setter/cast function.
4. Preserve normalization of literal/parsed destination keys, root-item rejection,
wrapper accounting, mounted/item source coordinates, and version projection.
Root `Path` is immutable and `toEnginePath` carries its private engine value;
keep normalization and actual mutable ownership copies. Do not redesign path
conversion or add redundant clones solely to replace the deleted decorator.
5. Leave source-result transport such as `SourceInfoPlacementModeForFacade`,
source-version conversion, sequence registration preparation, and opaque
records alone. They are not input setters targeted by this step. Adapt tests
mechanically to new engine construction without weakening public assertions.
Acceptance: both public loading APIs retain zero/mapping and sequence-item
placement, errors before input consumption, final values, source metadata, and
history. Reuse the public placement tests plus destination/accounting,
item-import, mounted-version, and invalid-option cases. Root signatures still
contain no engine types, and engine exports remain below the internal boundary.
Focused checks:
`go test ./internal/engine -run 'Destination|Placement|Item|SourceVersion'` and
`go test . -run 'Placement|Destination|SourceInfo|SourceVersion|PublicFacadeBoundary|DependencyImportBoundary'`.
## Stage 5 — Integrate, Document, And Verify The Result
Findings: final acceptance for **C03, F07**. Status: **not started**.
1. Review both paths together: defaults resolving wildcard discovery in ordinary,
layered, and transitive imports; placement carrying the resulting sources
through composition and inspection; and failures stopping before later hooks.
Existing integration tests own these interactions. Add only a concrete missing
case rather than a second copy of the complete matrix or a new fixture framework.
2. Inspect the resulting handoffs. Confirm the glob selector no longer embeds
per-entry preparation, the engine has one defaults input type, direct fields
replaced the four input decorators, and the load snapshot is not repeated at
each private representation. Keep domain-specific policy/registration and
root conversion boundaries, necessary ownership copies, and existing budgets.
3. Update `docs/internal/imports.md` for the selector/preparation split and
`docs/internal/overview.md` for defaults conversion, preflight snapshot ownership,
and direct placement construction. Keep algorithms private and avoid public
performance or copy-count guarantees. No ADR/policy change is needed.
4. Replace C03/F07 detail in the first two `docs/roadmap/future.md` entries with
short pointers to `internal-boundaries.md`. Retain their heading anchors for
existing audit links; leave other future candidates unchanged. Correct old
roadmap references that would otherwise mistake this replacement plan for
the previous sprint's completed record: use
`bb5540b:docs/roadmap/implementation.md` for that historical plan. Do not reopen
the prior audit or retire unrelated planning records in this sprint.
5. Format changed Go files, finish deferred checks, run the final checks below,
and inspect the complete diff for API, dependency, semantics, or scope changes.
Record actual results and platform limitations here. Commit focused nonempty
units and hand off a clean workspace; do not create empty validation commits.
Final validation, following the existing development guide:
```sh
env GOTOOLCHAIN=local GOPROXY=off GOTELEMETRY=off ./scripts/check.sh
env GOTOOLCHAIN=local GOPROXY=off GOTELEMETRY=off go test -race ./...
git diff --check
```
The everyday check includes consumers, documentation links, and public/dependency
boundary tests. Confirm those existing tests ran rather than adding redundant
final invocations. Report filesystem/platform skips accurately; express macOS
validation, CI completion, release publication, extended fuzz campaigns, and
benchmarks are not additional acceptance requirements.
No product or architectural question remains open. The concrete choices above
complete the plan without changing the roadmap's public contracts; private
formatting and file organization can follow local idiom.