Compare commits
7 Commits
d898cb193d
...
v0.10.1
| Author | SHA1 | Date | |
|---|---|---|---|
| 50199eb0af | |||
| 34cbc0d63b | |||
| 5e12b2d360 | |||
| 8dfaf44d1d | |||
| 64be8b4df5 | |||
| e585decdb2 | |||
| 6440a500f7 |
@@ -189,8 +189,14 @@ remediation record are preserved in Git history at `0369111`, for example
|
||||
[configuration discovery](config.md#configuration-discovery); the public
|
||||
[discovery declarations](../discovery.go) own the Go API.
|
||||
|
||||
The [consolidation roadmap](roadmap/consolidation.md) owns the current refactoring
|
||||
proposal and the associated complexity-audit findings.
|
||||
The completed consolidation proposal, implementation plan, and resolved audit
|
||||
findings are preserved in Git history at `34cbc0d` under `docs/roadmap/`. For
|
||||
example:
|
||||
|
||||
```sh
|
||||
git show 34cbc0d:docs/roadmap/implementation.md
|
||||
git show 34cbc0d:docs/roadmap/remediation.md
|
||||
```
|
||||
|
||||
Potential enhancements are tracked in [future features](roadmap/future.md).
|
||||
|
||||
@@ -250,7 +256,7 @@ code changes. Use the component overview to find the corresponding implementatio
|
||||
| Layered loading and source equivalence | [Grouped lifecycle](../internal/engine/load_request_test.go), [layered facade](../facade_layered_test.go), [equivalence](../facade_equivalence_test.go) | `go test ./internal/engine -run '^TestGrouped'`, `go test . -run '^TestFacadeLayered'`, and `go test . -run '^TestEquivalentSourceForms'` |
|
||||
| Input and relative-import destinations, mounted sources, and source ownership | [Destination engine tests](../internal/engine/destination_test.go), [tier tests](../internal/engine/destination_tiers_test.go), [import traversal and version tests](../internal/engine/destination_import_test.go), [accounting tests](../internal/engine/destination_accounting_test.go), [lifecycle inspection tests](../internal/engine/destination_import_lifecycle_test.go), [public source/error tests](../facade_destination_test.go), [resource contexts](../facade_resource_context_test.go), and [consumer examples](../examples/consumers/destinations_test.go) | `go test ./internal/engine -run 'Destination|Import|Glob|Mounted'`, `go test . -run 'Destination|Import|SourceContext|FacadeMounted'`, and `go test ./examples/consumers` |
|
||||
| Shared source-version modes and evidence | [Engine source-version tests](../internal/engine/source_version_test.go), [public facade tests](../facade_source_version_mode_test.go), and [consumer example tests](../examples/consumers/layered_test.go) | `go test ./internal/engine -run 'SourceVersion'`, `go test . -run 'SourceVersion'`, and `go test ./examples/consumers` |
|
||||
| Schema, defaults, projection, cloning | [Implementation flow](internal/overview.md#implementation-flow), [schema](../internal/engine/schema_test.go), [decoding](../internal/engine/decode_test.go), [projection and typed cloning](../internal/engine/projection_test.go), [deferred clone review](roadmap/future.md#4-clone-traversal-review) | `go test ./internal/engine -run 'Schema\|Decode\|Projection\|Clone\|Inline\|CompareDocument'` |
|
||||
| Schema, defaults, projection, cloning | [Implementation flow](internal/overview.md#implementation-flow), [schema](../internal/engine/schema_test.go), [decoding](../internal/engine/decode_test.go), [projection and typed cloning](../internal/engine/projection_test.go) | `go test ./internal/engine -run 'Schema\|Decode\|Projection\|Clone\|Inline\|CompareDocument'` |
|
||||
| Lifecycle, resolution, validation, callback ownership | [Loading](../internal/engine/load_test.go), [resolution](../internal/engine/resolution_test.go), [validation](../internal/engine/validation_test.go), [ownership](../internal/engine/callback_ownership_test.go), [independence](../internal/engine/independence_test.go) | `go test ./internal/engine -run 'Load\|Resolution\|Validation\|Validator\|Hook\|Callback\|Parallel'` |
|
||||
| Paths, metadata, provenance | [Paths](../internal/engine/path_test.go), [metadata](../internal/engine/metadata_test.go), [provenance](../internal/engine/provenance_test.go) | `go test ./internal/engine -run 'Path\|Metadata\|Provenance\|Queries'` |
|
||||
| Passive resource contexts | [Engine contexts](../internal/engine/resource_context_test.go), [public contexts](../facade_resource_context_test.go) | `go test ./internal/engine -run 'Resource'` and `go test . -run 'SourceContext'` |
|
||||
|
||||
@@ -8,8 +8,9 @@ This note records implementation flow and private identities for maintainers.
|
||||
|
||||
Each source's root directive is parsed into presence-aware declarations and
|
||||
resolved against load-wide defaults. `acquisition.go` owns shared acquisition
|
||||
policy, load accounting, and per-tier accounting; `import_acquisition.go` owns
|
||||
reading candidate bytes, declaration traversal, and import admission. Candidate
|
||||
policy, load-wide accounting, and tier-scoped repetition/namespace state;
|
||||
`import_acquisition.go` owns reading candidate bytes, declaration traversal,
|
||||
and import admission. Candidate
|
||||
selection in `import_selection.go` preserves a normalized
|
||||
namespace path separately from canonical access identity: overlapping roots
|
||||
coalesce the same candidate spelling in that namespace, while aliases reached
|
||||
@@ -71,6 +72,12 @@ tuple comparisons. On a stalled graph, deterministic DFS over the residual graph
|
||||
returns a closed cycle rotated to its smallest key; blocked dependents are not
|
||||
reported as cycle members.
|
||||
|
||||
Import edge preparation builds an importer-to-target adjacency index once for
|
||||
an acquisition's outer and item-local scheduling, and once for grouped
|
||||
scheduling. Each scope gathers edges from its participating identities and
|
||||
retains only edges with both endpoints in its local slots. The retained
|
||||
inclusion graph still owns cycle diagnostics and source relationships.
|
||||
|
||||
Import composition schedules a main tier or one underlay group as a single
|
||||
strict-scope graph. Applicable collection declarations are gathered for the
|
||||
scope before peer composition, including identity inherited from lower tiers.
|
||||
|
||||
@@ -98,12 +98,14 @@ version, default-import, and override policy stays distinct from each input's
|
||||
source, filesystem, destination, and import settings.
|
||||
|
||||
[`acquisition.go`](../../internal/engine/acquisition.go) defines acquisition
|
||||
policy and load-wide and per-tier accounting state. Each input is acquired under
|
||||
that policy; grouped requests retain acquired underlay scopes until their tier is
|
||||
composed in [`load_request.go`](../../internal/engine/load_request.go). Live
|
||||
policy, load-wide accounting, and tier-scoped repetition/namespace state. Each
|
||||
input is acquired under that policy; grouped requests retain acquired underlay
|
||||
scopes until their tier is composed in
|
||||
[`load_request.go`](../../internal/engine/load_request.go). Live
|
||||
filesystem access handles are confined to acquisition and closed there. Resolved
|
||||
filesystem facts used to authorize access are separate from passively retained
|
||||
resource contexts: see
|
||||
resource contexts. Non-import validation retains those facts on `assembledInput`
|
||||
for passive-context construction and ordinary-overlay handling: see
|
||||
[`filesystem_context.go`](../../internal/engine/filesystem_context.go) and
|
||||
[`source_context.go`](../../internal/engine/source_context.go).
|
||||
|
||||
@@ -140,5 +142,5 @@ covered by `imports_test.go`, `load_request_test.go`, and focused source,
|
||||
discovery, and destination tests; inheritance by the inheritance test files;
|
||||
reflection behavior by `schema_test.go`, `projection_test.go`, and
|
||||
`decode_test.go`. The [development test map](../development.md#task-to-test-map)
|
||||
links focused commands. Typed clone extraction remains deferred in
|
||||
[`future.md`](../roadmap/future.md#4-clone-traversal-review).
|
||||
links focused commands. [`clone.go`](../../internal/engine/clone.go) owns typed
|
||||
copying, using the shared descriptors and per-operation traversal helpers.
|
||||
|
||||
42
docs/releases/v0.10.1.md
Normal file
42
docs/releases/v0.10.1.md
Normal file
@@ -0,0 +1,42 @@
|
||||
# ConfigKit v0.10.1
|
||||
|
||||
This patch release consolidates ConfigKit's internal loading machinery and
|
||||
reduces repeated import bookkeeping. Public APIs and configuration semantics
|
||||
remain unchanged from v0.10.0.
|
||||
|
||||
## Changes
|
||||
|
||||
- Consolidate ordinary and layered input acquisition, including import requests
|
||||
and selected-overlay handling, while retaining their distinct context and
|
||||
precedence rules. Remove unused private reverse conversions.
|
||||
- Separate import acquisition, scoped composition, and evidence projection;
|
||||
clarify inheritance preparation, combination, and finalization; and simplify
|
||||
reflective projection and schema-field assembly responsibilities.
|
||||
- Retain resolved filesystem facts from non-import validation for later passive
|
||||
resource contexts and ordinary overlays. Remove duplicate absent-host context
|
||||
derivation while preserving validation timing and handle cleanup.
|
||||
- Record empty optional-import selections directly against their declaring
|
||||
source, avoiding repeated searches through acquired sources. Index import
|
||||
dependencies for scope-local edge preparation instead of scanning the whole
|
||||
inclusion graph for every item scope. Stable dependency-ready ordering,
|
||||
provenance, and source destinations are preserved.
|
||||
- Expand regression coverage for import evidence ownership, scope boundaries,
|
||||
and broad main/underlay item imports. Improve
|
||||
[consumer loading guidance](../consumers/README.md) and internal documentation.
|
||||
|
||||
## Compatibility and upgrade
|
||||
|
||||
No consumer code or configuration migration is required. Composition, collection
|
||||
operations, inheritance, discovery, CLI precedence, source-version checks, and
|
||||
inspection retain their existing contracts.
|
||||
|
||||
Select this version and rebuild:
|
||||
|
||||
```sh
|
||||
go get gitea.maximumdirect.net/eric/ConfigKit@v0.10.1
|
||||
```
|
||||
|
||||
The consumer minimum remains Go 1.26.0. Dependencies and platform support are
|
||||
unchanged. Publication follows the existing [release procedure](../release.md).
|
||||
|
||||
[COPYRIGHT](https://gitea.maximumdirect.net/eric/ConfigKit/src/tag/v0.10.1/COPYRIGHT) | [LICENSE](https://gitea.maximumdirect.net/eric/ConfigKit/src/tag/v0.10.1/LICENSE) | [ATTRIBUTION](https://gitea.maximumdirect.net/eric/ConfigKit/src/tag/v0.10.1/docs/oss/attribution.md)
|
||||
@@ -1,481 +0,0 @@
|
||||
# ConfigKit Consolidation Toward 1.0
|
||||
|
||||
Status: selected target state; implementation pending. This document defines the
|
||||
refactor's scope and rationale; it does not authorize a release. The scope
|
||||
questions are settled. The [implementation plan](implementation.md) owns the
|
||||
concrete steps, sequencing, and implementation progress.
|
||||
|
||||
## Purpose
|
||||
|
||||
Consolidate the machinery introduced by recent import, collection, inheritance,
|
||||
and discovery work so ConfigKit is easier to understand, maintain, and adopt
|
||||
before its public contracts become stable. Success means fewer independently
|
||||
maintained paths through the same behavior, clearer state ownership, and concise
|
||||
consumer workflows. Reducing a function's line count is useful only when it
|
||||
serves those outcomes.
|
||||
|
||||
The review considered public API consolidation as well as internal extraction.
|
||||
The settled public direction retains the existing simple and layered loading APIs,
|
||||
consolidates their private translation and acquisition machinery, and removes
|
||||
unused reverse inspection conversions. No public signature or configuration
|
||||
semantic change is planned in this effort. This is an assessment of the reviewed
|
||||
consumer workflows, not a requirement to preserve every beta API in perpetuity.
|
||||
|
||||
## Evidence And Baseline
|
||||
|
||||
Reviewed worktree: `c9574ef`, immediately after the `v0.10.0` release at `5959537`.
|
||||
This document replaces the earlier `complexity.md` roadmap and `remediation.md`
|
||||
audit record. Their recommendations and relevant evidence are incorporated here;
|
||||
the original documents remain available in Git history:
|
||||
|
||||
```sh
|
||||
git show fbaaaf1:docs/roadmap/complexity.md
|
||||
git show fbaaaf1:docs/roadmap/remediation.md
|
||||
```
|
||||
|
||||
The earlier roadmap reviewed `7a83704f43ef9870db33661231cdff5d6ee502f7`, before
|
||||
discovery. The audit reviewed `5959537d1adc2516e7f7e8881df72b707ec48c3f`, including
|
||||
implemented discovery. The consolidation review checked their recommendations
|
||||
against that newer baseline and also considered the public API.
|
||||
|
||||
This review inspected the current public load/input/discovery declarations and
|
||||
maintained consumer examples, the shared request executor and acquisition flow,
|
||||
import parsing and selection, inheritance execution, reflective projection and
|
||||
cloning, schema field construction, and filesystem-context derivation. Graph
|
||||
caller traces and text-reference checks confirmed the unused reverse conversion
|
||||
chain. This was source review, not a new runtime correctness or performance audit.
|
||||
Neither this review nor the audit established a runtime correctness defect.
|
||||
|
||||
### Audit Measurements
|
||||
|
||||
The audit refreshed the codebase-memory graph and used function size, cognitive
|
||||
complexity, branch counts, and parameter counts to rank candidates for source
|
||||
inspection. It indexed 901 non-test functions/methods and 928 test-file
|
||||
declarations. Its fast index excludes examples, documentation, and scripts; a
|
||||
tracked-file inventory covered those separately. Standalone `gocyclo`, `gocognit`,
|
||||
`scc`, and `cloc` tools were unavailable.
|
||||
|
||||
These are the graph tool's reported measurements at the audited revision.
|
||||
Function spans include comments and blank lines. The scores support relative
|
||||
triage; they are not standardized acceptance thresholds or evidence of a defect.
|
||||
|
||||
| Function | Source | Lines | Cognitive score |
|
||||
| --- | --- | ---: | ---: |
|
||||
| `valueOperation.project` | [projection.go](../../internal/engine/projection.go) | 195 | 174 |
|
||||
| `parseImportDeclarationSpec` | [import_spec.go](../../internal/engine/import_spec.go) | 127 | 129 |
|
||||
| `valueOperation.clone` | [clone.go](../../internal/engine/clone.go) | 130 | 104 |
|
||||
| `schemaBuilder.describeFields` | [schema.go](../../internal/engine/schema.go) | 99 | 100 |
|
||||
| `assembleBaseInputTrackedForRole` | [load_input.go](../../internal/engine/load_input.go) | 188 | 96 |
|
||||
| `selectDirectoryImportCandidates` | [import_selection.go](../../internal/engine/import_selection.go) | 154 | 96 |
|
||||
| `valueBuilder.build` | [value.go](../../internal/engine/value.go) | 104 | 93 |
|
||||
| `decodeValue` | [decode.go](../../internal/engine/decode.go) | 115 | 85 |
|
||||
| `applyInheritanceScopeTemplates` | [inheritance_schedule.go](../../internal/engine/inheritance_schedule.go) | 219 | 75 |
|
||||
| `contextsForAcquiredSources` | [source_context.go](../../internal/engine/source_context.go) | 102 | 72 |
|
||||
|
||||
At that revision, the engine contained 88 production Go files and approximately
|
||||
20,144 physical lines; the root facade contained 16 files and 1,861 lines.
|
||||
`imports.go` was the largest production file at 1,195 lines. These measurements
|
||||
locate concentrated responsibilities. They do not establish a need for more
|
||||
packages or make the smaller document/YAML visitors and explicit facade
|
||||
conversions maintenance defects.
|
||||
|
||||
### Findings And Recommended Treatment
|
||||
|
||||
The finding numbers below preserve the audit's identifiers. The selected work
|
||||
remains unimplemented; the cloning portion of finding 3 is deferred to future
|
||||
work. Retiring the original documents does not mark their findings implemented.
|
||||
|
||||
| Audit finding | Maintenance priority | Assessment and recommended treatment |
|
||||
| --- | --- | --- |
|
||||
| 1. Import entry points and responsibilities | Higher | Main refactor. Include shared acquisition normalization, import-default ownership, declaration parsing, and directory admission. |
|
||||
| 2. Shared inheritance executor | Higher | Main refactor. Expose meaningful phases and explicit combination results while retaining one executor. |
|
||||
| 3. Projection and cloning | Higher for projection; cloning deferred | Main refactor for projection. Clone traversal review is tracked separately in [future.md](future.md#4-clone-traversal-review). |
|
||||
| 4. Schema field discovery and inline assembly | Medium | Include a focused refactor coordinated with projection. Keep one schema builder and its two descriptor views. |
|
||||
| 5. Resolved filesystem context | Medium | Include alongside acquisition work. Reuse resolved facts without merging resource lifetimes or changing validation timing. |
|
||||
| 6. Unused reverse inspection conversions | Small, independently reviewable cleanup | Include in the root facade and engine. Retain useful forward-conversion and detached-result coverage. |
|
||||
|
||||
Findings 1–3 confirm the earlier roadmap's main opportunities; findings 4–6 add
|
||||
focused work. The following sections own their rationale and intended changes.
|
||||
Implementation and validation will establish completion of the selected work;
|
||||
the deferred clone review retains its separate disposition.
|
||||
|
||||
Completed correctness and algorithmic remediations remain part of the baseline,
|
||||
including sequence records preserved at `ea46001:docs/roadmap/remediation.md`
|
||||
and discovery records preserved at `0369111:docs/roadmap/remediation.md`.
|
||||
Item-scope application matching and inheritance policy localization are examples
|
||||
of completed corrections that overlap this work and whose regressions should
|
||||
remain protected.
|
||||
|
||||
The [architecture policy](../policy/architecture.md),
|
||||
[configuration contract](../config.md), and exported declaration comments own
|
||||
existing constraints and behavior. The
|
||||
[component overview](../internal/overview.md) and
|
||||
[import internals](../internal/imports.md) locate implementation responsibilities.
|
||||
|
||||
## Consumer-Facing Target
|
||||
|
||||
### Keep Simple Loading Simple And Advanced Inputs Explicit
|
||||
|
||||
The current APIs already provide progressive setup:
|
||||
|
||||
- A simple consumer calls `Load[Config](File(path))`.
|
||||
- A consumer with defaults, imports, or resolution supplies `Options[Config]`.
|
||||
- Explicit discovery produces an ordinary `Source`, so it uses the same loading
|
||||
call and lifecycle.
|
||||
- A layered consumer supplies an `Input` and `LayeredOptions[Config]`, with
|
||||
`Underlay.Inputs` distinguishing strict peers from precedence tiers.
|
||||
|
||||
The two loading functions already converge on `loadRequest` and
|
||||
`executeLoadRequest`. Their public differences have meaning: ordinary options
|
||||
include the one input's controls; layered loading has per-input controls and a
|
||||
separate overlay filesystem. Ordinary overlay context follows its selected
|
||||
source, whereas layered overlay context is independently specified. These are
|
||||
observable conventions, not interchangeable forwarding spellings.
|
||||
|
||||
**Decision (OQ-1, settled):** retain `Load`, `LoadLayered`, `Options`, `LayeredOptions`,
|
||||
`Input`, and `Underlay`. Remove repeated translation and option reconstruction
|
||||
behind them. Keep common application rules in `Definition`; do not require
|
||||
applications to build a runtime loader or manually coordinate phases.
|
||||
|
||||
A single public request type could place main input, underlays, and common
|
||||
options together. It would reduce duplicated option fields and make promotion
|
||||
from ordinary to layered loading more uniform. A convenience entry could preserve
|
||||
the simplest call. Such a design would still need to resolve the existing
|
||||
overlay-context differences and demonstrate a clearer consumer model. The
|
||||
maintained examples do not show enough consumer benefit to justify that redesign
|
||||
in this effort. Retaining the current APIs accepts some duplicated public option
|
||||
fields and setup changes when adopting underlays; internal consolidation and
|
||||
clearer guidance address the observed maintenance needs. The decision is based on
|
||||
the resulting API, rather than the amount of refactoring needed to achieve it.
|
||||
|
||||
### Preserve Useful Convenience And Ownership Boundaries
|
||||
|
||||
| Surface reviewed | Recommended public end state |
|
||||
| --- | --- |
|
||||
| `Source`, `Filesystem`, `Search`, filename groups and presets | Retain typed, reusable source/search descriptions. Discovery selects a concrete source during loading; applications do not loop over complete loads. |
|
||||
| `FilesystemContext` | Retain the public description and current host/native rules. Consolidate its resolved private representation; do not expose acquisition handles or engine context records. |
|
||||
| `SequenceIdentityAt` and `SequenceIdentityMatching` | Retain the exact-path convenience and dynamic selector form over the common registration model. Both serve distinct consumer shapes. |
|
||||
| `Definition`, resolution and validation callbacks | Retain application-owned domain rules inside one recorded lifecycle. Preserve the distinction between mutable recorded resolution and read-only validation. |
|
||||
| Results, metadata, explanations, source context and diff | Retain detached, safe inspection with consumer-owned presentation. Remove unused inverse conversions without removing supported queries. |
|
||||
| Root wrappers and private conversions | Retain root-owned public types and explicit ownership conversion under ADR 0006. Consolidate repeated real conversions and delete unused ones. |
|
||||
|
||||
The consumer-facing work is to make the ordinary path easier to find and show
|
||||
how additional features compose, using the existing maintained examples. Update
|
||||
the adoption guide with a concise choice between simple loading, discovery, and
|
||||
layered loading, and show which options belong to an input versus a whole load.
|
||||
Keep exact behavior in its canonical owners. Examples should make application
|
||||
rules recognizable and avoid requiring consumers to recreate generic merging,
|
||||
presence, or provenance machinery.
|
||||
|
||||
Use the current simple README call, bundled-file and layered catalog examples,
|
||||
the import-focused prompt catalog, and the discovered sequence catalog to assess
|
||||
the end state. The latter exercises imports, underlays, independent overlay
|
||||
context, CLI assignment, and recorded resolution in one workflow. A high number
|
||||
of options in that example does not by itself justify a new abstraction.
|
||||
|
||||
## Internal Target
|
||||
|
||||
Keep the root facade, cohesive `internal/engine`, ordered document model, and
|
||||
private YAML adapter. Use ordinary private records, functions, and focused files
|
||||
to express responsibilities. Existing mapping combination, sequence combination,
|
||||
dependency scheduling, schema descriptors, and traversal accounting remain shared.
|
||||
|
||||
`executeLoadRequest` stays the visible lifecycle coordinator. It should show
|
||||
acquisition, assembly, inheritance, decoding/CLI assignment, resolution, secret
|
||||
processing, validation, and publication in their established order. Extract a
|
||||
phase only when it gives that work a clear input, output, or ownership boundary.
|
||||
Do not replace the visible lifecycle with a registry of configurable stages.
|
||||
|
||||
### 1. Normalize Loading Inputs And Consolidate Import Acquisition
|
||||
|
||||
The current executor constructs an engine `Options[T]` from `loadRequest`, and
|
||||
underlay acquisition constructs `Options[struct{}]` to call the same input
|
||||
assembler. `assembleBaseInputTrackedForRole` then combines discovery, directive
|
||||
processing, version rules, imports, placement, accounting, and contexts. Import
|
||||
entry points carry long positional lists through several forwarding variants.
|
||||
This repeats the representation of information already normalized for a load.
|
||||
|
||||
In [imports.go](../../internal/engine/imports.go), the
|
||||
`loadImportedDocumentsTrackedWithVersion`,
|
||||
`loadImportedDocumentsTrackedWithProcessing`, and
|
||||
`loadImportedDocumentsTrackedWithProcessingDeferred` variants have corresponding
|
||||
`loadImportedWithAccess...` variants. Both final signatures carry 13 parameters.
|
||||
Placement, processing results, sequence policy, accounting, repetition tracking,
|
||||
namespace identity, and deferred assembly must stay aligned through the chain.
|
||||
The same file's `importTraversal.visitPath` combines source-admission decisions
|
||||
in 174 lines, alongside acquisition, pending-item assembly, and evidence work.
|
||||
|
||||
Introduce cohesive private acquisition inputs and explicit state ownership,
|
||||
reusing `loadRequest`, `loadInput`, `acquisitionScope`, and `importTraversal`
|
||||
where they fit. Translate ordinary and layered public declarations into the
|
||||
shared loading model once. Acquisition should receive the source, input role,
|
||||
placement, context, and policies it needs without a fabricated application type
|
||||
or a reconstructed broad options object. Small adapters may preserve meaningful
|
||||
entry operations while callers migrate; remove obsolete forwarding variants
|
||||
within the completed refactor.
|
||||
|
||||
Separate immutable policy, mutable processing state, and results. Move import
|
||||
defaults out of `sourceVersionState` and into the load/acquisition policy owner.
|
||||
Version state continues to own version processing, projected effective version
|
||||
location, evidence, and diagnostics. Local and effective version rules have
|
||||
different meanings and must remain distinguishable.
|
||||
|
||||
The state boundaries are especially important:
|
||||
|
||||
| Lifetime or scope | State and responsibility |
|
||||
| --- | --- |
|
||||
| Whole load | Aggregate resource accounting, application definition/registrations, import defaults, shared version evidence and diagnostics. |
|
||||
| One strict tier | Repetition tracking shared by that tier's inputs, namespace scheduling anchors, and its inclusion/composition graph relationship. A later tier may reuse a file. |
|
||||
| Active import traversal | Active routes and inclusion edges, filesystem access ownership, and cycle detection, including edges crossing item-scope boundaries. |
|
||||
| One composition scope | Peer contributions, pending collection declarations, item-local helpers, and deferred item placement. Nested scopes do not become separate precedence tiers. |
|
||||
| Retained result | Source records, effective destinations, origins, history, and passive resource-context descriptions. |
|
||||
|
||||
Keep repetition tracking tier-scoped. In the current code,
|
||||
`acquireUnderlayGroupScopes` creates a fresh visited map per group. Preserve valid
|
||||
reuse across tiers while keeping shared aggregate limits. Namespace equality,
|
||||
candidate spelling, and stable scheduling keys also serve different purposes.
|
||||
|
||||
Give acquisition, already-acquired scope assembly, and evidence projection
|
||||
focused homes. Keep discovery at the acquisition boundary: resolve a search once
|
||||
per input consumption, then process the concrete file or absence. Preserve
|
||||
source IDs and source-local version processing in acquisition order, even when
|
||||
composition uses a different dependency-ready order. The layered main is acquired
|
||||
before underlays; lower-tier identity can still inform its later strict assembly.
|
||||
|
||||
Within `parseImportDeclarationSpec`, extract boolean/collection/placement field
|
||||
handling and selector-value parsing where it clarifies responsibilities. Keep
|
||||
field presence, authored error locations, selector applicability, and the order
|
||||
of consequential validation visible. Within `selectDirectoryImportCandidates` and
|
||||
`importTraversal.visitPath`, separate admission and descent decisions from
|
||||
traversal orchestration where they represent distinct work. Retain the existing
|
||||
root deduplication, candidate union, exclusions, sorting, and active-path cycle
|
||||
rules. No shared abstraction should conflate literal first-match main discovery
|
||||
with multi-file import selection.
|
||||
|
||||
### 2. Share Resolved Filesystem Facts Without Sharing Live Handles
|
||||
|
||||
`importFilesystem`, `nativeFilesystemAccess`, and `contextsForAcquiredSources`
|
||||
derive overlapping root/base/namespace facts. The latter reconstructs context
|
||||
from combinations of source kind and optional fields, including absent input.
|
||||
Discovery and independent layered overlays make these combinations consequential.
|
||||
Context construction is used by ordinary acquisition and both selected-overlay
|
||||
paths, so those callers need to use the same applicable derivation rules.
|
||||
|
||||
Use a small engine-owned resolved-context description for the facts actually
|
||||
needed by acquisition and passive views: namespace identity/kind, resolved root,
|
||||
source base, and selected source path when present. Preserve the distinction
|
||||
between host paths and native slash paths. Reuse already resolved facts where
|
||||
available instead of repeatedly deriving them from the original options.
|
||||
|
||||
Resolution and validation can occur at different existing lifecycle points.
|
||||
The record must not force every source to perform all filesystem checks early.
|
||||
Context descriptions, library-owned acquisition handles, and returned passive
|
||||
resource views have different lifetimes. Closing acquisition must remain
|
||||
explicit on success and failure; retaining context must not retain an open root
|
||||
handle or assume ownership of a borrowed `fs.FS`.
|
||||
|
||||
Cover selected and absent discovery outcomes, root-only absent host context,
|
||||
native sources, in-memory inputs with explicit context, and independently rooted
|
||||
overlays. Passive context remains associated with the source that contributed
|
||||
the value, including retained underlay and inherited values. Use the acquired
|
||||
source's canonical identity where available. Preserve containment failures,
|
||||
source associations, deferred search bases, and terminal selected-file errors.
|
||||
|
||||
Acquisition and context handling use the same private handoff rather than
|
||||
maintaining parallel models for overlapping callers. The description does not
|
||||
replace the public `FilesystemContext` or `ResourceContext` contracts.
|
||||
|
||||
### 3. Make Inheritance Combination Results Explicit
|
||||
|
||||
Keep the mapping and identified-record adapters and one shared executor.
|
||||
`applyInheritanceScopeTemplates` currently prepares templates, mutates values
|
||||
through merge callbacks, projects policy and origins, records replacements and
|
||||
replay evidence, and enforces budgets in one body. The callbacks mutate several
|
||||
surrounding result collections, making a combination difficult to review alone.
|
||||
|
||||
Give the executor named operations for child/control preparation, parent payload
|
||||
and policy preparation, combination, evidence construction, and retained-node
|
||||
accounting. Use a small private result for outputs of one combination where it
|
||||
replaces unrelated surrounding mutations. Value, policy, origin, replacement,
|
||||
and sequence-position evidence should come from the same combination outcome;
|
||||
do not rerun a merge to reconstruct its history.
|
||||
|
||||
Keep borrowed authored templates separate from owned resolved payloads and from
|
||||
retained history snapshots. Parent identity exclusion and child identity/origin
|
||||
preservation must apply consistently to values and history. Preserve nested
|
||||
unresolved instructions for destination-local evaluation, policy retirement on
|
||||
actual replacement, cancellation, and source-version movement through sequences.
|
||||
|
||||
The shared Kahn scheduler with stable keys remains unchanged. Resolve enclosing
|
||||
relationships before nested relationships and retain assembled record order.
|
||||
Preserve expansion limits and retained-history accounting for shared nodes;
|
||||
extraction must not charge shared evidence twice or make an unbounded snapshot.
|
||||
|
||||
### 4. Separate Projection Handlers And Clarify Schema Assembly
|
||||
|
||||
Keep `projectConfiguration` as the schema-checked entry and `valueOperation` as
|
||||
the operation-state owner. Move substantial struct, map, and sequence projection
|
||||
into focused handlers. Keep pointer/dynamic dispatch and scalar representation
|
||||
choices visible. All handlers share the same cycle, depth, and node state.
|
||||
|
||||
Struct projection needs particular care: physical members drive traversal,
|
||||
flattened fields drive name lookup and sensitivity, and an inline container is
|
||||
not retained as an extra output node. Preserve declaration order, deterministic
|
||||
map order, nil/empty distinctions, collision diagnostics, codec failures, and
|
||||
secret classification. Scalar projection retains the existing number, duration,
|
||||
text-codec, and secret-reference representations. Keep cycle entries active
|
||||
through child traversal and balance their release on every exit.
|
||||
|
||||
In `schemaBuilder.describeFields`, separate field discovery and tag parsing from
|
||||
inline struct assembly, inline map assembly, and ordinary field registration.
|
||||
Retain one builder/cache and its recursion detection. Make the paired updates to
|
||||
`members`, flattened `fields`, `fieldByName`, and `inlineMap` easy to follow.
|
||||
Keep index-path construction and inherited secret flags intact. These descriptor
|
||||
views are both needed; combining them into one collection would shift complexity
|
||||
into decoding and projection.
|
||||
Keep pointer-chain and recursive-inline decisions explicit, preserve supported
|
||||
field/tag forms and ambiguity paths, and retain cache/building-state behavior.
|
||||
|
||||
Clone traversal review and cleanup are deferred to
|
||||
[future.md](future.md#4-clone-traversal-review). This effort preserves cloning's
|
||||
existing behavior while changing the shared schema and projection machinery.
|
||||
Keep projection's document-building semantics distinct from typed copying.
|
||||
The mostly explicit dispatch in `decodeValue` and `valueBuilder.build` does not
|
||||
warrant a generic reflection visitor solely because it has a high complexity
|
||||
score.
|
||||
|
||||
### 5. Remove Unused Inspection Conversions And Consolidate Real Adapters
|
||||
|
||||
Remove `toEngineSourceInfo`, `toEngineOrigin`, `toEngineSourceVersionCheck`, and
|
||||
`toEngineImportSelections` from [conversion.go](../../conversion.go), along with
|
||||
the engine adapters used only by that chain.
|
||||
At the reviewed revision, the three callers of `toEngineSourceInfo` are tests;
|
||||
`toEngineOrigin` has none. `SourceInfoWithPlacement`,
|
||||
`SourceInfoWithImportSelections`, `importSelectorFromString`, and
|
||||
`importSelectionOutcomeFromString` in [records.go](../../internal/engine/records.go)
|
||||
have no production role beyond that reverse chain. Consumers receive inspection
|
||||
records; production facade operations do not send those records back to the
|
||||
engine. The unused inverse path adds another ownership mapping to maintain as
|
||||
inspection evidence grows. Recheck callers at the implementation baseline before
|
||||
removal.
|
||||
|
||||
Rewrite the affected [record tests](../../records_test.go) around real
|
||||
engine-to-public conversion and detached public inspection. Preserve useful
|
||||
placement, version evidence, import-selection, and copy-isolation assertions.
|
||||
Avoid manufacturing replacement production setters solely to keep an old
|
||||
round-trip fixture working. Existing facade loads already provide genuine sources
|
||||
and inspection records.
|
||||
|
||||
Retain reverse conversions that serve real inputs: `toEngineDiagnostics`, for
|
||||
example, supports `WriteDiagnostics`. Consolidate repeated override/input
|
||||
conversion in `toEngineOptions` and `toEngineLayeredOptions` where semantics
|
||||
match. Keep explicit root-to-engine ownership conversion and the public-boundary
|
||||
checks. A reflective transport framework or public engine aliases would obscure
|
||||
the boundary rather than simplify it.
|
||||
|
||||
## Scope, Compatibility, And Policy Consequences
|
||||
|
||||
The proposed work spans private engine code, private root-facade conversions,
|
||||
affected tests, consumer guidance, and implementation documentation. Under the
|
||||
settled answer to [OQ-1](#oq-1-should-the-public-loading-apis-change-in-this-effort),
|
||||
preserve public types/signatures and the behavior owned by the current contracts,
|
||||
including:
|
||||
|
||||
- Acquisition versus composition ordering, strict scopes versus precedence
|
||||
tiers, identity ownership, placement, and source-version protection.
|
||||
- Discovery exhaustion and error behavior, source lifetime, passive resource
|
||||
contexts, and host/native namespace distinctions.
|
||||
- Defaults, post-decode CLI assignment, recorded resolution, secret handling,
|
||||
final registered-identity enforcement, and semantic validation.
|
||||
- Explicit presence, provenance/history, sensitivity, safe diagnostics and diff,
|
||||
frozen inspection, and detached publication/query results.
|
||||
- Contracted diagnostic codes, paths, coordinates, ordering, and resource limits.
|
||||
|
||||
Do not turn incidental helper order, exact private representations, numerical
|
||||
complexity scores, or allocation counts into new guarantees. Keep the existing
|
||||
algorithmic remediations effective; watch for repeated whole-tree traversals,
|
||||
policy scans, or copies introduced by helper extraction.
|
||||
|
||||
The recommendation fits the existing architecture policy and ADRs 0006,
|
||||
0015, 0017, 0018, and 0019. It needs no new architectural ownership decision or
|
||||
policy revision merely to introduce private helpers. Update the component
|
||||
overview and import internals when code moves. Accepted ADR decision content
|
||||
remains unchanged. A public loading-model redesign is outside this effort's
|
||||
settled scope.
|
||||
|
||||
Pre-1.0 public changes are possible under the [release policy](../policy/release.md),
|
||||
but they must have a consumer benefit and documented impact. This draft does not
|
||||
propose a parallel compatibility API or temporary migration scaffold. It also
|
||||
does not make refactoring completion a new release gate or establish that 1.0
|
||||
is otherwise ready. Future performance experiments remain in
|
||||
[future.md](future.md#3-performance-experiments); no benchmark campaign was run
|
||||
or is required to claim readability improvements.
|
||||
|
||||
## Validation And Completion
|
||||
|
||||
Use the [development guide](../development.md) and
|
||||
[testing policy](../policy/testing.md) for focused checks and the ordinary full
|
||||
development check. Preserve existing behavior tests; rewrite tests coupled only
|
||||
to removed plumbing. Add a test when a consequential handoff risk lacks coverage,
|
||||
not for every new helper.
|
||||
|
||||
| Work area | Existing protection and review focus |
|
||||
| --- | --- |
|
||||
| Acquisition and imports | Selection, defaults, scheduling, relative/item placement, source-version evidence, aggregate limits, repetition within versus across tiers, and ordinary/layered equivalence. |
|
||||
| Resolved contexts | Host/native access, discovery selected/absent outcomes, terminal context failures, source-relative resources, independent overlay context, and handle closure. |
|
||||
| Inheritance | Mapping and record inheritance, nested destination-local evaluation, child identity, policy lifetime, version movement, history ordering, sensitivity, and retained-node budgets. |
|
||||
| Projection/schema | Inline fields, recursive schemas, dynamic values, nil/empty values, cycles and limits, text codecs, secret states, callback ownership, and independent loads; preserve existing cloning behavior where shared descriptors/state are affected. |
|
||||
| Facade and consumers | Forward conversion, detached records, supported API boundaries, source-form equivalence, maintained simple/catalog/discovery examples, and safe explanation/diff. |
|
||||
|
||||
Examples of established boundaries include
|
||||
[source-form equivalence](../../facade_equivalence_test.go),
|
||||
[discovery integration](../../facade_discovery_test.go),
|
||||
[final identity validation](../../facade_final_sequence_identity_test.go),
|
||||
[inheritance history](../../internal/engine/inheritance_history_test.go), and
|
||||
[projection and cloning](../../internal/engine/projection_test.go).
|
||||
Use applicable race checks when changing ownership or shared state. Reuse checks
|
||||
for unchanged content and keep benchmark work separate unless new evidence
|
||||
justifies it and it is authorized.
|
||||
|
||||
Completion requires reviewable evidence that:
|
||||
|
||||
- Simple and advanced consumer examples remain concise and valid, with no new
|
||||
public concepts required for existing workflows.
|
||||
- Both load entry points reach one normalized internal model; acquisition no
|
||||
longer reconstructs broad options or uses version state to transport defaults.
|
||||
- Import, context, inheritance, and reflection responsibilities have identifiable
|
||||
owners and explicit handoffs without duplicate semantic implementations.
|
||||
- Unused reverse conversions and their exclusive adapters are removed, with
|
||||
useful inspection ownership coverage retained.
|
||||
- Contracted values, presence, provenance/history, diagnostics, sensitivity,
|
||||
resource lifetime, and algorithmic corrections remain protected by appropriate
|
||||
tests and source review.
|
||||
- The reviewed scope is accounted for against all six audit findings, with clone
|
||||
traversal review deferred to future work. Completed portions have validation
|
||||
evidence rather than being closed because code moved.
|
||||
- Consumer guidance, contributor references, and internal documentation describe
|
||||
the resulting implementation. Current contracts continue to own behavior.
|
||||
|
||||
The [implementation plan](implementation.md) defines private records, file owners,
|
||||
and stage boundaries. No open scope questions remain, and no additional feature
|
||||
semantics are needed to carry out the selected refactors.
|
||||
|
||||
## Settled Decisions
|
||||
|
||||
### OQ-1: Should The Public Loading APIs Change In This Effort?
|
||||
|
||||
**Settled:** retain `Load`/`Options`, `LoadLayered`/`LayeredOptions`, `Input`, and
|
||||
`Underlay`, including their existing overlay-context behavior. Consolidate their
|
||||
private inputs, conversions, and acquisition machinery, and improve consumer
|
||||
guidance. The [consumer-facing target](#consumer-facing-target) records the
|
||||
rationale and tradeoffs. This decision governs the consolidation effort; it does
|
||||
not establish a perpetual restriction on future API design.
|
||||
|
||||
### OQ-2: How Much Clone Cleanup Should This Effort Commit To?
|
||||
|
||||
**Settled:** defer clone traversal review and any resulting cleanup to
|
||||
[future.md](future.md#4-clone-traversal-review). Neither the review nor clone
|
||||
refactoring is part of this consolidation effort. Projection and schema
|
||||
refactoring remain in scope, including protection of existing cloning behavior
|
||||
where they share descriptors or operation state. The future review can conclude
|
||||
that no clone edits are warranted; its goal is demonstrated maintenance benefit.
|
||||
@@ -83,36 +83,3 @@ 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?
|
||||
|
||||
## 4. Clone traversal review
|
||||
|
||||
Status: deferred. Review typed configuration cloning after the projection and
|
||||
schema consolidation work, when the resulting structure can inform the review.
|
||||
This is future maintenance work, not a prerequisite for completing consolidation.
|
||||
|
||||
The complexity audit identified `valueOperation.clone` in
|
||||
[clone.go](../../internal/engine/clone.go) as a candidate for cleanup. Much of its
|
||||
size is straightforward type dispatch. Focused handlers may clarify allocation,
|
||||
recursive copying, cycle tracking, secret-state handling, or text-codec
|
||||
reconstruction, but extraction may also add indirection without making the code
|
||||
easier to understand.
|
||||
|
||||
When this work is taken up, assess the traversal and record whether changes are
|
||||
worthwhile; a conclusion that no refactor is needed is valid. Make focused
|
||||
changes only where the maintenance benefit justifies the code movement. Keep
|
||||
cloning's typed-copy semantics distinct from projection's document-building
|
||||
semantics. Preserve detached values, nil/empty distinctions, physical inline
|
||||
fields, cycle/depth checks, and unresolved versus validated secret handling.
|
||||
Reuse existing ownership, codec, secret, and traversal tests, adding coverage for
|
||||
consequential gaps. No performance improvement is assumed.
|
||||
|
||||
### Questions to resolve before implementation
|
||||
|
||||
- After projection and schema consolidation, which clone branches still mix
|
||||
responsibilities enough to benefit from extraction?
|
||||
- Would a proposed helper keep allocation, recursive copying, and cycle cleanup
|
||||
easier to follow together, or merely move a short loop out of sight?
|
||||
- Are any small shared operations semantically identical across traversals, or
|
||||
would sharing require special cases that obscure their different purposes?
|
||||
- Which ownership or special-value risks, if any, lack sufficient existing test
|
||||
coverage for the proposed changes?
|
||||
|
||||
@@ -1,783 +0,0 @@
|
||||
# Consolidation Implementation Plan
|
||||
|
||||
Status: complete. Stages 1 through 20 are complete.
|
||||
The target executor is a Luna coding agent working through one end-to-end sprint.
|
||||
|
||||
## Authority, Baseline, And Execution
|
||||
|
||||
The [consolidation roadmap](consolidation.md) owns the target state, rationale,
|
||||
and settled scope. Start from `8a99e6107001c69117a41dc37bc6ea09d2910af9` plus the
|
||||
commit introducing this plan. Production code is unchanged from the audited
|
||||
`5959537d1adc2516e7f7e8881df72b707ec48c3f`; intervening commits consolidated the
|
||||
planning documents and settled the public-API and clone decisions.
|
||||
|
||||
Read [development.md](../development.md), [agents.md](../policy/agents.md),
|
||||
[architecture.md](../policy/architecture.md), [testing.md](../policy/testing.md),
|
||||
and [documentation.md](../policy/documentation.md) as applicable. Use the code
|
||||
graph for discovery and confirm source at the implementation baseline. Current
|
||||
public comments and [config.md](../config.md) define behavior; this plan does not
|
||||
add public obligations.
|
||||
|
||||
Implement only the selected consolidation. Retain the public `Load`,
|
||||
`LoadLayered`, options, inputs, callbacks, source/search descriptions, inspection,
|
||||
and sequence-registration APIs. Keep the root-to-engine-to-document/YAML
|
||||
dependency direction. Clone traversal review/refactoring remains deferred under
|
||||
[future.md](future.md#4-clone-traversal-review). Changes to shared descriptors must
|
||||
preserve existing cloning behavior. This sprint does not publish a release,
|
||||
update dependencies/toolchain requirements, change configuration semantics, or
|
||||
introduce complexity/performance gates.
|
||||
|
||||
Stages are bounded work assignments, not independently shippable commits.
|
||||
Intermediate stages may leave callers, tests, or compilation unfinished until a
|
||||
later stage. Proceed directly through those dependencies; do not build temporary
|
||||
compatibility layers solely to keep each stage compiling. Update affected tests
|
||||
with their callers, and run the focused checks below when that portion builds.
|
||||
Record any deferred verification accurately. The completed sprint must compile,
|
||||
pass its checks, and leave no superseded plumbing. Commit completed, reviewable
|
||||
units under the agent policy; stage boundaries do not require separate commits.
|
||||
|
||||
Preserve existing meaningful validation/processing order, diagnostics, ownership,
|
||||
and limits. Private helper order and representation are not new contracts. Do
|
||||
not add rejection rules, error recovery, caches, new merge strategies, another
|
||||
pipeline framework, or tests for incidental helper choreography. A correctness
|
||||
issue discovered during the refactor needs identifiable regression evidence and
|
||||
an explicit explanation, rather than an unexplained expectation change.
|
||||
|
||||
## Chosen Private Design
|
||||
|
||||
Use the existing `loadRequest[T]` as the normalized complete-load model and
|
||||
`loadInput` for each selected input. Keep generic application definitions and
|
||||
the final lifecycle in `executeLoadRequest[T]`; acquisition is non-generic.
|
||||
The following records are ordinary engine-private data, not extension points.
|
||||
Use these names to make migrations concrete; their fields describe the selected
|
||||
ownership model, not a requirement to duplicate data already available there.
|
||||
|
||||
| Record | Fields and ownership |
|
||||
| --- | --- |
|
||||
| `acquisitionPolicy` | Read-only operation snapshot of `importDefaults`, `sequencePolicy`, and `importLimits`. Snapshot caller-owned defaults at entry, preserving nil versus empty. |
|
||||
| `acquisitionState` | Borrowed pointers to the load's `importAccounting` and `sourceVersionState`. One instance per load; version state no longer transports import defaults. |
|
||||
| `tierAcquisitionState` | The tier's visited-resource map and shared host namespace token. Underlay groups each own a fresh instance. Existing group graph aggregation and namespace anchors retain their current owners. |
|
||||
| `inputAcquisitionRequest` | `input loadInput`, source-version role, root-directive context, applicable protection rule, deferred-assembly choice, and `ordinaryOverlay string`. The last field carries only the ordinary main input's overlay-dependent validation/context timing; it is empty for underlays and layered main. |
|
||||
| `importAcquisitionRequest` | The input request, parsed main source, and already computed source-version/directive processing result. It preserves the distinction between source-local processing and application-path protection. |
|
||||
| `resolvedFilesystemContext` | `native *nativeNamespace` (nil means host), `hostToken *namespaceToken` where supplied, and `rootPath`, `basePath`, `mainPath` strings. Native facts use slash-relative paths; host facts use the existing canonical host paths. The record contains no open root/file or acquisition access object. |
|
||||
|
||||
The main private entry is
|
||||
`acquireInput(request inputAcquisitionRequest, policy acquisitionPolicy, state *acquisitionState, tier *tierAcquisitionState) (assembledInput, []Diagnostic)`.
|
||||
Import acquisition uses the corresponding named request with the same borrowed
|
||||
policy/state/tier owners. An access-based worker receives a resolved filesystem
|
||||
description and an explicitly owned `*filesystemAccess`, rather than more
|
||||
positional paths and independent optional policy parameters. Retain
|
||||
`assembledInput`, `acquisitionScope`, and `acquiredContribution` as the structural
|
||||
handoffs; add an optional resolved-context field to `assembledInput` for reuse.
|
||||
|
||||
`importTraversal` borrows the policy and state references. Active routes, stack,
|
||||
current item scope/prefix/fallback, and the acquired inclusion graph remain
|
||||
traversal-local. Nested item traversal shares the active graph and tier repetition
|
||||
state; it does not create another tier. Reusing a file in a later tier remains
|
||||
valid. Do not collapse namespace identity, lexical candidate spelling, and stable
|
||||
scheduling keys into one key.
|
||||
|
||||
Context facts are obtained at the existing points where those facts are needed.
|
||||
There is no global eager context-validation pass or persistent live-handle owner.
|
||||
Directory checks, canonicalization, handle opening/closing, and passive view
|
||||
creation remain distinguishable. Reusing a description does not authorize
|
||||
earlier checks that bypass an existing earlier error or implicit loading activity
|
||||
after publication. Consumer-initiated passive filesystem operations retain their
|
||||
existing contract.
|
||||
|
||||
For inheritance, retain both scope adapters and one executor. Use private
|
||||
prepared-child/prepared-parent records for combination inputs, then an
|
||||
`inheritanceTemplateResult` containing the resolved value, origins, local policy,
|
||||
policy templates, and the controls/transfers/replacements/operations produced by
|
||||
that template. Sequence position evidence lives in the existing operation
|
||||
records. Merge callbacks mutate that local result, not the enclosing executor's
|
||||
accumulator. The executor appends the result and applies existing accounting.
|
||||
|
||||
For reflection, keep `valueOperation` and `schemaBuilder` as their existing state
|
||||
owners. Extract substantial value-kind and inline-field operations as methods;
|
||||
do not introduce a generic reflection visitor or merge the two schema views.
|
||||
|
||||
## Validation Groups
|
||||
|
||||
Use the installed compatible Go toolchain and already acquired dependencies as
|
||||
described in the development guide. Commands run from the repository root with
|
||||
`GOTOOLCHAIN=local` and `GOPROXY=off`. These are development checks for this
|
||||
refactor, not additions to the release procedure.
|
||||
|
||||
| Group | Commands and purpose |
|
||||
| --- | --- |
|
||||
| F: facade cleanup | `go test . -run 'SourceInfo|SourceVersionCheck|ImportSelection|Facade|PublicFacadeBoundary|DependencyImportBoundary'` checks useful forward evidence and the existing public boundary. |
|
||||
| A: acquisition integration | `go test ./internal/engine -run 'Load|Grouped|Source|Search|Discovery|Import|Glob|Filesystem|Native|Destination|Overlay|Resource|Version|Sequence|Collection'`; `go test . -run 'Facade|EquivalentSourceForms|Search|SourceContext|ImportDefaults|SourceVersion'`; `go test ./examples/consumers`. Covers normalization, ordinary/layered acquisition, selection, contexts, scopes, and consumer workflows. |
|
||||
| I: inheritance | `go test ./internal/engine -run 'Inheritance|Inherited|Sequence|Collection|Version|History|Explain|Sensitivity|Secret'`; `go test . -run 'Inheritance|Inherited|SequenceIdentity|FinalSequence'`. Covers shared adapters and their evidence. |
|
||||
| R: reflection | `go test ./internal/engine -run 'Schema|Decode|Projection|Clone|Inline|CompareDocument|Callback|Ownership|Independent|Secret'`. Covers descriptors, projection, shared cloning behavior, and ownership. |
|
||||
| Final | `./scripts/check.sh` followed by `go test -race ./...`. The first owns formatting, whitespace, ordinary tests/examples/doc links, vet, and build; race validation is appropriate because state and ownership handoffs change throughout this sprint. |
|
||||
|
||||
Run F after Stage 2, A after Stages 9 and 13, I after Stage 16, and R after
|
||||
Stage 18 when buildable. If earlier migrations still prevent compilation, repair
|
||||
them at the next integration point and run the deferred group there. Run the
|
||||
final checks once on the finished code/documentation; repeat only for subsequent
|
||||
changes affecting their evidence. No benchmark campaign, timing threshold,
|
||||
traversal-count test, extended fuzz campaign, CI wait, or manual platform check is
|
||||
required by this plan. Add narrow behavior tests only for consequential gaps
|
||||
identified below that existing tests do not already cover.
|
||||
|
||||
## Stage 1 — Establish The Starting Map (Complete)
|
||||
|
||||
**Work:**
|
||||
|
||||
1. Confirm the revision/worktree and preserve unrelated work. Read the roadmap's
|
||||
settled decisions and the selected owners above.
|
||||
2. Map the current callers of `selectOptions`, `assembleBaseInput...`, both
|
||||
`loadImported...` ladders, `contextsForAcquiredSources`, the selected-overlay
|
||||
loaders, and `applyInheritanceScopeTemplates`. Inspect test callers as well
|
||||
as production callers; some old entry variants exist mainly for tests.
|
||||
3. Identify the existing behavioral owners for repeated resources within/across
|
||||
tiers, enabled/disabled version checking, selected/absent searches, ordinary
|
||||
versus independent layered overlays, item-local helper scopes, inherited
|
||||
identity/history, inline fields, and detached records.
|
||||
4. Keep brief working notes or implementation progress in this plan. Reuse
|
||||
existing validation evidence where applicable; establish an implicated
|
||||
baseline only when needed to distinguish a pre-existing failure. Do not
|
||||
generate another broad audit or blanket characterization-test suite.
|
||||
|
||||
**Finish:** a concrete caller/coverage map and any actual coverage gaps. No
|
||||
production change or empty commit is needed for orientation alone.
|
||||
|
||||
Progress notes (2026-10-25): the worktree was clean at `8ab1568`, immediately
|
||||
after the planning baseline `8a99e61`; the audited production baseline
|
||||
`5959537` is present. `selectOptions` is called by the ordinary and grouped
|
||||
load entry points. The `assembleBaseInput...` wrappers converge on
|
||||
`assembleBaseInputTrackedForRole`, reached from ordinary loading, layered main
|
||||
loading, and underlay-group acquisition. The tracked import wrappers converge
|
||||
on `loadImportedDocumentsTrackedWithProcessing`; production callers are input
|
||||
acquisition and underlay-group acquisition, while older convenience variants
|
||||
also have direct test callers. `contextsForAcquiredSources` is shared by ordinary
|
||||
loading, layered assembly, input acquisition, and overlay application. Selected
|
||||
overlay loading is reached from ordinary overlay application and layered input
|
||||
assembly. Both inheritance adapters, for ordinary and identified sequences,
|
||||
feed `applyInheritanceScopeTemplates`.
|
||||
|
||||
Existing behavioral owners cover repeated/cyclic resources and cross-tier reuse
|
||||
(`internal/engine/imports_test.go`, `facade_equivalence_test.go`); default and
|
||||
enabled source-version modes (`internal/engine/source_version_test.go`,
|
||||
`facade_source_version_mode_test.go`); selected/absent discovery
|
||||
(`internal/engine/discovery_load_test.go`, `facade_discovery_test.go`); ordinary
|
||||
and layered overlays (`internal/engine/selected_overlay_test.go`,
|
||||
`facade_layered_test.go`); item-local helper scopes (`internal/engine/imports_test.go`,
|
||||
`facade_import_lifecycle_test.go`); inheritance identity/history
|
||||
(`internal/engine/inheritance_identity_history_test.go`,
|
||||
`internal/engine/inheritance_history_test.go`); inline fields
|
||||
(`internal/engine/schema_test.go`, `internal/engine/projection_test.go`); and
|
||||
detached results and callback ownership (`internal/engine/metadata_test.go`,
|
||||
`internal/engine/callback_ownership_test.go`, `facade_diff_test.go`). No specific
|
||||
coverage gap surfaced in this map; later focused checks remain assigned in the
|
||||
validation groups above.
|
||||
|
||||
## Stage 2 — Remove Unused Reverse Bridges And Share Real Facade Conversion (Complete)
|
||||
|
||||
**Owners:** root `conversion.go`, `records_test.go`, `lifecycle.go`, `layered.go`,
|
||||
and engine `records.go`.
|
||||
|
||||
1. Reconfirm callers, then delete `toEngineSourceInfo`, `toEngineOrigin`,
|
||||
`toEngineSourceVersionCheck`, and `toEngineImportSelections`, plus
|
||||
`SourceInfoWithPlacement`, `SourceInfoWithImportSelections`,
|
||||
`importSelectorFromString`, and `importSelectionOutcomeFromString` when their
|
||||
only remaining purpose is the deleted chain.
|
||||
2. Rewrite the three reverse-round-trip record tests using genuine engine output
|
||||
and forward conversion/public inspection. Retain placement, version-evidence,
|
||||
selection-evidence, nil/empty, and detached-copy protection. Engine test
|
||||
fixtures can use existing loading APIs; do not add production fixture setters.
|
||||
3. Extract one root `toEngineOverrides` helper and use it from both option
|
||||
converters. Reuse `toEngineInput`, filesystem, path, definition, and default
|
||||
conversion where already shared. Preserve slice ownership and nil/empty
|
||||
behavior; do not make conversion interpret/validate input values.
|
||||
4. Retain reverse diagnostic conversions used by `WriteDiagnostics`, forward
|
||||
source/origin inspection conversion, and all public named types.
|
||||
|
||||
**Finish:** the unnecessary cross-package inverse interface is gone and real
|
||||
conversion is shared without a new transport layer. Run F.
|
||||
|
||||
Progress notes (2026-10-25): removed the unused reverse source-record bridge
|
||||
and engine setters, replaced synthetic round-trip fixtures with engine loads,
|
||||
and shared override conversion across ordinary and layered options. Validation
|
||||
group F passed with `GOTOOLCHAIN=local GOPROXY=off go test . -run
|
||||
'SourceInfo|SourceVersionCheck|ImportSelection|Facade|PublicFacadeBoundary|DependencyImportBoundary' -count=1`.
|
||||
|
||||
## Stage 3 — Normalize Preflight Without Fabricated Options (Complete)
|
||||
|
||||
**Owners:** engine `options.go`, ordinary entry in `load.go`, grouped entry in
|
||||
`load_request.go`, and their option tests.
|
||||
|
||||
1. Extract non-generic input normalization/validation over `loadInput`, returning
|
||||
the normalized input plus diagnostics. Pass the ordinary overlay name only
|
||||
where its context requirements apply. Keep discovery-description validation,
|
||||
placement checks, normalized destinations, and filesystem option checks at
|
||||
their existing pre-acquisition boundary.
|
||||
2. Separate load-wide import-default validation, version-rule/mode validation,
|
||||
and sequence-registration compilation from per-input checks. Share their
|
||||
mechanisms between entries without adding a second interpretation of options.
|
||||
3. Keep the public/engine entry arity handling and each entry's existing error
|
||||
classification. Ordinary loading still translates its one selected options
|
||||
value; layered loading validates main, all underlay inputs, and its independent
|
||||
overlay context in the existing meaningful order. Preserve canonical diagnostic
|
||||
accumulation and early returns, including invalid options before reads.
|
||||
4. Replace the grouped entry's constructed `Options[T]` values with direct
|
||||
normalization of `loadInput`; set the effective protection rule from the
|
||||
normalized main placement. Leave typed application behavior in the definition.
|
||||
5. Remove `importDefaultsValidated` and preflight-only forwarding machinery when
|
||||
callers migrate. Update tests through the entry/helper that owns the behavior,
|
||||
rather than keeping `selectOptions` solely as a test compatibility surface.
|
||||
|
||||
**Finish:** both entries produce validated `loadRequest[T]` values without using
|
||||
the ordinary options type to validate grouped inputs. Acquisition may still use
|
||||
old options until Stage 6; do not broaden this stage into that migration.
|
||||
|
||||
Progress notes (2026-10-25): added shared normalization over `loadInput`, moved
|
||||
load-wide defaults, source-version, and sequence-registration validation into
|
||||
shared settings preparation, and removed `selectOptions` plus its validation
|
||||
flag from grouped preflight. `GOTOOLCHAIN=local GOPROXY=off go test ./...` passed.
|
||||
|
||||
## Stage 4 — Express Input Acquisition With Non-Generic Requests (Complete)
|
||||
|
||||
**Owners:** new engine `acquisition.go` and existing `load_input.go`.
|
||||
|
||||
1. Define `acquisitionPolicy`, `acquisitionState`, `tierAcquisitionState`,
|
||||
`inputAcquisitionRequest`, and `importAcquisitionRequest` as selected above.
|
||||
Reuse existing enums, policy records, and accounting; introduce no new public
|
||||
controls or duplicate budget counter.
|
||||
2. Replace the generic `assembleBaseInputTrackedForRole` implementation with
|
||||
`acquireInput`. Translate its references to source, imports, filesystem,
|
||||
placement, role, directive context, version rule, policy, and deferred
|
||||
assembly into explicit request/policy/state fields.
|
||||
3. Keep its phases visible: search selection; parse and failed-source evidence;
|
||||
disabled-directive checks; source-local version/directive processing; import
|
||||
acquisition or single-input placement; accounting/repetition registration;
|
||||
context handoff. Small helpers may isolate version/directive preparation.
|
||||
4. Preserve discovery resolution once per input consumption and the acquired
|
||||
concrete source on both success and failure. Keep mapping diagnostic
|
||||
destination projection and the ordinary-overlay context deferral unchanged.
|
||||
5. Retain `assembledInput`'s existing structural/evidence fields. Do not merge
|
||||
parsed/local payloads with placed documents or eagerly compose deferred input.
|
||||
|
||||
**Finish:** the shared input-acquisition implementation no longer depends on
|
||||
`Options[T]` or an application type. Call-site and import-worker migration is
|
||||
completed by Stages 5–6; temporary non-compilation is acceptable.
|
||||
|
||||
Progress notes (2026-10-25): introduced the non-generic acquisition request and
|
||||
policy/state/tier records, moved the input-acquisition implementation into
|
||||
`acquireInput`, and added the resolved-context handoff field. The focused engine
|
||||
test command currently cannot compile because the old assembly callers in
|
||||
`load_input.go` and `load_request.go` are scheduled to migrate in Stage 6; test
|
||||
verification is deferred until that integration.
|
||||
|
||||
## Stage 5 — Consolidate Import Requests And Default Ownership (Complete)
|
||||
|
||||
**Owners:** engine import entry points, `importTraversal`, `source_version.go`,
|
||||
and tests that directly invoke the old import variants.
|
||||
|
||||
1. Replace the `loadImportedDocumentsTracked...` ladder with
|
||||
`acquireImportedInput` using `importAcquisitionRequest` and the shared
|
||||
policy/state/tier owners. Replace the access-based ladder with one worker
|
||||
receiving the same request plus explicit access/resolved facts. Keep only
|
||||
genuinely distinct convenience entries, not one wrapper per optional field.
|
||||
2. Move `importDefaults` out of `sourceVersionState`. Resolve declarations from
|
||||
`acquisitionPolicy.importDefaults`, including nested item scopes. Import
|
||||
defaults must work independently of whether a version rule/state is active.
|
||||
Preserve enabled version processing and its separate local/effective rules.
|
||||
3. Make traversal references to aggregate accounting, version evidence, tier
|
||||
repetition, and immutable policies point to their existing operation owners.
|
||||
Keep active-route tracking, scope switching, and inclusion edges local to
|
||||
the traversal. Do not allocate a fresh visited map inside a nested item scope.
|
||||
4. Preserve nil version-state behavior used by lower-level operations and the
|
||||
existing default processing branch. Replace optional positional/variadic
|
||||
policy passing with explicit inputs for meaningful operation distinctions.
|
||||
Private test entry spellings do not need compatibility wrappers; preserve
|
||||
their relevant behavioral cases through the resulting operation.
|
||||
5. Migrate direct import tests to named inputs. Small test-only constructors may
|
||||
supply default state/policy values when this reduces repetitive fixtures.
|
||||
Keep their behavior assertions intact.
|
||||
|
||||
**Finish:** the import request surface has named cohesive inputs; defaults no
|
||||
longer travel through version processing; no forwarding ladder remains merely
|
||||
to supply another nil/default positional parameter.
|
||||
|
||||
Progress notes (2026-10-25): replaced the import entry ladders with named
|
||||
requests and one access worker, moved defaults to acquisition policy, and
|
||||
retargeted direct import tests. The focused engine test command cannot compile
|
||||
yet because the remaining ordinary and grouped assembly callers are scheduled
|
||||
for Stage 6; verification is deferred until that integration. `git diff --check`
|
||||
passed.
|
||||
|
||||
## Stage 6 — Wire Ordinary And Grouped Lifecycle To Acquisition (Complete)
|
||||
|
||||
**Owners:** engine `load.go`, `load_request.go`, `load_input.go`, related tests.
|
||||
|
||||
1. Construct the load's acquisition policy and mutable state once in
|
||||
`executeLoadRequest`. Keep the definition and schema/callback lifecycle there.
|
||||
2. Call `acquireInput` for main using its ordinary/layered directive context,
|
||||
main version role, current protection rule, and deferred assembly. Preserve
|
||||
the existing schema-build and version-check timing, including the different
|
||||
default/enabled source-version paths.
|
||||
3. Have `acquireUnderlayGroupScopes` use the same load policy/accounting/version
|
||||
state and one fresh tier state per group. Remove `Options[struct{}]`
|
||||
construction. Preserve explicit namespace anchors and graph reanchoring.
|
||||
4. Main acquisition still precedes underlays. Strict assembly still follows
|
||||
the existing dependency-ready scopes and lower-tier identity information.
|
||||
Keep group-local repetition separate from load-wide limits, and retain the
|
||||
existing selected-overlay duplicate-source checks.
|
||||
5. Replace the ordinary `applyInputLayers` options argument with the precise
|
||||
main/overlay/protection/policy inputs it uses. A named overlay request will
|
||||
complete that migration in Stage 9. Remove remaining production
|
||||
`assembleBaseInput...` variants and the acquisition-only `sequencePolicy`,
|
||||
`deferAssembly`, and `directiveContext` fields from `Options`. Retain the
|
||||
placement, destination, and import-default fields used by facade translation.
|
||||
6. Retain real lower-level test/benchmark capabilities with direct request
|
||||
construction or test-only adapters. Do not delete meaningful source/limit
|
||||
cases to make removed helpers disappear.
|
||||
|
||||
**Finish:** no executor or underlay caller reconstructs broad/generic options
|
||||
for acquisition. The shared path is wired end to end; context and import-flow
|
||||
extraction continue in later stages.
|
||||
|
||||
Progress notes (2026-10-25): `executeLoadRequest` now creates one acquisition
|
||||
policy/state and acquires the main input through `acquireInput`; underlay groups
|
||||
reuse those load owners with a fresh tier state. Removed the generic base-input
|
||||
assembly variants and the acquisition-only fields from `Options`, and adapted
|
||||
direct tests to construct named requests. A search-source regression was caught
|
||||
and fixed during verification. The focused engine selection, facade selection,
|
||||
consumer example tests, and `GOTOOLCHAIN=local GOPROXY=off go test ./...` passed.
|
||||
|
||||
## Stage 7 — Separate Resolved Filesystem Facts From Access (Complete)
|
||||
|
||||
**Owners:** new engine `filesystem_context.go`, `filesystem.go`,
|
||||
`filesystem_access.go`.
|
||||
|
||||
1. Add `resolvedFilesystemContext` with the fields selected above. Reuse native
|
||||
namespace/token identity; do not compare arbitrary `fs.FS` values or equate
|
||||
equal labels with equal namespaces.
|
||||
2. Extract host and native fact derivation from `importFilesystem` and
|
||||
`nativeFilesystemAccess`. Prefer a selected `SourceInfo.Path` when it already
|
||||
supplies canonical source identity. Preserve source-derived file bases,
|
||||
explicit in-memory roots/bases, native slash paths, and the root-only absent
|
||||
host default.
|
||||
3. Keep separate functions for the directory checks and opening access that
|
||||
currently occur at each operation. A returned description does not mean
|
||||
every possible check has run. Do not add validation-state flags or an
|
||||
all-purpose eager resolver with stronger acceptance rules.
|
||||
4. Adapt the import worker to consume resolved facts and explicitly own its
|
||||
access until the existing acquisition handoff closes it. Keep close-error
|
||||
reporting and primary-error precedence unchanged. Native access borrows its
|
||||
caller filesystem and must not close it.
|
||||
5. Keep any old derivation adapters only while genuine callers migrate in
|
||||
Stages 8–9; remove them at the end of that migration.
|
||||
|
||||
**Finish:** common context facts have one private representation while validation
|
||||
timing and live resource ownership remain explicit.
|
||||
|
||||
Progress notes (2026-10-25): added shared host/native filesystem descriptions,
|
||||
separated directory validation and access creation, and migrated import
|
||||
acquisition to pass resolved facts alongside its owned access. The prior
|
||||
derivation adapters remain for the validator and overlay migration. Focused
|
||||
acquisition tests, `GOTOOLCHAIN=local GOPROXY=off go test ./...`, and
|
||||
`git diff --check` passed.
|
||||
|
||||
## Stage 8 — Carry Context Facts Through Input And Passive Views (Complete)
|
||||
|
||||
**Owners:** engine `load_input.go`, `source_context.go`, `load_request.go`, and
|
||||
the ordinary context backfill in `load.go`.
|
||||
|
||||
1. Add the optional resolved-context field to `assembledInput`. Populate it
|
||||
from import acquisition or the existing non-import context validation point.
|
||||
When context work is deferred for an ordinary overlay, leave it deferred.
|
||||
2. Replace the source-kind derivation branches of `contextsForAcquiredSources`
|
||||
with `contextsForResolvedSources`: consume resolved facts and source records,
|
||||
construct the existing rooted host/native passive view, and compute each
|
||||
source's relative base using the appropriate path rules.
|
||||
3. Share relative-base/containment calculation where applicable; retain each
|
||||
acquired source's own path and source-ID association. Do not substitute the
|
||||
first source record for the selected main file or attach all values to main.
|
||||
4. Migrate normal input acquisition, underlay contexts, and ordinary context
|
||||
backfill. Resolve only still-missing facts at the same lifecycle point as
|
||||
before. Retained descriptions/views never capture `filesystemAccess` or
|
||||
`*os.Root`; the existing passive `ResourceContext` contract stays unchanged.
|
||||
5. Retain failure evidence, root-only absent host context, discovered native
|
||||
outcomes, and deferred search-base behavior. Update focused context tests
|
||||
only where private function signatures changed.
|
||||
|
||||
**Finish:** ordinary/underlay acquisition and passive views use the common facts;
|
||||
selected-overlay callers are the remaining migration in Stage 9.
|
||||
|
||||
Progress notes (2026-10-25): input acquisition now retains resolved filesystem
|
||||
facts and builds passive contexts from those facts for ordinary and underlay
|
||||
sources. Migrated the ordinary backfill and reordered-source context test. The
|
||||
selected-overlay adapters remain for Stage 9. Focused context/acquisition tests,
|
||||
`GOTOOLCHAIN=local GOPROXY=off go test ./...`, and `git diff --check` passed.
|
||||
|
||||
## Stage 9 — Migrate Selected Overlays Without Merging Their Semantics (Complete)
|
||||
|
||||
**Owners:** engine `selected_overlay.go`, ordinary `applyInputLayers`, and
|
||||
`assembleLayeredInput`.
|
||||
|
||||
1. Define a private `selectedOverlayRequest` containing the overlay name,
|
||||
resolved filesystem description, protection rule, sequence policy, and
|
||||
existing limits/version state. Derive context at the caller's existing
|
||||
lifecycle point. Pass assembly and source evidence explicitly to the worker.
|
||||
2. Route ordinary overlays through the main source's context; route layered
|
||||
overlays through their independent `OverlayFilesystem`. Use named operation
|
||||
inputs instead of fabricating a `sourceBytes`/empty parsed-main pair merely
|
||||
to select a context branch. Preserve the actual absent-host root behavior.
|
||||
3. Return or retain the resolved description used by overlay acquisition and
|
||||
build overlay passive context from it. Reuse main facts only when that
|
||||
ordinary operation previously used the same root. Context facts do not share
|
||||
a live handle between the main import traversal and the later overlay read.
|
||||
4. Keep the production collection-aware overlay path. Migrate meaningful
|
||||
lower-level tests to that path with explicit policy/limits, retaining their
|
||||
budget, version, and failure assertions. Delete the test-only legacy branch
|
||||
selected by missing variadic policy and its exclusive adapters, rather than
|
||||
treating optional argument count as a contract. The baseline production entry
|
||||
points all supply the policy argument.
|
||||
5. Keep overlay source IDs, duplicate detection, version protection, directory
|
||||
validation, read/close errors, aggregate accounting, and post-read application
|
||||
order. Remove superseded context derivation and overlay-forwarding adapters.
|
||||
|
||||
**Finish:** all acquisition/context callers share facts and named inputs while
|
||||
ordinary and independent layered overlay semantics remain distinct. Run A when
|
||||
buildable; carry any genuine remaining migration dependencies to the next
|
||||
integration opportunity instead of adding scaffolding or stopping the sprint.
|
||||
|
||||
Progress notes (2026-10-25): introduced the named overlay request and migrated
|
||||
ordinary and independent layered overlay acquisition to resolved facts plus
|
||||
explicit access. Replaced the variadic compatibility branch with the existing
|
||||
collection-aware path, removed old context adapters, and retained overlay
|
||||
validation timing, source evidence, budgets, version protection, and close
|
||||
reporting. Validation group A, consumer examples, the full Go suite, and
|
||||
`git diff --check` passed.
|
||||
|
||||
## Stage 10 — Give Import Responsibilities Focused Files (Complete)
|
||||
|
||||
**Owners:** engine `imports.go` and new `import_acquisition.go`,
|
||||
`import_scope.go`, `import_projection.go`.
|
||||
|
||||
1. Put the request entry/access worker and traversal admission/orchestration in
|
||||
`import_acquisition.go`. Keep `imports.go` as the small common owner for
|
||||
limits, accounting/route/traversal data, and shared import diagnostics.
|
||||
2. Move `composeAcquiredInputWithPolicies`, `composeAcquiredScopeWithPolicy`,
|
||||
pending-item composition, placement reservation, and their item-scope
|
||||
assembly helpers into `import_scope.go`.
|
||||
3. Move nested-origin/policy/evidence projection, acquired source destinations,
|
||||
version-evidence projection, sequence-prefix helpers, and diagnostic-path
|
||||
projection into `import_projection.go`. Scheduling and graph identity remain
|
||||
with their existing graph/scheduler owners.
|
||||
4. Make the acquisition-to-closed-resources-to-assembly handoff visible. Keep
|
||||
evidence projection based on existing composition applications/position maps;
|
||||
file movement does not authorize another merge, full-tree replay, or scan of
|
||||
every unrelated policy for each record.
|
||||
|
||||
**Finish:** maintainers can locate acquisition, local strict assembly, and evidence
|
||||
work separately. Keep this stage structural; parse/admission extraction follows.
|
||||
|
||||
Progress notes (2026-10-25): moved request acquisition and traversal admission to
|
||||
`import_acquisition.go`, local strict composition and item-scope assembly to
|
||||
`import_scope.go`, and origin, destination, version-evidence, and diagnostic-path
|
||||
projection to `import_projection.go`. Shared limits, traversal data, and import
|
||||
diagnostics remain in `imports.go`. `./scripts/check.sh` passed.
|
||||
|
||||
## Stage 11 — Extract Import Declaration Parsing Operations (Complete)
|
||||
|
||||
**Owner:** engine `import_spec.go`.
|
||||
|
||||
1. Retain the outer mapping check, exactly-one-selector check, source-order field
|
||||
walk, applicability checks, and final selector parsing in
|
||||
`parseImportDeclarationSpec`.
|
||||
2. Extract boolean assignment, wildcard-set parsing/validation (shared by
|
||||
`pattern` and `exclude`), placement parsing, and selector-value parsing into
|
||||
ordinary private helpers. Reuse existing `parseStringSet`, authored-member
|
||||
sites, and destination/depth parsers.
|
||||
3. Preserve unspecified pointers and set flags, especially nil versus empty
|
||||
pattern lists. Keep error paths/locations tied to the authored selector or
|
||||
list member. Do not reorder consequential checks or replace the selector
|
||||
matrix with a configurable parser registry.
|
||||
|
||||
**Finish:** field decoding is readable without changing declaration syntax,
|
||||
selector rules, default resolution, or diagnostics. Keep behavior tests as the
|
||||
owner; no helper-by-helper assertion inventory is needed.
|
||||
|
||||
Progress notes (2026-10-25): extracted boolean assignment, shared wildcard set
|
||||
assignment, placement parsing, and selector-value parsing into private helpers.
|
||||
The declaration parser retains its validation ordering, authored member sites,
|
||||
and nil-versus-empty presence behavior. Import-focused tests and
|
||||
`./scripts/check.sh` passed.
|
||||
|
||||
## Stage 12 — Extract Directory Entry Admission And Descent (Complete)
|
||||
|
||||
**Owner:** engine `import_selection.go`, using existing directory/link helpers.
|
||||
|
||||
1. Keep root normalization/deduplication, root ordering, the explicit traversal
|
||||
stack, candidate union, sorting, exclusion, and required-empty decision visible
|
||||
in `selectDirectoryImportCandidates`.
|
||||
2. Extract coherent entry inspection and descent preparation. Return a small
|
||||
result identifying skip, admitted file, or directory frame to enter, plus a
|
||||
diagnostic when appropriate. Keep stack/active-directory mutation with the
|
||||
traversal so its lifetime is visible; do not invent a general visitor API.
|
||||
3. Preserve hidden/follow/depth behavior, distinct lexical candidates versus
|
||||
canonical access identity, resource-entry accounting, root-error handling,
|
||||
and active-path cycle detection. A later non-active alias remains eligible;
|
||||
exclusions still do not suppress discovery errors.
|
||||
4. Keep globs and first-match main-file discovery on their existing mechanisms.
|
||||
Share a helper only where its existing admission semantics actually coincide.
|
||||
|
||||
**Finish:** substantial entry/descent policy is separated from stack orchestration
|
||||
without changing the selection algorithm or adding repeated root walks.
|
||||
|
||||
Progress notes (2026-10-25): extracted entry inspection and file/directory
|
||||
admission into a result-returning helper. The selector retains root handling,
|
||||
stack and active-path mutation, candidate union, sorting, exclusions, and the
|
||||
required-empty decision. Directory-focused tests and `./scripts/check.sh` passed.
|
||||
|
||||
## Stage 13 — Extract Imported-Source Admission From Recursion (Complete)
|
||||
|
||||
**Owner:** engine `import_acquisition.go` after Stage 10.
|
||||
|
||||
1. Keep `importTraversal.visitPath` as the visible coordinator of containment,
|
||||
canonical identity, cycle/repetition checks, read/parse, directive/version
|
||||
processing, placement, and recursive declaration traversal.
|
||||
2. Extract source admission/read-and-process results into a private
|
||||
`admittedImport` carrying canonical resource/node identity, source record,
|
||||
processed payload/declarations, and processing evidence. Return an explicit
|
||||
optional-missing outcome; do not represent a read failure as absence.
|
||||
3. Preserve when source records are appended, rolled back for optional missing
|
||||
input, and retained for failures; preserve source IDs, accounting increments,
|
||||
version-callback ordering, and inclusion-edge creation on repetition/cycles.
|
||||
4. Pass the admitted record to mapping/item placement helpers rather than
|
||||
repeating its payload, declarations, and processing evidence as parallel
|
||||
arguments to `visitItemSource`.
|
||||
5. Factor route push/pop and item-scope save/restore only where it makes their
|
||||
shared lifetime clearer. Keep active graph state across item scopes and
|
||||
reserve placement nodes once. Item helpers complete before outer placement;
|
||||
they are strict local peers, not a precedence layer.
|
||||
|
||||
**Finish:** admission decisions and recursion have clear handoffs, with one
|
||||
read/process path and the original scheduling/evidence algorithms. Run A again
|
||||
because Stages 10–13 changed the import implementation after the first checkpoint.
|
||||
|
||||
Progress notes (2026-10-25): introduced an explicit admitted-import record and
|
||||
optional-missing outcome for source read/parse/version processing. `visitPath`
|
||||
retains containment, identity/cycle checks, graph updates, and recursive
|
||||
placement coordination; item placement receives the admitted record directly.
|
||||
Validation group A and `./scripts/check.sh` passed.
|
||||
|
||||
## Stage 14 — Extract Inheritance Child And Parent Preparation (Complete)
|
||||
|
||||
**Owners:** engine `inheritance_schedule.go`, new `inheritance_prepare.go`, and
|
||||
existing scope/policy helpers.
|
||||
|
||||
1. Extract preparation of the child's directive-free payload, direct origins,
|
||||
local policy templates, and optional control/control-origin evidence.
|
||||
2. Extract parent preparation: clone/project policies, lower them against the
|
||||
child's claims, exclude parent identity from the inherited payload, and
|
||||
prepare the separate parent history snapshot where currently required.
|
||||
Keep effective policy templates and transfer construction available to the
|
||||
combination result rather than reconstructing them afterward.
|
||||
3. Use focused prepared-child/prepared-parent records of these related values.
|
||||
Borrow authored/resolved parent inputs read-only; retain existing clone points
|
||||
for mutable payloads and history. Keep the current preliminary lower-bound
|
||||
budget decisions at the executor's existing point before materialization.
|
||||
4. Keep mapping and identified-sequence adapters, the stable Kahn scheduler, and
|
||||
the parentless/cancelled path semantics intact. Do not flatten nested directives
|
||||
before their destination-local evaluation.
|
||||
|
||||
**Finish:** the executor no longer embeds child/control and parent/policy
|
||||
preparation. Actual recursive combination/evidence moves in Stage 15.
|
||||
|
||||
Progress notes (2026-10-25): extracted child/control and parent/policy preparation
|
||||
into focused records in `inheritance_prepare.go`. The executor retains its
|
||||
preliminary budget checks, scheduler, and combination path, while history and
|
||||
transfer inputs stay available to combination. Engine inheritance coverage and
|
||||
facade inheritance tests passed.
|
||||
|
||||
## Stage 15 — Return One Inheritance Combination Result (Complete)
|
||||
|
||||
**Owners:** new engine `inheritance_combine.go`, `inheritance_collection.go`,
|
||||
and the shared executor.
|
||||
|
||||
1. Add `inheritanceTemplateResult` as described above and extract the existing
|
||||
parent/child combination into a helper receiving prepared inputs and the
|
||||
application policy. Parentless templates return the same result shape.
|
||||
2. Keep `mergeRecursiveMappings` and `combineSequence` as the combination
|
||||
mechanisms. Their callbacks update local origins/policy and local replacement
|
||||
evidence. Have `applyInheritanceSequenceOrigins` return its replacement slice
|
||||
instead of taking the whole enclosing scope result; append that slice to the
|
||||
local template result while retaining its updates to the supplied origin maps.
|
||||
3. Derive sequence history, transferred origins, retired/replaced policies,
|
||||
identity preservation, and the transfer operation from that same combination.
|
||||
Preserve `historyParent` and all policy/identity fields on the existing
|
||||
`inheritanceOperation`; do not rerun a merge or project values to recover them.
|
||||
4. Keep sequence validation, diagnostic canonicalization, location assignment,
|
||||
replacement order, and no-parent cloning behavior at their established
|
||||
semantic points. Keep child identity absent from inherited-parent history but
|
||||
present with its own origin in the effective child.
|
||||
|
||||
**Finish:** merge callbacks no longer mutate the executor accumulator. The
|
||||
resolved template and all evidence travel together for Stage 16 integration.
|
||||
|
||||
Progress notes (2026-10-25): added `inheritanceTemplateResult` and moved the
|
||||
parent/child merge and evidence construction into `combineInheritanceTemplate`.
|
||||
Callbacks now update the local origins, policies, and replacement evidence;
|
||||
sequence-origin handling returns replacement records to the template result.
|
||||
Parentless templates use the same result shape. Engine and facade inheritance
|
||||
tests passed.
|
||||
|
||||
## Stage 16 — Finish The Inheritance Executor And Accounting (Complete)
|
||||
|
||||
**Owner:** engine `inheritance_schedule.go` and focused finalization helpers.
|
||||
|
||||
1. Reduce the executor to preparation, budget precheck, combination, accumulation,
|
||||
and final rebuild. Append each template's control before its transfer/history
|
||||
in the same order as before, and update the indexed resolved value/origin/
|
||||
policy/template state once per template.
|
||||
2. Extract retained-history charging for one template's returned operations and
|
||||
replacements, reusing the same `retainedNodeCounter` across the scope as
|
||||
before. Preserve pointer-based shared-node accounting, lower-bound checks,
|
||||
scope-slot overhead, final expansion check, limits, and diagnostics.
|
||||
3. Extract final rebuilding and scope policy/origin projection where it clarifies
|
||||
the coordinator. Reuse `newInheritancePolicyIndex` once for scope localization;
|
||||
do not return to scanning all policy entries for every slot.
|
||||
4. Confirm both adapters and nested walking consume the same output. Retain
|
||||
source-version movement, sensitivity, independent snapshots, assembled order,
|
||||
cancellation, and policy retirement on actual replacement.
|
||||
|
||||
**Finish:** one readable executor coordinates explicit value/policy/evidence
|
||||
handoffs, with no parallel inheritance implementation. Run I.
|
||||
|
||||
Progress notes (2026-10-25): extracted per-template retained-history charging and
|
||||
scope rebuild/origin/policy projection into `inheritance_finalize.go`. The shared
|
||||
counter, budget checks, final expansion check, and one policy index remain intact.
|
||||
Both mapping and identified-sequence adapters still use the shared executor.
|
||||
Validation group I passed.
|
||||
|
||||
## Stage 17 — Extract Substantial Projection Handlers (Complete)
|
||||
|
||||
**Owners:** engine `projection.go`, optionally new `projection_collection.go`.
|
||||
|
||||
1. Keep `projectConfiguration`'s schema/type guard and one `valueOperation`.
|
||||
Keep `project`'s depth check, pointer/dynamic dispatch, and scalar conversion
|
||||
choices visible; delegate structs, maps, and sequences to named methods.
|
||||
2. Move struct member/inline flattening, collision checks, mapping-key charging,
|
||||
and flattened-field/inline-map secret classification into `projectStruct`.
|
||||
Preserve declaration order and the current decrement for a flattened
|
||||
container that is not retained in the output.
|
||||
3. Move deterministic sorted-map traversal into `projectMap`; keep its cycle
|
||||
entry active until all recursive children complete. Move slice/array
|
||||
construction into `projectSequence`, preserving nil-slice handling and the
|
||||
slice-only cycle guard.
|
||||
4. All recursive children call the same operation's dispatcher. Avoid duplicate
|
||||
node charging/depth increments or early cycle release introduced by extraction.
|
||||
Preserve float/text/duration/secret scalar handling and error paths.
|
||||
|
||||
**Finish:** dispatch and substantial handlers are separately readable, using the
|
||||
original operation state. Do not refactor `clone`, `decodeValue`, or
|
||||
`valueBuilder.build` as an additional cleanup project.
|
||||
|
||||
Progress notes (2026-10-25): delegated struct, map, and sequence projection to
|
||||
methods on the existing `valueOperation`. The dispatcher retains depth checks,
|
||||
pointer/dynamic handling, and scalar conversion; struct order, map sorting,
|
||||
flattened-node charging, nil slices, and cycle guards remain in their original
|
||||
paths. Validation group R passed.
|
||||
|
||||
## Stage 18 — Separate Schema Field Discovery And Assembly (Complete)
|
||||
|
||||
**Owner:** engine `schema.go`, optionally new `schema_fields.go`.
|
||||
|
||||
1. Keep `describeFields` as declaration-order enumeration, exported/ignored/tag
|
||||
decisions, and dispatch to ordinary or inline handling.
|
||||
2. Extract inline pointer-chain inspection and inline-struct/map assembly,
|
||||
retaining recursion/building-state checks and the existing codec-versus-
|
||||
structural distinction. Reuse the one builder/cache.
|
||||
3. Make ordinary registration and inline flattening visibly maintain both
|
||||
`members` and flattened `fields`/`fieldByName`/`inlineMap`. Preserve index
|
||||
paths, nested secret propagation, ambiguity paths, and the single-inline-map
|
||||
rule. Share registration helpers only for genuinely identical updates.
|
||||
4. Preserve descriptor reuse and the physical-member view used by typed cloning.
|
||||
Do not unify these views or add alternate field/tag policy. Existing schema,
|
||||
projection, decoding, and cloning expectations remain the behavior baseline.
|
||||
|
||||
Progress notes (2026-10-25): kept declaration-order enumeration, exported and
|
||||
ignored-field decisions, and tag parsing in `describeFields`; moved ordinary
|
||||
registration, inline pointer inspection, and inline struct/map assembly into
|
||||
focused helpers in `schema_fields.go`. Physical members remain distinct from
|
||||
flattened fields and inline-map metadata. Validation group R passed.
|
||||
|
||||
**Finish:** field discovery and descriptor assembly have explicit responsibilities
|
||||
without a new registration framework. Run R for both Stages 17–18.
|
||||
|
||||
## Stage 19 — Update Consumer And Internal Navigation (Complete)
|
||||
|
||||
**Owners:** `docs/consumers/README.md`, `docs/internal/overview.md`,
|
||||
`docs/internal/imports.md`, and affected links/test-owner entries in
|
||||
`docs/development.md`.
|
||||
|
||||
1. Add a concise consumer choice guide: ordinary `Load` for one selected source,
|
||||
explicit discovery as a source usable by either entry, and `LoadLayered` for
|
||||
underlay groups. Distinguish input-owned placement/import/filesystem choices
|
||||
from load-wide definition/default/version/override choices and independent
|
||||
layered overlay context. Link current API/config owners for exact rules.
|
||||
2. Link the existing simple README example, bundled-file/layered examples,
|
||||
import-focused prompt catalog, and discovered sequence catalog. Keep the
|
||||
README's simple path simple; do not duplicate complete examples or rewrite
|
||||
consumer domain rules to showcase new internal types.
|
||||
3. Update implementation ownership and flow for normalized requests, acquisition
|
||||
policy/state/tier lifetimes, resolved context versus live handles, import file
|
||||
owners, and explicit inheritance/projection/schema handoffs. Fix moved-symbol
|
||||
and behavioral-test navigation and stale comments affected by the refactor.
|
||||
4. Preserve public contract definitions and accepted ADR decisions. Keep deferred
|
||||
clone work in `future.md`. No policy change or new ADR is needed for the
|
||||
selected private refactor.
|
||||
|
||||
Progress notes (2026-10-25): added a consumer entry-point and ownership guide,
|
||||
linked the existing adoption examples, and documented normalized requests,
|
||||
acquisition/context lifetimes, import owners, and inheritance/projection/schema
|
||||
handoffs. Updated test navigation and retained clone review as deferred.
|
||||
Documentation links and repository boundaries passed.
|
||||
|
||||
**Finish:** current documentation describes implemented structure, with the
|
||||
roadmap/plan retaining future work and progress until release cleanup.
|
||||
|
||||
## Stage 20 — Verify And Close The Combined Sprint (Complete)
|
||||
|
||||
1. Search for obsolete import/input forwarding ladders, synthetic `Options`
|
||||
acquisition construction, `sourceVersionState.importDefaults`, discarded
|
||||
context derivation branches, unused inverse bridges, and temporary scaffolds.
|
||||
Remove leftovers along with exclusive test scaffolding. Preserve distinct
|
||||
operations and real diagnostic conversions; do not delete private code solely
|
||||
because it resembles another helper.
|
||||
2. Review the combined call flow: one normalization model, non-generic acquisition,
|
||||
load versus tier versus traversal state, library-owned acquisition handles
|
||||
closed before composition/publication, one combination outcome for inheritance
|
||||
evidence, and one state owner per reflective operation. Review for added whole-tree
|
||||
traversals, policy scans, or ownership copies without requiring measurements.
|
||||
3. Account for every selected finding using the table below. Confirm any new
|
||||
handoff coverage addresses a concrete gap. Existing correctness/algorithmic
|
||||
regressions, public-boundary tests, and meaningful assertions stay effective.
|
||||
4. Run the Final validation group, resolve failures, inspect the staged diff and
|
||||
workspace, and commit the completed remaining work with plain-English
|
||||
messages. Report changes, checks, limitations, and any unrelated work retained.
|
||||
Do not tag, push, publish, or update consumer repositories in this sprint.
|
||||
|
||||
Progress notes (2026-10-25): reviewed the combined request/acquisition/tier and
|
||||
traversal flow, the inheritance result handoff, and the shared reflection state
|
||||
owners. The named obsolete source-version field, synthetic acquisition-options
|
||||
construction, and inverse conversion symbols are absent; no additional cleanup
|
||||
was needed. The final development check and race-enabled full test suite passed.
|
||||
|
||||
| Audit finding | Completion evidence |
|
||||
| --- | --- |
|
||||
| 1: imports | Stages 3–6 and 10–13 remove options reconstruction/default coupling and forwarding ladders; acquisition/scope/evidence owners and admission operations are clear. A and final checks pass. |
|
||||
| 2: inheritance | Stages 14–16 return explicit template results, keep one executor, and preserve accounting and value/history/identity agreement. I and final checks pass. |
|
||||
| 3: projection | Stage 17 extracts substantial handlers sharing operation state. R and final checks pass. Clone review remains deferred, not implicitly completed. |
|
||||
| 4: schema | Stage 18 clarifies ordinary/inline assembly while preserving both descriptor views. R and final checks pass. |
|
||||
| 5: contexts | Stages 7–9 share resolved facts across input/import/overlay/passive contexts without retaining handles or changing validation timing. A and final checks pass. |
|
||||
| 6: facade | Stage 2 removes unused inverse conversions/adapters and shares real conversion. F and final boundary/consumer checks pass. |
|
||||
|
||||
**Finish:** the selected roadmap target is implemented, validated, documented,
|
||||
and reviewable as one complete sprint. No unresolved product/scope decisions are
|
||||
needed to begin these stages.
|
||||
@@ -1,77 +0,0 @@
|
||||
# Consolidation Review Findings
|
||||
|
||||
Reviewed revision: `557e006a4ab98b666735a71b3845418c702f7cec`.
|
||||
Comparison baseline: `8ab1568` (the committed implementation plan).
|
||||
|
||||
The review compared the [consolidation target](consolidation.md) and
|
||||
[implementation plan](implementation.md) with the completed sprint, including
|
||||
public/private conversions, preflight and acquisition, import selection and
|
||||
evidence, filesystem contexts, inheritance, projection, schema construction,
|
||||
changed tests, and documentation. The development check (`./scripts/check.sh`)
|
||||
and full race suite (`go test -race ./...`) passed with Go 1.26.10 on Linux.
|
||||
|
||||
## 1. Complete The Non-Import Resolved-Context Handoff
|
||||
|
||||
Status: open. Priority: medium. Category: implementation gap and maintainability.
|
||||
No user-facing correctness regression has been established for this finding.
|
||||
|
||||
**Target.** Roadmap internal target 2 and implementation Stages 7–9 select one
|
||||
resolved filesystem description shared by validation/acquisition and passive
|
||||
context construction. Stage 8 explicitly requires populating the description
|
||||
at the existing non-import validation point and resolving only facts that are
|
||||
still missing later.
|
||||
|
||||
**Evidence.** In [acquisition.go](../../internal/engine/acquisition.go),
|
||||
`acquireInput` calls `validateFilesystemContext` at line 181 for inputs without
|
||||
import traversal and with an applicable explicit context. That validator returns
|
||||
only a diagnostic. Its host/native branches derive a resolved description but
|
||||
discard it. At lines 220–226, acquisition sees `input.resolvedFilesystem == nil`
|
||||
and calls `resolveFilesystemContext` again before building passive views.
|
||||
|
||||
For example, an ordinary `Load` of a byte or mapping source with explicit host
|
||||
`FilesystemContext` and imports disabled resolves/canonicalizes the base and
|
||||
root during validation, then derives those same facts again for its source
|
||||
context. The retained description therefore does not complete the intended
|
||||
validation-to-context handoff.
|
||||
|
||||
Additionally, [filesystem.go](../../internal/engine/filesystem.go) still contains
|
||||
`validateAbsentHostFilesystemContext` (line 40), with its own root/base
|
||||
canonicalization, root-defaulting, and containment logic. That derivation
|
||||
duplicates `resolveAbsentFilesystemContext` in
|
||||
[filesystem_context.go](../../internal/engine/filesystem_context.go) (line 29).
|
||||
The two paths already maintain separate containment-diagnostic messages. Graph
|
||||
caller inspection confirms that the old validator is reachable from
|
||||
`acquireInput`; it is not unused migration scaffolding.
|
||||
|
||||
**Why address it.** The new model exists, but one important path still discards
|
||||
and reconstructs its results, while absent-host context derivation has two
|
||||
production implementations. This leaves part of audit finding 5 unresolved and
|
||||
preserves the drift risk the consolidation was intended to remove. This is a
|
||||
structural completion issue, not a claim of measured performance loss or a new
|
||||
single-lookup guarantee.
|
||||
|
||||
**Required revision.**
|
||||
|
||||
- Have non-import context resolution/validation return or populate the resolved
|
||||
description, and retain it on `assembledInput` at the existing validation
|
||||
point. Later passive-view construction and ordinary-overlay handling should
|
||||
reuse those applicable facts.
|
||||
- Route absent-host derivation through the common resolved-context owner and
|
||||
remove the duplicate derivation from the old validator. Keep the existing
|
||||
directory/access checks and close-error handling at their appropriate points.
|
||||
- Preserve deferred discovery/ordinary-overlay checks, root-only absent host
|
||||
context, host/native distinctions, diagnostics, and borrowed filesystem
|
||||
ownership. Independently required validation must not disappear merely to
|
||||
reduce filesystem calls.
|
||||
- Keep internal documentation aligned with the completed handoff. Describe
|
||||
aggregate accounting as load-wide and repetition/namespace state as
|
||||
tier-scoped; the new internal overview/import prose currently calls the latter
|
||||
per-tier accounting despite its distinct role.
|
||||
|
||||
**Verification.** Review the non-import call path to confirm that applicable
|
||||
resolved facts are carried forward and absent-host derivation has one owner.
|
||||
Reuse the existing host/native, discovery selected/absent, ordinary/layered
|
||||
overlay, and passive-resource-context tests. Add a focused behavioral regression
|
||||
only for a consequential gap exposed by the change; do not assert incidental
|
||||
canonicalization/stat call counts. Run the development check and applicable
|
||||
ownership race checks after remediation.
|
||||
@@ -178,9 +178,11 @@ func acquireInput(request inputAcquisitionRequest, policy acquisitionPolicy, sta
|
||||
input.document = versionProcessing.payload
|
||||
}
|
||||
if request.input.filesystem != nil && (selectedSource.kind == sourceNone || !activeOverlay(request.ordinaryOverlay)) {
|
||||
if diagnostic := validateFilesystemContext(selectedSource, request.input.filesystem, parsed); diagnostic != nil {
|
||||
resolved, diagnostic := validateFilesystemContext(selectedSource, request.input.filesystem, parsed, tier.hostToken)
|
||||
if diagnostic != nil {
|
||||
return input, []Diagnostic{*diagnostic}
|
||||
}
|
||||
input.resolvedFilesystem = &resolved
|
||||
}
|
||||
}
|
||||
if !request.input.imports && input.document != nil {
|
||||
|
||||
@@ -5,6 +5,29 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestImportSchedulingKeepsUnselectedSlotsAndScopeBoundaries(t *testing.T) {
|
||||
dependent := importNodeKey{resourcePath: "a-dependent"}
|
||||
external := importNodeKey{resourcePath: "external"}
|
||||
prerequisite := importNodeKey{resourcePath: "z-prerequisite"}
|
||||
graph := newImportInclusionGraph()
|
||||
for _, key := range []importNodeKey{dependent, external, prerequisite} {
|
||||
graph.addNode(importGraphNode{key: key})
|
||||
}
|
||||
graph.addEdge(dependent, prerequisite, Location{})
|
||||
graph.addEdge(dependent, external, Location{})
|
||||
graph.addEdge(external, prerequisite, Location{})
|
||||
contributions := []acquiredContribution{
|
||||
{identity: dependent}, {identity: external}, {identity: prerequisite},
|
||||
}
|
||||
scheduled := scheduleContributionSlots(contributions, []int{0, 2}, indexImportDependencies(graph))
|
||||
if scheduled[0].identity != prerequisite || scheduled[1].identity != external || scheduled[2].identity != dependent {
|
||||
t.Fatalf("scope schedule = %#v", scheduled)
|
||||
}
|
||||
if contributions[0].identity != dependent || contributions[2].identity != prerequisite {
|
||||
t.Fatal("scheduling changed the input slots")
|
||||
}
|
||||
}
|
||||
|
||||
func TestImportAndInheritanceIntegrationsUseEquivalentReadySchedules(t *testing.T) {
|
||||
names := []string{"a-base", "b-independent", "z-dependent"}
|
||||
keys := make(map[string]importNodeKey, len(names))
|
||||
@@ -33,7 +56,7 @@ func TestImportAndInheritanceIntegrationsUseEquivalentReadySchedules(t *testing.
|
||||
}
|
||||
graph.addEdge(keys["z-dependent"], keys["a-base"], Location{})
|
||||
|
||||
scheduled := scheduleContributionSlots(contributions, []int{0, 1, 2}, graph)
|
||||
scheduled := scheduleContributionSlots(contributions, []int{0, 1, 2}, indexImportDependencies(graph))
|
||||
got := make([]string, len(scheduled))
|
||||
for index, contribution := range scheduled {
|
||||
got[index] = contribution.identity.resourcePath[1:]
|
||||
|
||||
@@ -108,13 +108,14 @@ parent: true
|
||||
}
|
||||
defer func() { _ = access.close() }()
|
||||
state := importTraversal{
|
||||
access: access,
|
||||
policy: &acquisitionPolicy{importLimits: defaultImportLimits()},
|
||||
state: &acquisitionState{accounting: &importAccounting{}},
|
||||
tier: &tierAcquisitionState{visited: make(map[resourceIdentity]importRoute)},
|
||||
active: make(map[resourceIdentity]int),
|
||||
access: access,
|
||||
policy: &acquisitionPolicy{importLimits: defaultImportLimits()},
|
||||
state: &acquisitionState{accounting: &importAccounting{sources: 1}},
|
||||
tier: &tierAcquisitionState{visited: make(map[resourceIdentity]importRoute)},
|
||||
sources: sourceInfos(main.source),
|
||||
active: make(map[resourceIdentity]int),
|
||||
}
|
||||
if diagnostics := state.walkDeclarations(directory, declarations, 0, Keys("entry")); len(diagnostics) != 0 {
|
||||
if diagnostics := state.walkDeclarations(directory, declarations, 0, Keys("entry"), 0); len(diagnostics) != 0 {
|
||||
t.Fatalf("walk diagnostics = %#v", diagnostics)
|
||||
}
|
||||
|
||||
@@ -128,11 +129,11 @@ parent: true
|
||||
{path: alphaPath, destination: "/entry/collection"},
|
||||
{path: betaPath, destination: "/entry/collection"},
|
||||
}
|
||||
if len(state.sources) != len(wantSources) {
|
||||
t.Fatalf("import sources = %#v, want %d", state.sources, len(wantSources))
|
||||
if len(state.sources) != len(wantSources)+1 {
|
||||
t.Fatalf("sources = %#v, want %d including main", state.sources, len(wantSources)+1)
|
||||
}
|
||||
for index, want := range wantSources {
|
||||
got := state.sources[index]
|
||||
got := state.sources[index+1]
|
||||
if got.Path != canonicalTestPath(t, want.path) || got.Destination.String() != want.destination {
|
||||
t.Errorf("source %d = (%q, %q), want (%q, %q)", index, got.Path, got.Destination.String(), canonicalTestPath(t, want.path), want.destination)
|
||||
}
|
||||
|
||||
@@ -7,73 +7,33 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
func validateFilesystemContext(source Source, context *FilesystemContext, main parsedSource) *Diagnostic {
|
||||
if source.kind == sourceNone && context.FS == nil {
|
||||
return validateAbsentHostFilesystemContext(context)
|
||||
}
|
||||
if nativeContext(source, context) {
|
||||
resolved, diagnostic := resolveNativeFilesystemContext(source, context)
|
||||
if diagnostic != nil {
|
||||
return diagnostic
|
||||
}
|
||||
return validateResolvedFilesystemDirectories(resolved)
|
||||
}
|
||||
resolved, diagnostic := resolveHostFilesystemContext(source, context, main, nil)
|
||||
func validateFilesystemContext(source Source, context *FilesystemContext, main parsedSource, hostToken *namespaceToken) (resolvedFilesystemContext, *Diagnostic) {
|
||||
resolved, diagnostic := resolveFilesystemContext(source, context, main, hostToken)
|
||||
if diagnostic != nil {
|
||||
return diagnostic
|
||||
// Keep absent-input validation's existing diagnostic while sharing
|
||||
// root/base derivation with overlay and resource-context handling.
|
||||
if source.kind == sourceNone && context.FS == nil && diagnostic.Code == CodeImportOutsideRoot {
|
||||
diagnostic.Message = "import base must stay inside the configuration root"
|
||||
}
|
||||
return resolvedFilesystemContext{}, diagnostic
|
||||
}
|
||||
if diagnostic := validateResolvedFilesystemDirectories(resolved); diagnostic != nil {
|
||||
return diagnostic
|
||||
return resolvedFilesystemContext{}, diagnostic
|
||||
}
|
||||
if resolved.native != nil {
|
||||
return resolved, nil
|
||||
}
|
||||
root, err := os.OpenRoot(resolved.rootPath)
|
||||
if err != nil {
|
||||
value := globFilesystemDiagnostic(err, "configuration root could not be opened", Location{})
|
||||
value.Primary = nil
|
||||
return &value
|
||||
return resolvedFilesystemContext{}, &value
|
||||
}
|
||||
if err := root.Close(); err != nil {
|
||||
value := importDiagnostic(CodeImportRead, "configuration root could not be closed", nil)
|
||||
return &value
|
||||
return resolvedFilesystemContext{}, &value
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateAbsentHostFilesystemContext(context *FilesystemContext) *Diagnostic {
|
||||
rootPath, err := canonicalExistingPath(context.Root)
|
||||
if err != nil {
|
||||
value := importDiagnostic(CodeImportInvalid, "configuration root could not be canonicalized", nil)
|
||||
return &value
|
||||
}
|
||||
basePath := rootPath
|
||||
if context.BaseDir != "" {
|
||||
basePath, err = canonicalExistingPath(context.BaseDir)
|
||||
if err != nil {
|
||||
value := importDiagnostic(CodeImportInvalid, "import base directory could not be canonicalized", nil)
|
||||
return &value
|
||||
}
|
||||
}
|
||||
if !containedPath(rootPath, basePath) {
|
||||
value := importDiagnostic(CodeImportOutsideRoot, "import base must stay inside the configuration root", nil)
|
||||
return &value
|
||||
}
|
||||
for _, candidate := range []string{rootPath, basePath} {
|
||||
info, err := os.Stat(candidate)
|
||||
if err != nil || !info.IsDir() {
|
||||
value := importDiagnostic(CodeImportInvalid, "configuration root and import base must be directories", nil)
|
||||
return &value
|
||||
}
|
||||
}
|
||||
root, err := os.OpenRoot(rootPath)
|
||||
if err != nil {
|
||||
value := globFilesystemDiagnostic(err, "configuration root could not be opened", Location{})
|
||||
value.Primary = nil
|
||||
return &value
|
||||
}
|
||||
if err := root.Close(); err != nil {
|
||||
value := importDiagnostic(CodeImportRead, "configuration root could not be closed", nil)
|
||||
return &value
|
||||
}
|
||||
return nil
|
||||
return resolved, nil
|
||||
}
|
||||
|
||||
func filesystemOptionDiagnostics(source Source, context *FilesystemContext) []Diagnostic {
|
||||
|
||||
@@ -22,6 +22,7 @@ type admittedImport struct {
|
||||
identity resourceIdentity
|
||||
nodeKey importNodeKey
|
||||
source SourceInfo
|
||||
sourceIndex int
|
||||
payload *document.Node
|
||||
declarations []importDeclarationSpec
|
||||
sequenceDeclarations []sequenceDeclarationSpec
|
||||
@@ -156,7 +157,7 @@ func acquireImportedWithAccess(request importAcquisitionRequest, policy *acquisi
|
||||
scopeOwner: mainNodeKey, payload: body, document: body, processing: processing,
|
||||
sequenceDeclarations: cloneSequenceDeclarations(sequenceDeclarations),
|
||||
})
|
||||
diagnostics = state.walkDeclarations(basePath, declarations, 0, Path{})
|
||||
diagnostics = state.walkDeclarations(basePath, declarations, 0, Path{}, 0)
|
||||
} else {
|
||||
_, placed, err := placeDocument(body, destination, body.Location, maxDocumentDepth, maxDocumentNodes)
|
||||
placementNodes := destinationWrapperNodeCount(destination)
|
||||
@@ -172,12 +173,12 @@ func acquireImportedWithAccess(request importAcquisitionRequest, policy *acquisi
|
||||
destination: destination.clone(), processing: processing,
|
||||
sequenceDeclarations: cloneSequenceDeclarations(sequenceDeclarations),
|
||||
})
|
||||
diagnostics = state.walkDeclarations(basePath, declarations, 0, destination)
|
||||
diagnostics = state.walkDeclarations(basePath, declarations, 0, destination, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(diagnostics) == 0 {
|
||||
state.scheduleAcquisitionScopes(&state.scope)
|
||||
scheduleAcquisitionScopes(&state.scope, indexImportDependencies(state.importGraph))
|
||||
}
|
||||
|
||||
closeErr := access.close()
|
||||
@@ -239,7 +240,7 @@ func (s *importTraversal) checkInitialLimits() *Diagnostic {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *importTraversal) walkDeclarations(basePath string, declarations []importDeclarationSpec, depth int, parentDestination Path) []Diagnostic {
|
||||
func (s *importTraversal) walkDeclarations(basePath string, declarations []importDeclarationSpec, depth int, parentDestination Path, sourceIndex int) []Diagnostic {
|
||||
policies := make([]resolvedImportPolicy, len(declarations))
|
||||
for index, spec := range declarations {
|
||||
policy, diagnostics := resolveImportPolicy(spec, s.policy.importDefaults)
|
||||
@@ -280,23 +281,19 @@ func (s *importTraversal) walkDeclarations(basePath string, declarations []impor
|
||||
}
|
||||
}
|
||||
if !policy.required && len(s.sources) == sourcesBefore {
|
||||
s.recordEmptyImportSelection(spec)
|
||||
s.recordEmptyImportSelection(sourceIndex, spec)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *importTraversal) recordEmptyImportSelection(spec importDeclarationSpec) {
|
||||
for index := range s.sources {
|
||||
if s.sources[index].ID != spec.location.SourceID {
|
||||
continue
|
||||
}
|
||||
s.sources[index].importSelections = append(s.sources[index].importSelections, importSelectionEvidence{
|
||||
path: spec.path.clone(), location: spec.location,
|
||||
selector: spec.selector, outcome: importSelectionNone,
|
||||
})
|
||||
return
|
||||
}
|
||||
func (s *importTraversal) recordEmptyImportSelection(sourceIndex int, spec importDeclarationSpec) {
|
||||
// The declaring source's position survives descendant appends and optional
|
||||
// missing-source rollback, unlike a pointer into the source slice.
|
||||
s.sources[sourceIndex].importSelections = append(s.sources[sourceIndex].importSelections, importSelectionEvidence{
|
||||
path: spec.path.clone(), location: spec.location,
|
||||
selector: spec.selector, outcome: importSelectionNone,
|
||||
})
|
||||
}
|
||||
|
||||
func (s *importTraversal) admitImportSource(canonical string, identity resourceIdentity, nodeKey importNodeKey, candidate selectedImportCandidate, policy resolvedImportPolicy, destination Path, location Location) (importAdmissionResult, []Diagnostic) {
|
||||
@@ -321,6 +318,7 @@ func (s *importTraversal) admitImportSource(canonical string, identity resourceI
|
||||
if kind == SourceKindFilesystem {
|
||||
info.namespace = s.access.token
|
||||
}
|
||||
sourceIndex := len(s.sources)
|
||||
s.sources = append(s.sources, info)
|
||||
data, skipped, diagnostic := s.readImport(canonical, sourceID, policy.required)
|
||||
if diagnostic != nil {
|
||||
@@ -369,7 +367,7 @@ func (s *importTraversal) admitImportSource(canonical string, identity resourceI
|
||||
}
|
||||
return importAdmissionResult{source: admittedImport{
|
||||
canonical: canonical, identity: identity, nodeKey: nodeKey,
|
||||
source: cloneSourceInfo(info), payload: processing.payload,
|
||||
source: cloneSourceInfo(info), sourceIndex: sourceIndex, payload: processing.payload,
|
||||
declarations: processing.imports,
|
||||
sequenceDeclarations: cloneSequenceDeclarations(processing.sequences),
|
||||
processing: &processing,
|
||||
@@ -477,7 +475,7 @@ func (s *importTraversal) visitPath(candidate selectedImportCandidate, spec impo
|
||||
s.active[admitted.identity] = len(s.stack)
|
||||
s.tier.visited[admitted.identity] = route
|
||||
s.stack = append(s.stack, route)
|
||||
diagnostics = s.walkDeclarations(s.access.dir(admitted.canonical), admitted.declarations, depth, destination)
|
||||
diagnostics = s.walkDeclarations(s.access.dir(admitted.canonical), admitted.declarations, depth, destination, admitted.sourceIndex)
|
||||
delete(s.active, admitted.identity)
|
||||
s.stack = s.stack[:len(s.stack)-1]
|
||||
return diagnostics
|
||||
|
||||
@@ -2,11 +2,54 @@ package engine
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBroadEmptyImportSelectionsKeepDeclaringSourceOwnership(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
directory := t.TempDir()
|
||||
mainPath := writeImportFile(t, directory, "main.yml", `configkit:
|
||||
imports:
|
||||
- glob: fragments/*.yml
|
||||
- path: []
|
||||
`)
|
||||
const fragments = 32
|
||||
for index := 0; index < fragments; index++ {
|
||||
writeImportFile(t, directory, fmt.Sprintf("fragments/fragment-%02d.yml", index), `configkit:
|
||||
imports:
|
||||
- path: missing.yml
|
||||
- path: []
|
||||
`)
|
||||
}
|
||||
loaded, err := testLoad[map[string]any](File(mainPath), Options[map[string]any]{Imports: true})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
sources := loaded.Sources()
|
||||
if len(sources) != fragments+1 {
|
||||
t.Fatalf("source count = %d, want %d", len(sources), fragments+1)
|
||||
}
|
||||
for index, source := range sources {
|
||||
wantPaths := []string{"/configkit/imports/0", "/configkit/imports/1"}
|
||||
if index == 0 {
|
||||
wantPaths = wantPaths[1:]
|
||||
}
|
||||
if len(source.importSelections) != len(wantPaths) {
|
||||
t.Fatalf("source %s selections = %#v", source.ID, source.importSelections)
|
||||
}
|
||||
for selectionIndex, selection := range source.importSelections {
|
||||
if selection.path.String() != wantPaths[selectionIndex] || selection.location.SourceID != source.ID ||
|
||||
selection.selector != importSelectorPath || selection.outcome != importSelectionNone {
|
||||
t.Errorf("source %s selection %d = %#v", source.ID, selectionIndex, selection)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestOptionalEmptyImportSelectionsRemainSourceLocalAndDetached(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
@@ -157,7 +157,20 @@ func (graph importInclusionGraph) scheduleCycle(closingEdge int, location Locati
|
||||
}, true
|
||||
}
|
||||
|
||||
func scheduleAcquisitionScope(scope acquisitionScope, graph importInclusionGraph) acquisitionScope {
|
||||
// importDependencyIndex is built once per scheduling operation and shared by
|
||||
// its scopes. Inclusion targets are grouped by importer, so local scheduling
|
||||
// need not scan or reserve storage for unrelated graph edges.
|
||||
type importDependencyIndex map[importNodeKey][]importNodeKey
|
||||
|
||||
func indexImportDependencies(graph importInclusionGraph) importDependencyIndex {
|
||||
index := make(importDependencyIndex)
|
||||
for _, edge := range graph.edges {
|
||||
index[edge.importer] = append(index[edge.importer], edge.imported)
|
||||
}
|
||||
return index
|
||||
}
|
||||
|
||||
func scheduleAcquisitionScope(scope acquisitionScope, dependencies importDependencyIndex) acquisitionScope {
|
||||
slots := make([]int, 0, len(scope.contributions))
|
||||
for index, contribution := range scope.contributions {
|
||||
if contribution.scopeOwner != scope.owner {
|
||||
@@ -166,7 +179,7 @@ func scheduleAcquisitionScope(scope acquisitionScope, graph importInclusionGraph
|
||||
slots = append(slots, index)
|
||||
}
|
||||
scheduled := scope
|
||||
scheduled.contributions = scheduleContributionSlots(scope.contributions, slots, graph)
|
||||
scheduled.contributions = scheduleContributionSlots(scope.contributions, slots, dependencies)
|
||||
return scheduled
|
||||
}
|
||||
|
||||
@@ -179,10 +192,10 @@ func scheduleAcquisitionGroup(scopes []acquisitionScope, graph importInclusionGr
|
||||
for index := range slots {
|
||||
slots[index] = index
|
||||
}
|
||||
return acquisitionScope{contributions: scheduleContributionSlots(contributions, slots, graph)}
|
||||
return acquisitionScope{contributions: scheduleContributionSlots(contributions, slots, indexImportDependencies(graph))}
|
||||
}
|
||||
|
||||
func scheduleContributionSlots(contributions []acquiredContribution, slots []int, graph importInclusionGraph) []acquiredContribution {
|
||||
func scheduleContributionSlots(contributions []acquiredContribution, slots []int, dependencies importDependencyIndex) []acquiredContribution {
|
||||
if len(slots) < 2 {
|
||||
return append([]acquiredContribution(nil), contributions...)
|
||||
}
|
||||
@@ -193,12 +206,12 @@ func scheduleContributionSlots(contributions []acquiredContribution, slots []int
|
||||
positions[contribution.identity] = position
|
||||
keys[position] = contribution.identity.dependencyKey()
|
||||
}
|
||||
edges := make([]dependencyEdge, 0, len(graph.edges))
|
||||
for _, edge := range graph.edges {
|
||||
importer, importerOK := positions[edge.importer]
|
||||
imported, importedOK := positions[edge.imported]
|
||||
if importerOK && importedOK {
|
||||
edges = append(edges, dependencyEdge{prerequisite: imported, dependent: importer})
|
||||
var edges []dependencyEdge
|
||||
for importerKey, importer := range positions {
|
||||
for _, importedKey := range dependencies[importerKey] {
|
||||
if imported, exists := positions[importedKey]; exists {
|
||||
edges = append(edges, dependencyEdge{prerequisite: imported, dependent: importer})
|
||||
}
|
||||
}
|
||||
}
|
||||
order, _ := scheduleDependencies(keys, edges)
|
||||
|
||||
@@ -184,7 +184,7 @@ func (s *importTraversal) visitItemSource(admitted admittedImport, destination,
|
||||
identity: admitted.nodeKey, role: importRoleItem, scopeOwner: admitted.nodeKey,
|
||||
processing: admitted.processing, sequenceDeclarations: cloneSequenceDeclarations(admitted.sequenceDeclarations),
|
||||
})
|
||||
diagnostics := s.walkDeclarations(s.access.dir(admitted.canonical), admitted.declarations, depth, Path{})
|
||||
diagnostics := s.walkDeclarations(s.access.dir(admitted.canonical), admitted.declarations, depth, Path{}, admitted.sourceIndex)
|
||||
itemScope := s.scope
|
||||
s.scope, s.itemScope, s.itemPrefix, s.itemFallback = parentScope, parentItemScope, parentPrefix, parentFallback
|
||||
if len(diagnostics) != 0 {
|
||||
|
||||
@@ -53,11 +53,11 @@ type importTraversal struct {
|
||||
importGraph importInclusionGraph
|
||||
}
|
||||
|
||||
func (s *importTraversal) scheduleAcquisitionScopes(scope *acquisitionScope) {
|
||||
*scope = scheduleAcquisitionScope(*scope, s.importGraph)
|
||||
func scheduleAcquisitionScopes(scope *acquisitionScope, dependencies importDependencyIndex) {
|
||||
*scope = scheduleAcquisitionScope(*scope, dependencies)
|
||||
for index := range scope.contributions {
|
||||
if pending := scope.contributions[index].pendingItem; pending != nil {
|
||||
s.scheduleAcquisitionScopes(pending)
|
||||
scheduleAcquisitionScopes(pending, dependencies)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1107,6 +1107,70 @@ id: item
|
||||
})
|
||||
}
|
||||
|
||||
func TestBroadItemScopesKeepHelperOriginsAndDestinations(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
type item struct {
|
||||
ID string `yaml:"id"`
|
||||
Value string `yaml:"value"`
|
||||
}
|
||||
type config struct {
|
||||
Items []item `yaml:"items"`
|
||||
}
|
||||
directory := t.TempDir()
|
||||
const count = 32
|
||||
var main strings.Builder
|
||||
main.WriteString("configkit:\n imports:\n")
|
||||
for index := count - 1; index >= 0; index-- {
|
||||
name := strconv.Itoa(100 + index)
|
||||
main.WriteString(" - path: item-" + name + ".yml\n destination: [items]\n placement: sequence-item\n")
|
||||
writeImportFile(t, directory, "item-"+name+".yml", "configkit: {imports: [{path: helper-"+name+".yml}]}\nid: item-"+name+"\n")
|
||||
writeImportFile(t, directory, "helper-"+name+".yml", "value: helper-"+name+"\n")
|
||||
}
|
||||
mainPath := writeImportFile(t, directory, "main.yml", main.String())
|
||||
definition := Definition[config]{sequenceIdentityRegistrations: []sequenceIdentityRegistration{{
|
||||
selector: sequenceIdentitySelector{literalSequenceSelector("items")}, identityFields: []string{"id"},
|
||||
}}}
|
||||
for _, layered := range []bool{false, true} {
|
||||
t.Run(strconv.FormatBool(layered), func(t *testing.T) {
|
||||
var loaded *Loaded[config]
|
||||
var err error
|
||||
if layered {
|
||||
loaded, err = LoadLayered(Input{Source: NoSource()}, testSecretBinding(), LayeredOptions[config]{
|
||||
Definition: definition,
|
||||
Underlays: []Underlay{{Inputs: []Input{{Source: File(mainPath), Imports: true}}}},
|
||||
})
|
||||
} else {
|
||||
loaded, err = Load(File(mainPath), testSecretBinding(), Options[config]{Imports: true, Definition: definition})
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
result := testLoaded(loaded)
|
||||
sources := result.Sources()
|
||||
if len(result.Config.Items) != count || len(sources) != 2*count+1 {
|
||||
t.Fatalf("item/source counts = %d/%d", len(result.Config.Items), len(sources))
|
||||
}
|
||||
metadata := result.Metadata()
|
||||
for index, record := range result.Config.Items {
|
||||
name := strconv.Itoa(100 + index)
|
||||
if record.ID != "item-"+name || record.Value != "helper-"+name {
|
||||
t.Fatalf("item %d = %#v", index, record)
|
||||
}
|
||||
// Acquisition remains authored (descending); final item order is
|
||||
// dependency-ready (ascending), with helper evidence reprojected.
|
||||
helper := sources[2*(count-index)]
|
||||
field, found := metadata.Lookup(Keys("items").Index(index).Key("value"))
|
||||
if !found || len(field.Origin.Locations) != 1 || field.Origin.Locations[0].SourceID != helper.ID ||
|
||||
helper.Path != canonicalTestPath(t, filepath.Join(directory, "helper-"+name+".yml")) ||
|
||||
helper.Destination.String() != Keys("items").Index(index).String() {
|
||||
t.Fatalf("item %d helper evidence = %#v, %#v", index, field, helper)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestItemHelperScopeScheduleInterleavesUnequalBranches(t *testing.T) {
|
||||
directory := t.TempDir()
|
||||
mainPath := writeImportFile(t, directory, "main.yml", `configkit:
|
||||
|
||||
@@ -43,7 +43,6 @@ func resolveOrdinaryOverlayFilesystem(source Source, context *FilesystemContext,
|
||||
value := optionDiagnostic(CodeOptionsInvalid, "NoSource overlays require explicit filesystem context")
|
||||
return resolvedFilesystemContext{}, &value
|
||||
}
|
||||
return resolveAbsentFilesystemContext(context)
|
||||
}
|
||||
if existing != nil {
|
||||
return *existing, nil
|
||||
|
||||
Reference in New Issue
Block a user