25 KiB
Pre-v1 Public API Cleanup Implementation Plan
Status: in progress; Stages 1–10 are complete and Stage 11 is pending.
Baseline: 07e4ae5d9236935389e81b207d4a8f00543fafce.
Audience: a Luna coding agent executing the stages sequentially.
Scope And Execution
Implement the accepted changes in the API roadmap. That document owns
the target, rationale, and behavioral boundaries. The revision-pinned
catalog and additional review preserve evidence and
alternatives; they are not additional work lists. The preceding completed
interface-remediation plan is preserved at
07e4ae5:docs/roadmap/implementation.md. References in older audit records to
that completed plan refer to its historical version, not this sprint.
This is one end-to-end sprint. Intermediate stages need not compile or leave a coherent repository. Do not create compatibility aliases, temporary public adapters, or intermediate releases to make individual stages green. Run focused checks when buildable and record deferred checks until they can actually run. Commit focused nonempty units under the agent policy, recording actual completion and validation below. A stage is not complete merely because its commands passed against the unchanged baseline.
Read AGENTS.md, the development guide, and applicable
architecture, testing, and
documentation policies. Use graph-first discovery
and check current references before editing. Preserve unrelated work.
The sprint changes Go names, removes selected public state, adds two passive queries, and changes exactly the two selected public strings. It does not change loading/composition, YAML syntax, version policy, callback auditing, history ownership, sensitivity, source acquisition, or secrets-bootstrap semantics. Do not add performance promises, restrictions, dependencies, release gates, or tests for incidental mechanics. Public path comparison (A2) remains deferred. Do not select a release version, write version-specific release notes, publish, or update external consumers in this sprint.
Coverage Map
| Accepted item | Implementation owner |
|---|---|
D1: EntryInfo; D2: Sensitive |
Stage 1 |
| D3: version policy/evidence vocabulary | Stage 2 |
| D4: empty-selection records; D5: destination fallback name | Stage 3 |
| N6: descriptive Go import defaults | Stage 4 |
N1: SearchPaths; D7: LoadSecretsIntoEnvironment |
Stage 5 |
| D6/N4: recording methods and invalid-kind diagnostic removal | Stage 6 |
| N2: main-tier provenance name and string | Stage 7 |
| N3: history-limit name/string; D8: unproduced diagnostic removal | Stage 8 |
| A1/N5: successful and failed source lookup | Stage 9 |
| Current guidance, migration information, integrated completion | Stages 10–11 |
Private Implementation Choices
- Keep the public facade root-owned, with explicit forwarding and conversion.
For facade-only renames, retain existing engine names unless steps below
require a change. Root
Sensitivecan forward to engineSecret, and rootEntryInfocan call engineInputInfo. Engine source-version records, import-default fields, discovery/bootstrap functions, and destination-fallback helpers need no cosmetic renames. - Remove empty-selection outcome state through its small private transport path. Keep candidate selection and admission decisions distinct from empty-selection evidence.
- Replace root and engine kind-selecting
Resolution.Recordentry points with fixed-intent methods. Keep commonaddRecordand assignment helpers; remove the unreachable invalid-kind category after tracing remaining routes. - Use one engine-local slice lookup helper for new result/error queries. Scan
the retained inventory and clone only a matching source with existing
cloneSourceInfo; do not cloneSources()in full to search it. Keep the callback's existing indexed entry-state lookup unchanged. This is a private implementation choice, not a public complexity guarantee. - Rename main-layer and history-limit constants in both root and engine, so producers, conversion, and rendering use the selected strings directly.
Validation Conventions
Commands run from the repository root using an installed compatible Go toolchain and already acquired dependencies. For Go checks use:
export GOTOOLCHAIN=local
export GOPROXY=off
export GOTELEMETRY=off
Keep disposable inventories outside the repository. Reuse behavioral tests for mechanical renames. Do not add negative-compilation fixtures or a permanent exported-symbol inventory test simply to prove old names disappeared; Stage 11 performs a one-time review. Focused commands name existing test families and the proposed new lookup/recording cases. Include differently named new tests explicitly. A no-test match is not validation.
Update affected declaration comments and maintained Go callers, including examples, with each change. Stage 10 owns the integrated prose/migration sweep; it is not a reason to leave obvious neighboring comments wrong.
Stage 1 — Clarify Resolution Entry Metadata And Sensitivity
Status: complete. Implements D1 and D2.
- In lifecycle.go, rename root
Resolution.InputInfotoEntryInfoandDefinition.SecrettoSensitive, without old aliases. UpdatetoEngineDefinitionto adapt the new predicate into engineSecret. Both loading entry points already share this conversion; retain that route. - Keep
EntryInfo's existing(FieldInfo, bool)forwarding and silent unknown/invalid/shape-mismatch behavior. Its fixed entry view includes retained typed defaults and CLI assignments. Do not substitute final metadata or changeIsExplicit's separate schema-path validation behavior. - Migrate root tests and maintained consumer calls. Update inactive-context
cases in
facade_context_test.go. Leave engineInputInfo, private sensitivity names, theconfigkit:"secret"tag, and typedSecretreferences unchanged. Do not broadly replace every identifierSecret. - Reuse
facade_resolution_presence_test.go,facade_candidate_test.go,facade_context_test.go, and existing secret/provenance owners. Preserve ordinary/container classification and its union with tags/dependencies.
Focused validation:
go test . -run 'Resolution|InactiveContexts|FacadeLoad|Secret|Inspection|Diff' -count=1
go test ./internal/engine -run 'Resolution|Provenance|Secret|Sensitivity' -count=1
Completion: selected root names are in use; entry queries, lifecycle guards, and classification behavior are unchanged.
Stage 2 — Rename Version Policy And Evidence
Status: complete. Implements D3.
- Replace root
SourceVersionModeDefaultwithSourceVersionModeMainOnlyin the same zero slot in lifecycle.go. Preserve numeric values and behavior ofIfPresent/Requiredand the numeric engine bridge used by ordinary/layered loads. - In records.go, rename
SourceVersionChecktoSourceVersionEvidenceandSourceInfo.VersionChecktoVersionEvidence. Update conversion.go; name its root-returning helperfromEngineSourceVersionEvidence, still accepting engineSourceVersionCheck. Preserve fields, nil handling, detached paths/locations, and sanitized text. - Migrate Go callers/tests/examples and version comments together.
MainOnlychecks the effective path only with a configured rule; broader modes use source-local paths and require a usable rule. Evidence means observation, not acceptance or checker invocation. Add neither acceptance flags nor rules. - Reuse
records_test.goandfacade_source_version_mode_test.go: zero-mode compatibility, missing-main checker invocation, absent non-main skipping,NoSource, explicit null, consumption, mounted/item coordinates, redaction, failure evidence, and detachment. Rename test names directly naming the replaced declaration; do not duplicate their case matrix.
Focused validation:
go test . -run 'SourceVersion|VersionCheckConversion|VersionEvidenceConversion|Facade.*Version' -count=1
go test ./internal/engine -run 'SourceVersion' -count=1
Completion: all three replacements cross every source-record exposure correctly, with existing version behavior intact.
Stage 3 — Simplify Empty-Selection And Destination Evidence
Status: complete. Implements D4 and D5.
- Replace root
ImportSelectionwithEmptyImportSelectionandSourceInfo.ImportSelectionswithEmptyImportSelections. RetainPath,Location, andSelector; removeOutcome,ImportSelectionOutcome, andImportSelectionNone. KeepImportSelectorand its three constants. - Update root conversion/tests. In engine
records.go, removeOutcomefromImportSelectionForFacade, the private evidenceoutcomefield, andimportSelectionOutcomeString. Remove its initializer inimport_acquisition.goand the now-unused selection-outcome enum inimport_selection.goafter checking references. Its unusedimportSelectionHasCandidatesmember does not justify creating evidence. Preserve actual admission outcomes such asimportAdmissionOptionalMissing, candidate handling, and accounting. Existing private transport/helper names may stay unchanged. - Rename root
SourceInfo.DestinationUnresolvedtoDestinationIsFallback. Conversion still reads existing private fallback evidence. Keep destination coordinates and placement modes; do not derive exact item coordinates where existing evidence does not contain them. - Migrate root/internal assertions and maintained Go callers. Preserve empty selection scope/order, declaration-local paths, and copies on results, callbacks, and errors. Remove assertions of the retired outcome only, keeping real selection assertions. Reuse nested-item fallback/partial-failure cases; do not invent new placement states.
Focused validation:
go test . -run 'ImportSelection|EmptyImport|Destination|SourceInfo|Fallback|RelativeImport' -count=1
go test ./internal/engine -run 'ImportSelection|ImportEvidence|Import|Destination|Item' -count=1
Completion: source records use the reduced record/new fields; selection, placement, partial evidence, and detachment retain established behavior.
Stage 4 — Make Go Import Defaults Self-Describing
Status: complete. Implements N6.
- Replace root
ImportDefaults.FollowwithFollowSymlinksandHiddenwithIncludeHidden, preserving*booland the other three fields. Update comments andtoEngineImportDefaultsto map to existing engineFollow/Hidden. - Migrate root option literals, examples, and tests by type/context. Do not
rename YAML
follow/hidden, declaration parser fields, or unrelated hidden-file controls. Do not snapshot earlier in the facade: existing engine preflight owns the once-per-load snapshot before acquisition/callbacks. - Reuse
facade_import_defaults_test.gofor nil, explicit false/true, transitive/underlay application, declaration overrides, pattern selection, and callback-mutation isolation. Preserve traversal/search scope and nil versus non-nil emptyPattern.
Focused validation:
go test . -run 'ImportDefaults|ImportSelection|EmptyImport' -count=1
go test ./internal/engine -run 'ImportDefaults|Import.*(Hidden|Follow|Symlink|Pattern)' -count=1
Completion: descriptive Go fields feed unchanged YAML policy and snapshots in ordinary/layered loading.
Stage 5 — Clarify Discovery And Environment Bootstrap Names
Status: complete. Implements N1 and D7.
- In root discovery.go, rename
PathstoSearchPaths. Forward the same literal candidates to existing engine machinery. Migrate callers without touching semanticPath,Keys, orSearches. Retain order, deferred acquisition, and selected-file terminal failures. - In root secrets.go, rename
LoadSecretsDirtoLoadSecretsIntoEnvironment, preserving arguments, return values, error conversion, and engine bootstrap call. Comments should plainly describe explicit process-environment mutation, already-set/empty preservation, reports, and existing partial-application behavior. - Migrate maintained calls, affected executable example identifiers, and nearby
documentation. Do not couple bootstrap to
Load, rename report/error types, or add filesystem/transactional guarantees. Process-mutating tests remain serial and restore state. Private engine function names may remain unchanged.
Focused validation:
go test . -run 'Search|Discovery|SecretsBootstrap|LoadSecrets|Example_weatherStartup' -count=1
go test ./internal/engine -run 'Search|Discovery|LoadSecretsDir' -count=1
go test ./examples/consumers -run 'Discovery|WorkingDirectoryPreset|SequenceCatalog|Weather' -count=1
Completion: old root functions are replaced outright; acquisition and safe bootstrap behavior remain unchanged.
Stage 6 — Replace Kind-Selecting Recording With Intent Methods
Status: complete. Implements D6 and N4.
-
Replace root and engine
Resolution.Recordwith:RecordDerivedDefault(path Path, reason string, dependencies ...Path) RecordNormalization(path Path, reason string, dependencies ...Path)Root methods forward through existing live-context guards/path conversion. Engine methods call
activeState().addRecordwith their fixedChangeDerivedDefaultorChangeNormalizationand no helper expected value. Share existing validation/coalescing/auditing; neither method assigns values or tests absence. -
Migrate manual callers according to their existing supported kind in root tests, engine tests, examples, and current snippets. Leave
DefaultandNormalizeas assignment-and-recording helpers using existingaddRecordroutes. RetainChangeKindand all inspection constants. -
Check every remaining
addRecordcaller supplies a supported fixed kind. Then remove the invalid-kind guard andCodeResolveInvalidKindfrom both root/engine. Remove only the obsolete arbitrary-kind case inresolution_test.go; retain invalid path, target, reason, dependency, projected-value, and unrecorded-change cases. If a legitimate supported public trigger remains, report it before changing its behavior. -
Update nil/zero/closed-context coverage for both methods. Add/adapt a small root case,
TestFacadeResolutionRecordingIntents, proving each method's resulting origin/history kind after application assignment. Include explicit input changed and recorded as a derived default, so this cannot accidentally become the absence-checking helper. Reuse container coverage, overlapping records, repeated/equal intent, dependencies, and net-change tests elsewhere.
Focused validation:
go test . -run 'Resolution|InactiveContexts|FacadeLoad|Historical|History' -count=1
go test ./internal/engine -run 'Resolution|Provenance|Callback|History' -count=1
Completion: root exposes only the selected manual recording methods; supported
input cannot produce resolve.invalid_kind. Other auditing/lifetime behavior
is unchanged.
Stage 7 — Use Main Consistently In Provenance
Status: complete. Implements N2.
- Replace
LayerBase = "base"withLayerMain = "main"in root/engine records. Migrate producer references across acquisition, composition, decoding, inheritance origins, overrides, discovery, and history/rendering. Keep existing tier assignments. - Review literal
"base"provenance comparisons/rendered expectations, not just Go references. Update only the main-tier category. Filesystem base directories, domain values, fixture labels, and unrelated words are unchanged. Retain other layer spellings and source IDs/order. - Update existing assertions in root, engine, and examples. Add a focused root
records assertion for
string(LayerMain) == "main"; reuse a public provenance/rendering fixture to verify main/imported-main attribution reaches consumers with that label. Imports retain their owning tier; other categories, precedence, and acquisition order are unchanged.
Focused validation:
go test . -run 'Facade|Historical|History|Layer|PublicValues' -count=1
go test ./internal/engine -run 'Compose|Overlay|Decode|Provenance|History|Explain|Grouped|Discovery' -count=1
Completion: main-tier evidence/rendering consistently uses the selected constant and spelling without attribution/precedence changes.
Stage 8 — Rename The History Diagnostic And Remove The Unused Code
Status: complete. Implements N3 and D8.
- Replace root/engine
CodeExplainLimitExceededwithCodeHistoryLimitExceeded = "history.limit_exceeded". Update every producer, includingexecuteLoadRequestin engineload.goandinheritanceHistoryBudgetDiagnosticininheritance_walk.go, plus readers/literal assertions. Other explanation errors are unchanged. - Reuse
history_budget_test.goand inheritance-budget cases to preserve exact retained-node accounting and failure before effective secret lookup/application validation. Change expected code, not quota, fixtures, accounting, or timing. Add the new literal to focused root constant-value assertions, not a general API snapshot test. - Reconfirm
CodeOptionsUnavailable/options.unavailablehas no producer or documented supported/reserved role. Baseline references are the two declarations and planning/review evidence. Remove both declarations. If a concrete supported role is discovered, bring it back for discussion rather than remove behavior or create a trigger. Other diagnostic codes stay intact.
Focused validation:
go test ./internal/engine -run 'HistoryBudget|RetainedNode|InheritanceExpansionEnforcesRetainedHistoryBudget' -count=1
go test . -run 'PublicValues|LoadReturnsFacadeLoadError|Diagnostics|Historical' -count=1
Completion: history failures carry the new code under unchanged conditions; D8's role check is resolved and its unproduced declaration is removed.
Stage 9 — Add Detached Source Lookup On Results And Load Errors
Status: complete. Implements A1 and N5.
-
Add engine-local
lookupSourceInfo(sources []SourceInfo, id string) (SourceInfo, bool)beside source cloning helpers. Return zero/false for empty/unknown IDs; otherwise scan the retained slice and clone the match. Add nil-safe engineInspection.SourceandLoadError.Sourceusing it. Do not change inventories, query-time evidence, or the callback's source map. -
Add the selected root methods in lifecycle.go and diagnostic.go:
func (r *Result[T]) Source(id string) (SourceInfo, bool) func (e *LoadError) Source(id string) (SourceInfo, bool)Return zero/false for nil or zero receivers/absent delegated state. Convert a found engine record through the Stage 2–3
fromEngineSourceInfopath. Keep enumeration, passiveSourceContext, and callbackSourceintact. -
Document passive lookup, load-local IDs, detachment, and partial failed-load evidence. Read
LoadError's already-sanitized retained sources, not raw acquisition/version state. Do not complete placement, reinterpret absence as acceptance, open resources, or perform credential lookup. -
Add focused external-package tests
TestResultSourceLookupandTestLoadErrorSourceLookup, or extend equivalent facade owners. Compare found records to enumeration; check empty/unknown IDs and nil/zero receivers. Mutate nested returned evidence and verify repeated lookup/enumeration are unchanged. Reuse import/version fixtures forImportedFrom,VersionEvidence.Location, and empty-selection slices. Cover representative layered sources and failure evidence with redaction/partial placement. Reuse counting-resource fixtures where useful to demonstrate no query I/O; do not add a full load-mode matrix, timing assertions, or concurrency promises.
Focused validation:
go test . -run 'SourceLookup|SourceVersionEvidence|ImportSelection|EmptyImport|SourceContext|Fallback' -count=1
go test ./internal/engine -run 'SourceVersion|ImportSelection|Resource|ResolutionInput' -count=1
Completion: both lookups agree with enumeration, return detached records, and preserve success/failure evidence without new load activity.
Stage 10 — Complete Guidance And Consumer Migration Information
Status: complete. Consumer guidance, current contracts, declaration comments, and maintained examples use the selected API; historical report references identify their completed plan revision.
- Review declaration comments and config.md's owning sections: discovery, import defaults/evidence, source versions, layered metadata, sensitivity, bootstrap, resolution, and history limits. Update relevant names and strings while preserving semantics. Keep detailed Go contracts beside declarations rather than reproducing them throughout prose.
- Migrate README, consumer guidance, root executable
examples, and maintained
examples/code/fixtures. Use new source lookup where an existing example enumerates solely to find an ID; retain enumeration where that is the task. Use new intent methods for manual recording while retaining assignment helpers where they do the work. - Add a concise Pre-v1 public API cleanup subsection to the consumer guide's
existing migration section. Include every old-to-new name from the roadmap,
removed empty-selection outcome field/type/constant and diagnostic constants,
both
Recordcall rewrites, new lookups, and"base"→"main"/"explain.limit_exceeded"→"history.limit_exceeded". Explain unchanged YAMLfollow/hiddenandconfigkit:"secret"; literal comparisons and serialized expectations may need migration as well as Go code. Do not claim external migrations or select a version for this breaking pre-v1 work. - Update internal docs only for changed descriptions. Do not rewrite accepted ADR decisions, pinned reviews, or historical release notes. If retained completed reports link to this overwritten plan, make their old-plan reference revision-specific rather than implying it concerns this sprint. Preserve unrelated proposals/status.
Validation:
go test ./examples/consumers -count=1
go test . -run '^Example|TestLocalDocumentationLinks' -count=1
git diff --check
Validation passed with the commands above.
Completion: current guidance/examples use the selected API, migration information is complete, and historical evidence remains accurate.
Stage 11 — Verify The Integrated Surface And Finish The Sprint
Status: pending.
-
Compare actual root exports/comments against the roadmap's complete accepted table using
go doc -all .and, if needed, a disposable AST/type inventory outside the checkout. Check fields/method sets/constants as well as top-level names. Confirm both recording and lookup signatures and no removed root names/compatibility aliases. -
Search active Go callers/current documentation for old names and the two old strings. Review matches by context: retained private engine names, domain text, migration instructions, pinned reviews, and historical records are valid. Do not globally rewrite
base,Secret,Paths,Follow,Hidden, orRecord, or enforce a blanket zero-match rule. Recheck D6/N4 and D8 reference evidence against the final tree. -
Run deferred focused tests and existing boundary/link checks:
go test . -run 'TestPublicFacadeBoundary|TestDependencyImportBoundary|TestLocalDocumentationLinks' -count=1 ./scripts/check.shThe everyday check already runs the full default suite, vet, and build, including examples. No additional full-suite pass or benchmarks are needed solely for this plan. If implementation changed mutable ownership/concurrency beyond forwarding/existing clone paths, follow the development guide with
go test -race ./...; record the reason/result. This introduces no permanent release gate and requires no CI waiting. -
Inspect the final diff for unselected semantics, ownership changes, dependencies, artifacts, and strengthened guarantees. Record actual stage completion/validation here. Mark the API roadmap implemented only after its selected surface and behavior preservation are verified. Keep completed planning/review records until normal release preparation. Commit completed nonempty units with plain-English messages; report remaining work accurately.
Completion: all accepted items are implemented, diagnostic role/reachability checks are resolved, applicable checks pass, and current docs/examples agree. No unselected public or semantic changes remain. This is implementation completion, not release publication or external consumer verification.
Decision Status
No open design question is required to execute this plan. D8's role check and D6/N4's reachability check are verification tasks with an explicit discussion path if unexpected supported use is found. Private helper naming may be adjusted for cohesion without changing the selected surface or behavior.