41 KiB
Implement ConfigKit adoption and configuration comparison
Status: implementation in progress; Stages 1–10 are complete.
Scope and authority
Follow the stages in order, starting with the first stage without completion evidence. Each stage is sized for one Luna implementation assignment. The feature roadmap owns the selected end state, operator behavior, target API, and scope limits. This document owns the steps to reach that state. Read development guidance and the applicable policies before implementation. Accepted ADRs and the architecture policy control if a conflict is found.
The implementation baseline remains f8bb789; this plan was reviewed against
7fc425e, which changed planning documents only. ConfigKit comparison decisions
are accepted in ADR 0008 and
ADR 0012. ADR 0008 delegates
comparison semantics to the adopted upstream version; it does not freeze those
semantics as separate CommandKit guarantees. Report details belong in the feature
roadmap until implemented, then in the CLI reference.
This is one sprint against an unreleased prototype. Preserve deliberately selected contracts, but give existing implementation shapes no compatibility preference. Intermediate commits need not build. Do not introduce wrappers or extra machinery to make intermediate states usable. The completed sprint must compile and provide the complete selected behavior. Core needs no new comparison model or dependency; reuse its existing typed bindings, captured declarations, and outcomes.
During implementation, add focused coverage with the stage that owns its behavior, format changed Go files, and record completion evidence here after each stage. Commit completed nonempty units under the agent policy. Evidence must describe actual checks and any limitations, not anticipated success. These development checks do not create new release gates. This planning pass runs no Go tests and changes no implementation, manifests, examples, or ADRs.
Settled architecture and stage boundaries
The public pair callback belongs to ckconfig and returns ConfigKit's native records. Core's binding-composition API is sufficient; add no core comparison types, new backend interface, view facet, provider registry, or universal plugin model. Ordinary acquisition and paired comparison remain separate callbacks. Shared state is created once by invocation execution, not by the pair adapter. The report is assembled with private YAML nodes and delivered through the existing outcome/write helpers. Application examples own their caches.
The stages below separate declarations, selection, input preparation, typed loading, YAML tree placement, report encoding, execution, consumers, and current documentation. Each stage changes its named owner and adds its focused evidence; do not move later-stage work forward merely to make an intermediate commit build. Run stage checks when their dependencies compile, and record deferred execution honestly. Stage 13 runs the completed suite. Private helper names and equivalent local refactoring remain implementation choices, not unsettled architecture or new public guarantees.
Stage 1: Adopt ConfigKit and establish local validation
Outcome: both integrations select ConfigKit v0.7.1, with a usable development graph and an honest distinction between local and published dependency evidence.
- Read both integration manifests and the pinned ConfigKit release/comparison contracts linked from the feature roadmap. Update the two direct ConfigKit requirements from v0.5.0 to v0.7.1. Keep PromptKit v0.12.0 and consumer Go minimums at 1.26.0. Use the selected Go 1.26.8 development/release toolchain.
- Preserve an existing ignored
go.work; ensure it includes the root, ckconfig, and ckprompt modules. Do not commit workspace files or add filesystem replacements to distributed manifests. Use released upstream dependencies, not sibling source replacements. - Resolve actual upstream dependencies and sums. ConfigKit's tool requirement selects ReleaseKit v0.4.1 in integration graphs; retain whatever legitimate indirect requirements normal resolution needs. Do not change core's declared ReleaseKit v0.4.0 tool, launcher, or profiles as an incidental upgrade.
- For unpublished CommandKit requirements, use the disposable module-file procedure in Stage 13. Generate real external dependency checksums; never invent sums for unpublished CommandKit versions. Keep normal manifests at their intended versioned requirements. Reconcile module-local sums from real dependency downloads, excluding disposable local wiring.
- Run existing ckconfig and ckprompt suites against the upgraded dependency. Fix actual upstream API incompatibilities within this scope, if any. Do not compensate for a test that independently freezes a superseded upstream detail.
Completion evidence — complete: both integration modules now require
ConfigKit v0.7.1, with Go 1.26.0 and PromptKit v0.12.0 unchanged. The pinned
release notes and comparison contract were reviewed at source commit
6fffde4af8f31f99d3ee295c426eb3234cb0fb77; they confirm the YAML dependency
and runtime/API remain unchanged and the upstream tool moves to ReleaseKit
v0.4.1. Normal module files contain no filesystem replacements. Real checksum
entries for released dependencies were generated through temporary paired
module files with local replacements only for unpublished CommandKit modules;
no CommandKit version checksums were added. Both integration suites pass with
the local workspace and with workspace-off temporary manifests. Each integration
graph selects ReleaseKit v0.4.1. The root module still declares ReleaseKit
v0.4.0, and no upstream API incompatibility was found by the suites. These
workspace-off checks validate local source wiring, not availability of the
unpublished CommandKit versions.
Stage 2: Add the comparison capability and captured provider contract
Primary files: integrations/ckconfig/contracts.go, family.go.
Outcome: scopes can independently declare comparison, without requiring ordinary acquisition for a comparison-only scope.
- Add
Diff Operation = "diff". Extend normalization, generated-operation inventory, and selector-union assembly to recognize it. Append it after the four existing operations so their relative order stays unchanged. - Add
ComparisonSide[I],ComparisonRequest[I], andScope[I, S].Compareexactly as selected under the roadmap's provider contract. Keep these exports integration-owned; keepRequest,View, and ordinaryAcquirefocused on existing operations. Add GoDoc for sides, diagnostics, errors, and lifetimes. The callback receives context, one request withLeftandRight, thenS. Keep the existingSuite.CheckInput(string, Selection, I)signature. - Change capture-time wiring checks: declared
DiffrequiresCompare; declared validate/show/sources/explain operations requireAcquire. A scope declaring both kinds needs both. Metadata-only scopes need neither. Preserve retained adapter issues even when capability lists are narrowed. - Capture the comparison callback reference with the other scope metadata. Keep existing collection copies, original binding identities, root-only declaration placement, batch validation, and namespace ownership. Factories and providers must remain uninvoked during capture and installation.
- Extend scope discovery and conditional family generation. Generate
diffonly when a scope supports it; selectors are accepted by that command only when applicable to a comparison-capable scope. Rely on existing core binding validation for collisions with generated flags.
Completion evidence — complete: Diff, ComparisonSide[I],
ComparisonRequest[I], and the optional Scope.Compare callback are declared in
ckconfig. Capability validation now requires Acquire only for ordinary
operations and Compare only for Diff; retained adapter issues still reach
installation after capability narrowing. Operation normalization and discovery
preserve the four existing operation order and append diff, generating the
family command only when a scope declares comparison. Focused checks cover
comparison-only, mixed, unsupported, and metadata-only scopes; missing provider
wiring; a narrowed ConfigKit adapter retaining its nil-loader issue; and captured
capability metadata and provider references surviving edits to the source
declaration. Construction, installation, discovery, and unsupported-scope
preflight leave factories and providers uninvoked. The ckconfig and dependent
ckp suites pass. Input flags and successful comparison execution remain for
their later owners.
Stage 3: Add comparison flags, inheritance, and availability preflight
Primary file: integrations/ckconfig/input.go.
Outcome: comparison has its selected CLI vocabulary and two resolved selections without prematurely constructing application inputs or shared state.
- Add right-side selection storage and expose
--against-configand--against-overlayonly ondiff. Keep--configand--overlayas the left selection. Default the comparison format to YAML; accept explicit YAML and reject unsupported formats through the existing usage-error path. - Resolve right fields independently: if the right
Supplied.Setis false, copy the corresponding complete leftSuppliedvalue; otherwise keep the right value, including an explicit empty string. Do not interpret an empty overlay, normalize provider defaults, or require unequal selections. - Reuse existing scope/capability preflight. Comparison defaults to
app, selects exactly one scope, and rejectsall, unknown scopes, and scopes without comparison before suite input/state factories or provider calls. - Add an
against Selectionfield to the private command input. Use one small private helper to compute right-side inheritance; it performs no loading or selector parsing. Add thediffname/summary inoperationDeclarationhere.--pathremains explain-only; against flags remain diff-only. Keep ordinary format defaults and singleton flag behavior unchanged.
Completion evidence — complete: right-side configuration and overlay fields
inherit independently with their supplied state, while explicit empty overrides
remain supplied. The diff command defaults to YAML, accepts explicit YAML, and
rejects text and unsupported formats. Its help exposes both right-side flags;
validate, show, sources, and explain do not, and --path remains explain-only.
Comparison scope preflight defaults to app and rejects all, unknown, and
unsupported scopes before suite factories run. Preflight resolves right-side
fields before pair preparation. The existing show default and other operation
defaults remain unchanged. Focused tests and the full ckconfig and dependent ckp
suites passed at this point in the sequence. Separate typed pair preparation is
completed in Stage 4; acquisition and delivery remain in Stage 8.
Stage 4: Prepare separate typed inputs for the selected pair
Primary file: integrations/ckconfig/input.go.
Outcome: the private operation plan stores both checked sides for one scope; shared state remains uninitialized until execution.
- Add a private
preparedComparison[I, S]with two fields: the selected*preparedScope[I, S]and itsComparisonRequest[I]. Add a pointer to it inconfigOperationPlan[I, S]. Ordinary operations keep their scope-input list; comparison uses the dedicated prepared pair, without duplicating its left input into that ordinary list. Set the comparison pointer only after both sides pass preparation. Ordinary plans leave it nil. - Capture the operation's original selector handles through
CaptureFlagsonce. Prepare left input and then right input withNewInputwhen reached. Apply only the selected scope's bindings independently throughFlagValues.ApplyTo. CallCheckInputwith the scope name, that side's selection, and its input. Store both checked sides in the operation plan; leave shared-state creation to execution after both checks succeed. Reuse the per-scope initialize/apply/check sequence through a private helper if needed; do not add another parser or a public input-preparation lifecycle. - Preserve nil-factory zero values, caller-owned objects/aliases, copied core-owned repeated collections, and early failure. Do not clone arbitrary inputs or add pointer allocation. Preserve existing ordinary-operation paths.
- Wrap side input-check errors with scope and left/right context, preserving
existing error identity and core classification. Missing required selected
values are usage errors;
ErrFlagWiringremains operational. Both stop before state creation and provider calls. Side wording is not a new exact-text contract.
Completion evidence — complete: comparison plans now hold a dedicated
prepared pair with its selected scope and two ComparisonSide values; ordinary
scope inputs remain separate, and the pair is stored only after both inputs pass
checks. The shared helper initializes each reached input, applies the captured
scope bindings, and checks it with that side's selection. Tests cover equal
selections preparing twice, explicit false/zero and required selector handling,
one conversion per supplied occurrence, independent repeated slices, nil factory
zero values, preserved caller aliases, wrapped right-side wiring classification
and context, and absence of a partial plan after a side failure. Shared state is
created only after both checks pass. Help converts even an inapplicable supplied
selector while leaving input/check/state factories untouched. Focused comparison
tests and the full ckconfig and dependent ckp suites pass.
Stage 5: Implement typed native comparison and diagnostic retention
Primary file: integrations/ckconfig/configkit_adapter.go.
Outcome: FromConfigKit and consequently AppCommand support comparison using
their existing typed loader, with native records and diagnostics preserved.
- Include
DiffinFromConfigKit's supported operation list and wire its comparison callback. Keep the genericTinside that adapter; no new view facet, result assertion, reflection, or private upstream import is needed. - Load left and then right with their respective selection/input and the same supplied state. Check context at ordinary boundaries, stop after a failed reached load, and do not skip either reached load just because selections compare equal. A successful callback returning nil is operational failure.
- Adapt diagnostics from each reached successful result or
LoadErrorusing existing conversion. Accumulate left diagnostics before loading right, and retain both loads' diagnostics before invoking native comparison. Preserve side identity in comparison diagnostic headings so source IDs from two loads are not confused. Leave ordinary diagnostic headings unchanged. - Call
configkit.Diff(left, right)and return its native records incommandkit.Acquired[[]configkit.Difference]. On error, return accumulated diagnostics with a cause preserving supportederrors.Is/errors.Asidentity. Add useful scope/side context without making exact error wording or callback internals a new contract. - Reuse small local acquisition/diagnostic helpers only where they simplify ordinary and paired loading. Keep cache policy and arbitrary callback output application-owned. Update adapter/convenience GoDoc from four operations.
Use a straightforward fail-fast adapter sequence: check context, load left, collect left diagnostics, check context, load right, collect right diagnostics, check context, call native Diff, and return records/diagnostics/error. A left failure prevents right loading; a right failure retains left diagnostics; native Diff failure retains all accumulated diagnostics. Never write report bytes here. Ordinary Acquire must continue to return its existing error identity and diagnostic shape. Use side-specific labels only for comparison, with quoted scope names and native source legends, not raw configuration contents or arguments in headings. This is the selected internal coordination for the adapter, not a new policy for application-supplied pair callbacks. Tests should protect diagnostic retention and required lifecycle boundaries, not an incidental number of context polls.
Completion evidence — complete: FromConfigKit now declares all five
operations and wires its typed comparison callback to sequentially load both
selected sides with the same state, then return configkit.Diff records. The
adapter keeps successful and LoadError diagnostics in left-to-right order,
labels comparison diagnostics by side, checks cancellation at the selected
boundaries, and wraps load/native comparison failures with their causes intact.
Direct tests use real ConfigKit loads for public changes, base-versus-overlay
loading, equal values after defaults and resolution, and safe concealed-change
records. They also cover both sides' diagnostics when the right load fails,
wrapped LoadError identity, nil results, cancellation between loads, and a
native Diff error from a non-nil uninitialized result while retaining available
diagnostics. Ordinary Acquire diagnostics and error identity remain covered
and unchanged. go test -count=1 ./... passes in ckconfig and ckp.
Stage 6: Implement safe YAML snapshot and semantic-path placement helpers
Primary new file: integrations/ckconfig/diff_report.go.
Outcome: small private helpers preserve native snapshot values and place them at semantic mapping paths. This stage does not classify records or deliver output.
- Use
go.yaml.in/yaml/v4nodes directly. Decode a native safe snapshot into a document node and retain its content node as the value, not its YAML string and notmap[string]any. Preserve tags, scalar values, nested collections, explicit null, and atomic sequence/structural values. Parsing is presentation work only. Add no public renderer contract or package. - ConfigKit's public path API exposes canonical JSON Pointer spelling, not
public segment access. Handle the empty spelling as the root before splitting;
/identifies one empty mapping key. For non-root pointers, remove the leading separator, split on/, and decode~1and~0once. Preserve empty, numeric, dot, percent, slash-containing, and tilde-containing keys. Do not use filesystem cleaning, URL decoding, or numeric-index guessing. Native sequence changes are atomic, so report ancestors for descendant records are mappings. Construct mapping keys as YAML strings. - Implement a private placement helper accepting a category root, decoded path tokens, and a value node. Create category ancestors as needed, inserting nodes in native traversal order. A root record assigns its safe node directly to the category; it need not be a mapping when the safe snapshot is null or wholly masked. Keep the accepted whole-root ambiguity; add no special root envelope or hidden-value analysis. Mapping-to-empty changes use the native deletion records. Reuse a matching ancestor rather than appending a duplicate key. Assemble mapping content as key/value pairs; preserve insertion order. If placement cannot represent supplied records, return an assembly error rather than silently replacing an incompatible ancestor. Do not add speculative validation of every theoretically malformed provider result.
- Helpers return errors to their caller with useful path context and no output effects. They do not examine original configs, expand atomic regions, infer masking from strings, or determine whether a difference exists.
Completion evidence — complete: private YAML-node helpers now decode safe
snapshots without converting them through loosely typed values, decode canonical
JSON Pointer tokens once, and insert mapping keys as YAML strings while reusing
ordered ancestors. Focused checks cover root and /, nested placement sharing an
ancestor, numeric/dot/percent keys, escaped slash/tilde keys, and ~01 becoming
literal ~1. Snapshot tags are checked for null, booleans, a large integer,
number/boolean-like strings, and empty mappings/sequences; malformed YAML returns
an error. go test -count=1 ./... passes in ckconfig. Classification and report
disclosure remain for the following stage.
Stage 7: Classify native records and encode the YAML document
Primary file: integrations/ckconfig/diff_report.go.
Outcome: a pure renderer produces the complete selected report bytes using Stage 6's helpers, ready for delivery in Stage 8.
-
Implement one private renderer taking
[]configkit.Differenceand returning([]byte, error). Create fresh orderedadditions,changes,deletionsnodes per call, initially empty mapping, empty mapping, and empty sequence. No exported DTO, configurable renderer, or alternate text format is needed. -
Classify native records using this table:
Left present Right present Report action false true Place the safe right value in additions true true Place the safe right value in changes true false Append the canonical pointer to deletions These are the native changed-region cases. Iterate records in native order; preserve that order within each category. Only decode right snapshots needed for additions/changes; an absent deletion side has no value to parse.
-
Use Stage 6's placement helper, including direct category assignment for a root record. Keep empty categories when no records apply. Encode with the existing
encodeYAMLhelper into a buffer, always emitting the three root keys in selected order. The empty comparison is the roadmap's three-empty-category document, not null, omitted keys, an empty stream, or a textual message. -
Do not inspect left YAML to derive changes, compare rendered snapshots, expand atomic regions, infer sensitivity from marker text, append full configurations, or expose
Redacted/HiddenChangedreport fields. Concealed changes remain included because the native record exists, even when safe snapshots look equal. Do not claim every masked member changed. -
Complete the document before delivery. Parsing or encoding failures return an error before successful report bytes are written. Apply ordinary error handling rather than adding a general provider schema-validation framework or new restrictions on legitimate native records.
Completion evidence — complete: renderDiffReport now classifies native
records into ordered additions, changes, and deletions and encodes the complete
three-category YAML document before returning bytes. Semantic node checks cover
all categories, category order, the empty document, root values, explicit null
versus deletion, mapping-to-empty deletions, atomic sequences, and structural
values. Real ConfigKit results cover native redaction, a public string matching
the upstream redaction marker, and a concealed-only change; the report retains
safe values without exposing secret contents or native mask metadata. Malformed
present snapshots return an error without report bytes. go test -count=1 ./...
passes in ckconfig.
Stage 8: Execute comparison through existing outcomes and delivery
Primary files: integrations/ckconfig/operations.go; new diff.go.
Outcome: an installed config diff works end to end with the selected
preflight, acquisition, reporting, cancellation, and outcome behavior.
- Add the dispatch case (the operation name/summary landed in Stage 3).
Use the existing execution path to create shared state once after checked
pair preparation. Pass the invocation context, paired request, and state to
the selected
Compare. Do not call ordinaryAcquirefor comparison. - Copy/retain provider diagnostics through the outcome. On provider/native comparison failure, return operational failure with supported cause identity and no successful report. Check context after acquisition and before delivery; do not promise interruption inside synchronous upstream calls.
- Render with Stage 7, then use existing checked report delivery. Parsing or encoding errors are operational failures before report writing. Output errors or short writes preserve the established delivery error and incomplete-report behavior. No new exit status, assertion mode, or process termination is added.
- Successful comparisons return success for public differences, concealed differences, and no records. Nil/empty returned record slices both represent an empty successful comparison. Diagnostic stream handling remains core-owned.
- Update focused public execution tests to assemble real declarations and run through core/Cobra. Reuse existing test helpers rather than constructing another dispatcher or private core bridge.
Use the plan's prepared comparison scope and request, verifying that the pair
exists, and call Compare once with that request and state. Missing prepared
wiring is
an operational failure, not a fallback to Acquire. Reuse
configOperationFailure for provider/render/cancellation failures and
configDeliveryFailure for output failure. Before writing, failures have
ReportIncomplete=false and no delivery error; a report write failure carries
the writer error and the existing incomplete-report indication. Diagnostic
delivery itself continues through core, including its established writer-failure
handling. Do not manually write the same diagnostics from the handler.
Completion evidence — complete: the installed config diff dispatches through
the prepared comparison pair and its provider once, never through ordinary
acquisition. Execution preserves provider diagnostics and causes, checks
cancellation before writing, renders the full report before delivery, and uses
the existing operational/delivery outcomes. Public core/Cobra execution tests
cover default and explicit YAML, text rejection before provider work, right-side
configuration with inherited overlay selection, comparison-only discovery and
execution, and unsupported scopes before factories or providers. They also cover
provider errors with diagnostics and wrapped identity, malformed report snapshots,
cancellation after acquisition, output errors and short writes, and repeated
invocations with distinct side inputs and fresh shared state. A real ConfigKit
consumer comparison covers concealed changes and empty results. Existing
operations remain covered by the ckconfig suite. go test -count=1 ./... passes
in ckconfig and ckp.
Stage 9: Demonstrate the simple consumer comparison
Primary owner: integrations/ckconfig/examples/simple.
Outcome: the minimal AppCommand consumer compares two real local sources
without custom comparison wiring.
-
Extend the simple example's application loader to honor a supplied config file through ConfigKit's public source API, retaining its existing default configuration when omitted. Add two small local YAML fixtures with different greetings and a documented
config diffinvocation. KeepAppCommandwiring as the minimal consumer path; do not add a bespoke comparison handler. -
Name the fixtures
fixtures/before.ymlandfixtures/after.yml; use greetingsHelloandGoodbye. A supplied configuration, including explicit empty, goes toconfigkit.Fileunchanged. An omitted configuration keeps the current mapping source. Pass supplied overlay selection to native loading options rather than inventing CommandKit overlay semantics. -
Add a small example test using the assembled app and local fixtures. Expect
changes.greetingto beGoodbye; equal fixture selections produce empty categories. Existing default show and greeting commands remain usable. -
Update this example's README with a command run from its directory:
go run . config diff --config fixtures/before.yml --against-config fixtures/after.yml
Completion evidence — complete: the simple consumer now uses ConfigKit file
sources when --config is supplied, preserves the in-memory default when it is
omitted, and forwards supplied overlays through ConfigKit options. The required
before/after fixtures and README invocation are present. Its assembled-app test
checks changes.greeting: Goodbye, equal fixture selections with empty
categories, default config show, default greet, and explicit empty config
selection failing instead of falling back. The documented diff, show, and greet
commands also run locally without external services; the ckconfig suite passes.
Composed state/cache and PromptKit changes remain for the following stage.
Stage 10: Demonstrate composed selection and typed-input cache identity
Primary owner: integrations/ckprompt/examples/composed and its consumer tests.
Outcome: paired acquisition shares state without reusing one result across different selections or loader inputs; PromptKit remains an independent capability.
- Repair the composed example's single-result
runCachebefore demonstrating paired loads. Key cached results by the complete selection, including presence, and the suppliedCampaigninput used below. These fields are comparable; a small application-owned key and map suffice. This is example guidance, not a CommandKit cache guarantee or service. Use a privateapplicationCacheKeycontaining the completeSelectionandcommandkit.Supplied[string]campaign, and a map of keys to typed results. Nil input contributes the zero campaign value. Nil state bypasses caching; initialize the map in application-owned code as needed. Retain only successful results; failed loads are not cached by this example. Equal keys may reuse a result even though CommandKit still invokes both reached loader callbacks. - Make the composed application loader honor a supplied ConfigKit source;
retain its synthetic default when omitted. Add two local fixtures with valid
existing campaign definitions and different default campaigns. Demonstrate
application-scope comparison through the shared state, and verify the two
source selections produce an actual changed value rather than cache equality.
To demonstrate meaningful typed input, apply explicitly supplied
Campaignthrough ConfigKit's publicOptions.Overridesto the existingdefault_campaignfield, usingconfigkit.Keys("default_campaign")and the safe flag label--campaign. Apply only when supplied; do not mutateResult.Configafter loading.Limitremains an input check rather than a loading dependency. Explain these example-specific choices and cache identity; they prescribe no downstream application behavior. Usefixtures/before.ymlandfixtures/after.yml, keeping both existing campaign definitions and changing only their default from spring to autumn. Use ConfigKit file sources when explicitly selected, the current mapping when omitted, and native overlay options for supplied overlays. Include campaign overrides in the load options before loading, not in a post-load mutation. - Keep
ckprompt.ConfigScopewithoutDiff. Verify comparison selects the application scope while engine validate/show/sources/explain, named prompt and profile inspection, andsession restorestill work under PromptKit v0.12.0. Do not add PromptKit comparison by reaching into its ConfigKit internals. - Add small consumer tests for fixture comparisons and cache reuse/separation, including separation for different supplied campaign values and presence. Use local sources and synthetic values; no model requests or credentials. With one cache, direct application-loader calls with different files and with different supplied campaigns must retain their respective values. Repeating an identical key may reuse the result. The CLI pair shares domain flags; these direct tests do not imply independent per-side domain selectors.
- Document the composed invocation with both fixture paths. Ordinary comparison
reports
changes.default_campaign: autumn; an explicit--campaign springsupplied to both sides can make their effective defaults equal. Include--limit=0when campaign inspection triggers the existing input check.
Completion evidence — complete: the composed example now loads its two local
files through ConfigKit, applies explicitly supplied Campaign as a native
default_campaign override, and caches successful Results by complete supplied
selection and Campaign presence/value. Tests confirm the ordinary fixture diff
reports changes.default_campaign: autumn, a shared --campaign spring override
produces equal effective values, distinct file and supplied-campaign keys do not
reuse the wrong result, equal keys may reuse, nil state bypasses caching, and
failed loads are not retained. Representative engine validate/show/sources/
explain, named prompt/profile inspection, and session restore commands continue
to succeed without model calls. The PromptKit config scope remains unsupported
by config diff; that selection returns a usage error. README commands and cache
identity explanation are updated. go test -count=1 ./... passes in ckp.
Stage 11: Reconcile operator, API, and consumer documentation
Outcome: current documentation describes implemented behavior under its canonical owners, while future/deferred work stays clearly separate.
- Update ckconfig GoDoc, its README, the CLI reference, consumer guide, internal overview, relevant example READMEs, and development commands. Document exact new exports in GoDoc and exact CLI/report semantics in the CLI owner; other documents summarize and link. Include inheritance/presence, lifecycle, independent capabilities, native semantics, reduced safe reports, ordinary null/deletion distinction, accepted root ambiguity, and outcome rules.
- Move the implemented DIFF-01 portion out of deferred work. Retain unsupported PromptKit comparison, catalogs, independent per-side domain selectors, and unrelated deferred ideas. Update this plan's completion evidence and the feature roadmap's implementation status; do not keep proposals disguised as current operator/API documentation.
- Explain that suite inputs are initialized per comparison side, while state remains per invocation. Native result metadata stays in the provider contract; the CLI omits separate masking/hidden-change fields. Distinguish a changed masked region from a claim about every masked member. Explicitly document no-difference YAML and the accepted whole-root ambiguity without promising reloadable output or patches.
- Verify links/fragments, copied CLI examples, and references to implemented symbols. Keep accepted ADR decision text unchanged; presentation details remain in CLI/API owners rather than returning to ADRs.
Completion evidence: current operator/API/consumer owners agree with the implemented behavior; no stale four-operation convenience claims, text-diff promises, or deferred markers for delivered ConfigKit comparison.
Stage 12: Reconcile manifests, notices, and pending integration release notes
Outcome: the two integrations distribute accurate dependency metadata and notices without altering core's release tool or publishing a release.
- Reconcile module-local ConfigKit notices to v0.7.1 and its pinned source commit from the roadmap. Inspect resolved integration tool graphs for ReleaseKit v0.4.1 and relevant transitives; update actual versions, roles, pinned links, and any required license/notice material under the OSS policy. Root notices describe core's own graph, not the combined workspace's selected versions.
- Finalize normal manifests/sums using real released upstream dependencies. Preserve intended CommandKit requirements without local replacement clauses or fabricated unavailable checksums. Any remaining prepublication limitations belong in the validation evidence and existing release procedure. For alternate-file validation, start from that module's own manifest, retain reviewed tidy changes, and remove only the injected CommandKit replacements before committing normal metadata. Do not substitute the root or workspace manifest. The intended requirements remain core v0.2.0 and, for ckprompt, ckconfig v0.1.0; source replacements are only local validation wiring.
- Update the still-unpublished integration v0.1.0 release notes to describe dependency adoption and comparison availability where appropriate. Recheck tag status first: never revise a published release record. If publication has occurred since this baseline, prepare a fresh version under the release policy rather than rewriting history. Do not add a core release solely for integration behavior, bump PromptKit, publish tags, or run the release launcher.
- Check module-local license/attribution links and release-note footers under each module's own tag prefix. Use actual pinned component sources; do not copy workspace-selected dependency versions into core attribution. Verify the normal manifests have no disposable local replacements or invented sums.
Completion evidence: reviewed integration notices and pending release notes, normal distributed manifests/sums reflecting actual resolved external dependencies, and unchanged root maintainer tool declaration, launcher, and release profiles.
Stage 13: Validate the complete sprint and finish
Outcome: evidence proves the complete feature end state across all three module boundaries, without implying publication or new guarantees.
-
With upstream dependencies prepared, run the deterministic default suites in the development workspace:
go test ./... (cd integrations/ckconfig && go test ./...) (cd integrations/ckprompt && go test ./...)These commands traverse their own modules only. Do not run paid/live calls, release publication exercises, or add coverage/performance/platform gates.
-
Independently check core from the repository root:
GOWORK=off GOTOOLCHAIN=local go test -mod=readonly ./... GOWORK=off GOTOOLCHAIN=local go list -mod=readonly -m all GOWORK=off GOTOOLCHAIN=local go list -mod=readonly -deps ./...Inspect output for the established absence of optional integration/owning-library dependencies and for the unchanged declared maintainer tool selection.
-
Check each integration outside workspace mode. If its normal CommandKit prerequisites are downloadable, use its normal manifest. Otherwise use a disposable alternate manifest, keeping its actual ConfigKit/PromptKit versions:
- Create a temporary directory; copy that integration's
go.modto a.modfile there and copy any existing sum file to the matching.sum. - From the integration directory, use
go mod edit -modfile=<temporary.mod>with-replace=gitea.maximumdirect.net/eric/CommandKit=<absolute-repo-root>. For ckprompt, also use-replace=gitea.maximumdirect.net/eric/CommandKit/integrations/ckconfig=<absolute-ckconfig-dir>. These are module-path replacements, not fabricated version downloads. Do not replace ConfigKit, PromptKit, or ReleaseKit. - With
GOWORK=off GOTOOLCHAIN=local, tidy the alternate file, then rungo test -modfile=<temporary.mod> -mod=readonly ./...,go list -modfile=<temporary.mod> -mod=readonly -m all, andgo list -modfile=<temporary.mod> -mod=readonly -deps ./.... Inspect selected versions, package imports, and boundary violations. - When using this procedure for Stage 1 dependency reconciliation, retain the
reviewed tidied external requirements and real sum entries in that module's
normal manifest/sum. Remove the injected root replacement and, for ckprompt,
the injected ckconfig replacement with
go mod edit -dropreplace=<module-path>before staging normal metadata. Preserve module identity, minimum Go version, and intended versioned CommandKit requirements. The temporary sum file has no checksum for a dependency replaced by local source; do not fabricate one. - Discard alternate files after review. They establish local-source module isolation and upgraded external compatibility, not availability of future CommandKit tags. Never copy their replacements into committed manifests.
- Create a temporary directory; copy that integration's
-
Run the maintained simple/composed comparison examples and representative existing inspection/domain commands. Inspect YAML content, diagnostic streams, and outcomes. Confirm no comparison is advertised by the PromptKit adapter.
-
Audit every feature-roadmap work area against changed code, tests, current docs, examples, manifests, and notices. Include both sides' input/check timing, callback-free discovery, equal-selection loading, warning retention, native safe values, all report categories, null/deletions, output errors, and ordinary inspection regressions. Reuse earlier successful evidence for unchanged areas.
-
Record actual commands/results and unresolved external limitations here. Review the complete diff including new files; check documentation whitespace and links. Commit intended implementation work under the agent policy and report remaining unrelated work. Follow existing release preparation later; this sprint does not publish or wait for module proxy availability.
Completion evidence: all three default suites, independent module graph reviews (with local-source limits identified when applicable), maintained consumer results, feature-to-evidence audit, and reviewed final documentation/distribution changes. No significant product/API decisions remain open in this plan.