Files
ConfigKit/docs/roadmap/implementation.md

541 lines
33 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Implementation Plan: Sequence Placement and Composition
Status: ready for implementation; no stages completed.
## Purpose and authority
Implement the accepted [feature roadmap](sequence-composition.md), starting from
`2051177` (`Settle sequence placement and composition roadmap decisions`). This
plan owns execution order and progress; the roadmap owns intent, scope, and the
ten settled behavior decisions. The target implementer is a Luna coding agent.
Complete the numbered stages in order. Each stage is a separate focused prompt
and validated commit, not a request to implement the whole feature at once.
Read [development](../development.md), [agents](../policy/agents.md),
[architecture](../policy/architecture.md), [testing](../policy/testing.md), and
[documentation](../policy/documentation.md) before starting. Use the code graph
for discovery, then inspect the current owning implementation and tests. Preserve
unrelated work. Follow the existing commit policy after each nonempty completed
unit; record completion and validation in this plan.
This planning change implements nothing. Later stages introduce private engine
capabilities before exposing them through the root facade. Until a capability
lands, current consumer documentation must continue to describe implemented
behavior only. Accepted ADRs may describe the decision before implementation.
When a stage makes behavior reachable by consumers, update its owning declaration
comments and configuration contract in that same unit, to the implemented
boundary. Stage 13 consolidates guidance and examples; it does not postpone those
contract updates.
## Fixed design for implementation
### Public API and YAML spelling
Use these root-owned declarations and fields. Their zero values preserve current
behavior; do not change the signatures of `Load` or `LoadLayered`.
```go
type PlacementMode uint8
const (
PlacementModeMapping PlacementMode = iota
PlacementModeSequenceItem
)
type SequenceComposition struct {
Path Path
IdentityField string
}
```
- Add `SequenceCompositions []SequenceComposition` to `Definition[T]`.
- Add `PlacementMode PlacementMode` to `Input` and `SourceInfo`.
- Add both `Destination Path` and `PlacementMode PlacementMode` to `Options[T]`.
Ordinary `Load` needs the same per-source controls as the layered entry point;
its overlay and CLI coordinates remain application-rooted.
- YAML import declarations accept `placement: mapping` or
`placement: sequence-item`. Omission means `mapping`, independently for every
declaration. Retain `destination: [literal, keys]`, `path`, `glob`, and
`optional` with their existing meanings. Do not add aliases or inferred modes.
- Root fields and types stay root-owned. Use explicit conversions into engine
records and a private engine mode, following the source-version-mode pattern.
Preserve unknown numeric modes until engine preflight rejects them.
Validate options before acquisition. Unknown placement modes, item placement at
an empty destination, invalid destinations/registration paths, and conflicting
registrations use `CodeOptionsInvalid`. Registration paths use existing literal
mapping-key destination normalization, including its current reserved-key rules;
do not add more reserved names. Parsed numeric tokens become literal keys;
explicit indexes and invalid paths fail. Identical registrations coalesce, unused
registrations are permitted, and an empty/root registration is harmless and
unused under the mapping-root model. Do not reject ancestor/descendant pairs.
`IdentityField` is one literal string, including an empty string, not a path.
No field-name or identity-value normalization is introduced.
Use `CodeImportInvalid` for malformed import placement syntax, including an item
import whose effective destination is empty in its current mapping scope. An
omitted item-import destination is valid when inherited placement already names
a non-root collection. At an item root, an omitted destination names the item
mapping itself and cannot turn that mapping into a sequence. A nested item import
needs a nonempty effective destination within that item scope; this can come from
its relative destination or an ordinary helper's existing placement prefix.
Use `CodeComposeInvalid` for malformed registered-sequence records/identities and
`CodeComposeConflict` for duplicate identities or ordinary strict conflicts.
Malformed-record paths identify the effective item or its identity field;
duplicate paths identify the sequence container, with the first and conflicting
physical locations in `Primary`/`Related`. Error messages must not interpolate
identity strings or record bodies. Preserve deterministic reporting without
inventing an all-errors guarantee or freezing incidental message wording.
### Composition, scopes, and evidence
1. Compile registrations once per load into an owned exact-path lookup. A strict
tier has a separate identity table for each encountered registered sequence.
Scan every actual sequence contribution, even one with no peer. Require map
items and a present string identity after source-header processing and helper
composition. Compare exact strings; accept empty strings. Non-sequences retain
existing behavior. Distinct records append in contribution order; repeated
identities conflict even when bodies are equal or disjoint.
2. Keep every precedence boundary atomic for sequences. Each underlay group and
the main tier starts new identity state. Selected overlays, CLI, defaults,
inheritance, and application resolution do not run this identity validator.
Registrations apply at effective literal mapping paths, never through record
indexes. They are composition policy, not final configuration schema.
3. Source acquisition remains depth-first and keeps current source IDs, physical
resource identity, cycle/repetition detection, and shared budgets. Model an
item source plus its ordinary helper imports as a local strict mapping scope.
Ordinary imports share that scope and append relative mapping destinations;
an explicit item import opens a child item scope. Complete each item scope
before wrapping/validating its record in the enclosing composition. Keep its
contribution at the declaring source's original traversal position.
4. Carry a small private placement projection with each contribution: its scope,
local destination, and any sequence range assigned by strict assembly. Use it
to translate source paths into effective paths without reordering sources or
mutating acquired trees. Authored sequence index `j` becomes `offset+j`;
an item source's local root becomes the assigned item index. Nested projections
compose. Do not locate records again by rescanning IDs during inspection.
5. Keep completed trees distinct from source/history applications. Extend private
history operations to distinguish a sequence contribution from recursive
mapping merge: the first contribution to a collection in a tier replaces the
previous tier/default value; later contributions append. An empty first list
clears the prior list; an empty later list adds no items. Store only incoming
fragments and offsets, not a cumulative list snapshot after every append.
For item scopes, retain the initial item body and helper contributions at their
projected item paths, so helpers retain their own history and origins.
6. Reuse that projection in origins, history replay/query relevance, historical
path enumeration, sensitivity, and version evidence. Public source records
describe containers, not a new permanent record-identity address. An item
source reports its sequence container; a mapping helper inside an item reports
its effective mapping destination, which can include an enclosing item index.
Output evidence may contain such indexes even though input destinations and
registrations accept only mapping-key segments.
Choose private helper names and file splits to fit existing responsibilities.
Do not introduce a provider framework, generic merge callbacks, a second loading
lifecycle, or a public identity-selector API to implement these mechanics.
### Efficiency and preserved limits
Use one string-keyed identity map per registered sequence per strict tier, with
ordered appends and existing indexed mapping lookup. Clone incoming records into
owned assembly storage without repeatedly cloning the accumulated sequence.
Registration lookup, validation, and sequence construction should be linear in
the visited data apart from existing path/depth costs. Complete each item scope
once; do not recursively recompose the entire ordinary-helper subtree at every
import edge. Keep retained history fragments immutable and independently owned
from the working tree. These are implementation choices, not a new public
complexity, allocation, or latency guarantee.
Preserve current per-source, aggregate, traversal, projection, and retained-history
limits and error families. Count acquired content once and synthetic mapping and
sequence wrappers where introduced; include the extra sequence level in depth
checks. Do not charge an already-accounted item/helper body again merely because
it moves to a completed scope. Keep library-owned handles closed before returning
on success or failure. Do not add new limits or change their existing values.
## Stage 1 — Record the decision and prepare private registration controls
Scope: one ADR and engine option normalization; no new public fields yet.
- Write `docs/adr/0014-sequence-placement-and-composition.md` as an Accepted ADR
using the established format. Explain explicit item placement and registered
duplicate-rejecting strict composition, unchanged precedence replacement, and
their rationale in ConfigKit terms. Discuss alternatives only in Alternatives
Considered; do not center a downstream application's semantics. Link the
roadmap for implementation status. Do not rewrite accepted earlier decisions.
- Add engine-owned registration records/state and normalization, reachable from
engine `Definition`. Validate/coalesce using the fixed design above; detach
registration slices and paths at the load boundary. Keep compiled policy
per-load, not mutable shared Definition state or a global cache.
- Wire preflight into both entry points without consuming a reader or opening a
source for invalid options. Thread compiled policy through the request rather
than rebuilding it or invoking application hooks for internal handoffs. Keep
the existing shared source-version checker lifecycle intact.
- Add focused normalization tests for duplicate/conflicting registrations, empty
identity-field names, numeric/escaped literal keys, invalid index paths,
unused/root registrations, and preserved zero-option behavior.
Validation: focused option tests and `./scripts/check.sh`. Completion: existing
loads remain unchanged, with only private registered-policy input newly accepted.
## Stage 2 — Implement registered strict sequence validation and assembly
Scope: `internal/engine/compose.go` and focused composition tests.
- Add a policy-aware internal composer while preserving an unregistered adapter
for existing callers/tests. Collect/check ordinary claims with current strict
semantics; only sequence/sequence claims at registered effective paths use the
new rule. Mapping/sequence, null/sequence, and scalar overlaps still conflict.
- Validate every registered sequence as it is encountered, not only overlapping
claims. Missing paths and non-sequences do not independently fail registration.
Validate the completed contribution, not the unfinished raw item source.
- Build identity tables once, reject duplicates within/across lists, and append
records into owned assembly storage. Preserve mapping traversal and list order,
explicit empty sequences, exact identity spelling, and existing error safety.
- Return the assigned source-to-effective sequence ranges alongside the composed
result for the following stages. Do not change overlay merging or diff logic.
- Test distinct/mixed lists, singleton malformed records, missing/null/non-string
fields, empty/whitespace-distinct identities, duplicate empty identities,
identical/disjoint-body duplicates, empty lists, missing/non-sequence values,
unused/ancestor registrations, and unchanged unregistered conflicts. Use a
compact table of meaningful categories, not a full cross-product.
Validation: composition tests and `./scripts/check.sh`. Completion: the private
composer returns the correct tree and range projection without aliasing inputs.
## Stage 3 — Project composition origins and claims onto appended indexes
Scope: composition evidence, not import acquisition.
- Use Stage 2's assigned ranges to project each sequence record and descendant
onto its actual effective index. Retain physical locations and contributor
ordering for mapping and sequence containers. Preserve field-specific origins;
do not attribute every descendant to the sequence's first source.
- Keep source-local claims useful for conflict reporting while effective origins
and presence describe assembled paths. Projection must handle already-authored
lists, mounted mapping prefixes, escaped/numeric keys, and more than one
registered collection without using textual pointer replacement.
- Add composition-origin tests in which a later list's local index zero becomes
an effective nonzero index. Mutate returned/copied evidence and verify input
and separately retained paths remain independent.
Validation: composition/provenance/path tests and `./scripts/check.sh`.
Completion: correct trees and effective origins share the same record ordering.
## Stage 4 — Preserve append history and whole-list tier replacement
Scope: private history operations, replay, and precedence handoff.
- Extend `historyApplication` and its cloning/query/replay helpers with explicit
sequence-contribution information from the fixed design. Keep ordinary merge
behavior as the default. Support semantic indexed prefixes for helper events;
current `mergeHistoryAtPath` supports mappings only and cannot be reused
unchanged for them.
- At every strict-tier boundary mark the first contribution at a sequence path
as replacement and subsequent contributions as append. This boundary cannot
be inferred from `LayerUnderlay` alone: different underlay groups share that
layer name. Keep this information when `assemblyAccumulator.applyOverlay`
imports a group's history applications.
- Append replay must affect the collection and newly added record paths, not
falsely attribute unchanged earlier records to a later source. Translate
historical queries to the incoming fragment's local indexes for location/value
lookup. Preserve source-level applications rather than replacing them with one
opaque completed-tier snapshot.
- Test before/after replay for append, explicit empty lists, independent lists,
two underlay tiers with the same identity, main replacement, and a later whole
list replacement/removal. Verify origin attribution and history agree with
Stage 3's composed tree.
Validation: history/explanation/overlay tests and `./scripts/check.sh`.
Completion: strict accumulation is distinguishable from precedence replacement.
## Stage 5 — Integrate projected paths with inspection and sensitivity
Scope: metadata, retained history traversal, and safe inspection.
- Adapt `finalizeMetadataPaths`, application-value traversal, explanation queries,
and secret classification to Stage 4's projected operations. Do not coerce
indexed application prefixes into mapping keys. Reuse a small common projected
traversal/path helper where these consumers need the same translation.
- Preserve independent effective and historical indexes, numeric key/index shape
distinctions, removed paths, detached query results, and current sensitivity
propagation. Classify original historical shapes before aggregating sensitivity
by pointer; do not repeat the previously fixed shape-collision defect.
- Ensure sensitive fields on appended records are classified at their assigned
indexes and stay safely handled after replacement. Apply the same projection
to source-version sensitivity evidence when that evidence is integrated in
Stage 10. Do not classify a fragment solely at its unshifted source index.
- Add focused tests for shifted-index metadata/history/rendering agreement and
a replaced sensitive record. Reuse existing numeric-shape, removed-descendant,
ownership, inheritance, and redaction cases where they already protect behavior.
Validation: metadata/provenance/history/explanation/secret tests,
`go test -race ./internal/engine`, and `./scripts/check.sh`.
Completion: the private registered composer can feed existing inspection safely.
## Stage 6 — Refactor acquisition into composition scopes without new behavior
Scope: `imports.go`, `load_input.go`, `load_request.go`, and their handoffs.
- Replace the assumption that traversal yields only a flat slice of already
placed documents with ordered contribution/scope records. At this stage all
scopes are ordinary mappings; preserve current placement and traversal exactly.
- Separate acquisition facts from completed composition and retained history.
Keep source-local payloads, physical locations, resource contexts, relative
destinations, processing results, and source order available to later stages.
Preserve input-specific filesystem contexts, tier-local repetition tracking,
active import stacks, and load-wide shared accounting.
- Stop reconstructing underlay contributions by treating every history
application's `value` as a complete root document. That current shortcut loses
projected paths and append semantics. Compose acquired contributions at the
group boundary while preserving their source/history evidence.
- Avoid recursive public `Load` calls, reopening files, a second parse pass,
per-helper defaults/hooks, or resetting import limits and active/visited state.
Preserve acquisition-first main behavior and all close/error paths.
- Prefer existing ordinary, relative-destination, underlay, namespace, repetition,
budget, and handle-ownership tests as the regression oracle. Add a test only for
a real handoff risk not already covered.
Validation: import/destination/grouped/source-version/resource-context tests and
`./scripts/check.sh`. Completion: this is a behavior-preserving private refactor.
## Stage 7 — Add the private item-placement operation and accounting
Scope: placement primitives and engine per-input options; not public exposure.
- Add the private placement mode to engine input/request/options/source records,
with preflight validation. Keep mapping placement unchanged. A valid item mode
needs a nonempty mapping-key destination relative to its current mapping scope.
- Extend placement to wrap one completed source mapping in a singleton sequence
at that destination. Preserve mapping-root input validation; do not accept raw
source sequence/scalar roots. A nil payload stays absent, and `{}` contributes
an actual empty item. Do not require a composition registration here.
- Account for the sequence wrapper plus existing mapping wrappers and full
mounted depth. Check limits before allocating oversized wrappers. Carry node
accounting through completed scopes without charging acquired content twice.
- Test mapping default equivalence, explicit item placement, nested literal
destinations, absent versus empty payloads, invalid/root placement, and exact
versus exceeded private depth/node allowances. Use test-controlled limits.
Validation: placement/accounting/option tests and `./scripts/check.sh`.
Completion: the private operation places a mapping once with correct ownership.
## Stage 8 — Assemble item scopes and YAML path/glob imports
Scope: directive parsing and scoped acquisition/composition.
- Parse the fixed `placement` syntax and report malformed values safely. Each
declaration's default is mapping mode; item mode is never inherited. Apply the
same mode independently to every glob match in existing sorted traversal order.
- An item source opens a local mapping scope. Its body and ordinary helpers
compose strictly within that mapping, with omitted/empty helper destinations
at the item root and other destinations relative to the immediate importer.
A helper can supply the identity; a duplicate identity field in the item still
fails ordinary mapping composition.
- Complete child item scopes before wrapping them into their enclosing scope.
Compose each scope once, preserving declaration/contribution order and source
history. Explicit nested item imports create nested sequences; registrations
do not descend through the enclosing record index, so repeated contributions
to those unregistered nested sequences conflict atomically.
- Pass completed records and their relative history/origins to the parent
composer. Assign outer ranges there, then project the retained item body and
helper events through those ranges. Do not emit each ordinary helper as a
separate record or validate its identity independently.
- Cover path and glob item imports, a helper-provided ID, relative and transitive
helper destinations, mixed authored lists/items, optional unmatched imports,
empty items, nested placement, default-mode independence, and physical paths
relative to the declaring helper. Exercise native filesystem and host imports
through existing shared-mechanism coverage rather than duplicating every case.
- If the new syntax is now reachable through ordinary public import loading,
document that implemented placement subset in `docs/config.md` in this unit;
do not advertise still-private registration controls yet.
Validation: import/directive/glob/destination/composition tests and
`./scripts/check.sh`. Completion: private item imports construct the agreed
records and preserve physical acquisition evidence.
## Stage 9 — Wire strict policy through the complete loading lifecycle
Scope: ordinary/grouped engine loading and every strict-tier entry point.
- Pass compiled registrations and placement mode through main acquisition and
each underlay group. Validate an authored registered list in an ordinary
one-source load without imports, overlays, or CLI; no composition shortcut may
bypass it. Skip only genuinely absent contributions.
- Assemble and validate each complete strict tier before precedence can replace
it. Reset identity maps between tiers and preserve current acquisition order.
A malformed lower-tier list must fail even when main or overlay would replace
it. Avoid running the checker a second time on the overlaid final tree.
- Keep unregistered item lists atomic. Selected overlays, CLI replacements,
defaults, callbacks, and sibling inheritance retain their current behavior.
Do not require the final list to retain unique or even present identities.
- Add compact engine lifecycle cases for main, an underlay group, separate tiers
sharing IDs, `[]` clearing, unregistered conflict, a hidden lower-tier error,
and duplicate IDs introduced by a replacement overlay/CLI/callback. Verify
normal application validation can reject such final values when configured.
Validation: load/grouped/overlay/override/resolution/default tests and
`./scripts/check.sh`. Completion: private engine policy is applied at strict
composition boundaries only, including the one-source case.
## Stage 10 — Preserve source-version and structural ownership under placement
Scope: version path projection and existing protected-path checks.
- Keep source-version checking on each original source before placement, exactly
once using the current local rule and callback lifecycle. Required/if-present
modes retain main absence and helper-document behavior. Accepted non-main
headers are consumed before helper/record identity validation; main's header
stays application content. Helpers do not inherit their parent's header.
- Default mode continues checking the configured effective path without prefix
reinterpretation. Enabled modes derive main's protected effective path from
its actual projection, including item indexes. Update both the request and
version state's effective rule before later protection checks need it.
- Audit `effectiveSourceVersionRule`, `fragmentVersionClaim`, import/underlay/
overlay checks, override checks, inheritance checks, and resolution auditing.
Mapping-only navigation or ancestor-sequence shortcuts cannot protect the
wrong record or reject an unrelated strict peer merely because it appends to
the same collection. Preserve protections of the actual main path and its
destructive ancestor replacements; do not add an exception to version policy.
- Retain source-local public VersionCheck paths, physical locations, full absent
paths, and conservative failure disclosure. Use assigned projections for
sensitivity checks of headers consumed before the final tree exists. Keep
original semantic key/index shapes and remap nested item/helper prefixes once.
- Keep import opt-in checks on original sources before consumed headers can hide
a directive. Identity lookup must not count consumed headers or structural
controls as application fields. Do not add a blacklist of identity-field names
or use later defaults/inheritance/resolution to supply an absent identity.
- Test main item placement in both entry points, default versus enabled version
coordinates, consumed helper/import headers, protected-main versus unrelated
peer paths, safe shifted evidence, optional absence below controls, and normal
inheritance inside item mappings. Reuse existing source-version and inheritance
regressions rather than rewriting their selected contracts.
Validation: source-version/version/inheritance/override/resolution tests,
`go test -race ./internal/engine`, and `./scripts/check.sh`.
Completion: item placement changes coordinates, not version or structural policy.
## Stage 11 — Expose root-owned controls and detached placement evidence
Scope: root facade declarations, conversions, and public integration tests.
- Add the exact public types/fields specified above, with declaration comments
describing defaults, option failures, and ownership. Convert Definition
registrations and paths, ordinary Options destinations/modes, layered Inputs,
and successful/failed SourceInfo records in both directions as applicable.
Update callback-entry source conversions too, not only `Result.Sources`.
- Preserve immutable source snapshots, reader single-use behavior, callback
lifetime, independent loads, and no public exposure of engine/parser types.
SourceInfo reports its own selected mode; CLI/overlay sources use mapping mode
unless an actual supported item placement was applied.
- For a completed load, resolve nested source containers through their owning
item projection. On an early failure retain acquired evidence without inventing
an index that was never assigned: keep an unresolved nested source's Destination
at its nearest resolved enclosing collection container, rather than flattening
away an index and reporting a fictitious effective mapping path. Once assigned,
use the projected destination. Failed-load records describe acquisition context,
not a usable partial configuration. Do not add a public index or identity-address
API or promise resolution of every failed source's final placement.
- Public tests demonstrate ordinary and layered opt-in, an item-import glob,
authored-list/item interoperability, one-source validation, helper origins and
resource contexts at shifted indexes, both option-error entry points, detached
source/registration paths, and unchanged zero-value callers. Keep mechanism
edge cases in engine tests.
- Update `docs/config.md` for the now-public registration controls, ordinary
destination options, complete placement/evidence behavior, and strict-versus-
replacement validation boundary. Update the architecture policy's composition
summary to recognize the registered-sequence rule and link ADR 0014. Stage 13
reviews and completes the surrounding guidance, not these canonical contracts.
Validation: focused facade tests,
`go test . -run 'TestPublicFacadeBoundary|TestDependencyImportBoundary'`,
`go test -race ./...`, and `./scripts/check.sh`.
Completion: both public entry points expose the complete feature with the same
composition rules and independently owned evidence.
## Stage 12 — Close cross-cutting regression and limit gaps
Scope: risks that require the assembled feature, not duplicate mechanism tests.
- Review Stage 1–11 coverage against the roadmap's ten settled decisions. Add
only missing consequential cases. Use a representative completed load to check
typed output, explicit zero/false/null/empty versus omission, effective origins,
callback-entry metadata, resource contexts, history, explanation, and rendering
agree after an append followed by a replacement.
- Verify item/helper source canaries do not leak through duplicate diagnostics,
errors, explanations, effective rendering, or consumed version evidence under
existing sensitivity rules. Preserve historical redaction after index shifts
and later removal/replacement, without imposing identity-based final masking
rules that the existing model does not have.
- Exercise shared aggregate accounting across item scopes/globs/underlays and
retained-history accounting of the new operations. Confirm immutable fragments
are counted according to existing shared-node rules and no retained cumulative
sequence snapshots multiply history storage. Use existing close/reader seams
for one representative nested-item failure, not a second filesystem framework.
- Confirm effective Diff still reports atomic sequence changes and unchanged
defaults/resolution/inheritance semantics. Review the implementation for repeated
ID scans, growing-list clones, and helper-subtree recomposition. No timing or
allocation threshold assertions, benchmark campaign, or new release gate is
required; add local performance evidence only if a concrete concern needs it.
Validation: targeted new regressions, `go test -race ./...`, and
`./scripts/check.sh`. Completion: no known evidence, ownership, or accounting gap
remains hidden behind a correct final tree.
## Stage 13 — Document implemented contracts and provide a maintained example
Scope: consumer documentation and a tested in-repository adoption fixture.
- Update `docs/config.md` as the canonical owner for import syntax, placement,
registered strict composition, validation timing, replacement boundaries,
structural/version processing, source containers, and history semantics. Link
declaration comments for API details rather than duplicating them everywhere.
- Update `docs/policy/architecture.md`'s composition summary so its unconditional
non-mapping-conflict description recognizes the explicitly registered sequence
rule. Preserve consistency, ownership, and precedence principles; this feature
does not authorize arbitrary configurable merge strategies. Link the new ADR
and current contract. Preserve upstream provenance and locally significant
policy decisions.
- Update `README.md`, `docs/consumers/README.md`, `docs/internal/overview.md`, and
`docs/development.md` only to summarize/navigate the implemented feature at
their respective ownership boundaries. Update `future.md` to distinguish the
implemented subset from still-potential broader identified-record features;
do not promote their deferred semantics into this feature's contract.
- Add a maintained fixture/test under `examples/consumers` showing independently
authored prompt-shaped records, explicit item path/glob imports, reusable
registration, and a lower-priority default collection replaced by the main
list. Show that a helper can supply an ID and that final domain validation is
consumer-owned. Use in-repository disposable data, not a live PromptKit
dependency or a second application-side merge.
- Keep ADR 0014 decision text immutable after acceptance; update status/navigation
to implemented when appropriate. Do not rewrite earlier ADR decision text to
make it appear they anticipated the extension.
Validation: executable example/consumer tests, local documentation-link tests,
whitespace review, and `./scripts/check.sh`.
Completion: current docs and tested public examples describe what actually lands.
## Stage 14 — Review the completed unit and hand off
- Read the complete implementation diff against `2051177` and the feature
roadmap. Check all ten settled decisions and the two independent opt-ins.
Look especially for validation accidentally extended into replacement-only
layers, final uniqueness/schema promises, new field restrictions, wrong
source indexes, and stale acquisition/history shortcuts.
- Run `go test -race ./...` and `./scripts/check.sh` on the completed candidate.
Reuse unchanged successful checks where appropriate; document actual results
and any unresolved issue rather than claiming an unrun consumer verification.
- Record stage completion and a concise final validation summary in this plan,
then commit the completed nonempty unit under the normal commit policy.
Do not create an empty completion commit or delete unfinished plans.
- Report the new public controls, preserved precedence/ownership behavior,
validation, and any remaining work. This sprint does not select/tag/push a
release, alter release gates, update Go/dependencies, or update downstream
repositories. Separately authorized release preparation can assess the public
field additions and feature under the existing release policy.
Completion: the repository is clean, the feature and its evidence agree, and no
known in-scope implementation work remains. No product decisions remain open;
private helper names and equivalent mechanics are ordinary implementation work.