Record the ConfigKit upgrade and comparison plan

This commit is contained in:
2026-10-11 18:23:55 +00:00
parent ecd713ef2f
commit f67ee235d7
3 changed files with 468 additions and 17 deletions

View File

@@ -2,7 +2,7 @@
**Base Policy Version:** 1.3
**Last Updated**: 2026-10-10
**Last Updated**: 2026-10-11
**Upstream Source:** [architecture.md](https://gitea.maximumdirect.net/eric/policy/src/commit/d84a38ebab26f048cafbe59a2246700c35d29843/docs/policy/architecture.md)
@@ -134,6 +134,14 @@ and input checks before shared state initialization and provider acquisition.
Factories need not run after a failure and remain uninvoked during construction,
installation, help, version, and scope discovery.
For two-selection configuration comparison, prepare and check each side's input
separately with its scope identity and selection, applying the same applicable
converted selector values to both. Finish both checks before initializing shared
state once and invoking the integration-owned pair provider under
[ADR 0012](../adr/0012-typed-configuration-comparison-pairs.md). Required provider
wiring follows declared capabilities; a comparison-only scope does not require
an unrelated ordinary acquisition callback.
Omitted input and state factories supply the respective type's zero value,
including nil for pointers. Caller factories and callbacks retain responsibility
for objects they return or reference; do not promise arbitrary deep copies,
@@ -232,8 +240,10 @@ Concrete integration needs guide provider representations under
Native report writers and safe inspection data preserve owning-library semantics.
Loading all selected views before report output is a `config show` decision under
[ADR 0007](../adr/0007-native-yaml-reports.md); other inspection operations define
their own output timing. Semantic comparison uses supported safe snapshots and
reports sensitivity gaps under [ADR 0008](../adr/0008-semantic-configuration-diff.md).
their own output timing. Semantic comparison delegates to supported owning-library
comparison APIs and retains paired safe disclosure and hidden-change metadata at
the provider boundary. Reports may present the reduced view selected by
[ADR 0008](../adr/0008-semantic-configuration-diff.md).
## Scope And Non-goals

438
docs/roadmap/configkit.md Normal file
View File

@@ -0,0 +1,438 @@
# Adopt ConfigKit v0.7.1 and expose native configuration comparison
Status: proposed feature roadmap; dependency adoption and `config diff` are not
implemented. The targeted delivery includes both, as requested by the user.
This document defines the selected end state; a subsequent staged
implementation plan will define execution order.
## Purpose and authority
Adopt ConfigKit's current loading and inspection improvements and expose its
native effective-configuration comparison through CommandKit. Operators should
be able to compare two selections of one configuration scope, including the
effect of a selected overlay, without applications writing duplicate CLI or
comparison infrastructure.
The revised [ADR 0008](../adr/0008-semantic-configuration-diff.md) owns native
comparison and paired safe disclosure. [ADR 0009](../adr/0009-configuration-scopes.md)
owns scope selection and independent capabilities; [ADR 0011](../adr/0011-captured-declarations-and-installation.md)
owns captured declarations, typed input, and shared state, refined for comparison
pairs by [ADR 0012](../adr/0012-typed-configuration-comparison-pairs.md). The
[architecture policy](../policy/architecture.md) controls ownership and dependency
direction. The current ADR alignment edits remain uncommitted for user
review; this roadmap follows their agreed comparison direction.
The repository is being developed as a pre-release alpha. Choose the simplest
coherent final API without preserving prototype shapes through compatibility
wrappers. Existing deliberately selected contracts still control; changes to
published releases follow the [release policy](../policy/release.md).
This work consumes ConfigKit's supported public API. No upstream extension is
required for the selected comparison goal. ConfigKit owns comparison and safe
disclosure; CommandKit owns selection, coordination, adaptation, and presentation.
## Current baseline
| Owner | Current state |
| --- | --- |
| Core module | Go 1.26.0; captured commands, typed bindings, dispatch, outcomes, help, and version. No ConfigKit dependency. ReleaseKit v0.4.0 is the declared maintainer tool. |
| `integrations/ckconfig` | Requires ConfigKit v0.5.0. `Scope`, `Suite`, `FromConfigKit`, and `AppCommand` expose scopes/validate/show/sources/explain. No comparison operation or provider exists. |
| `integrations/ckprompt` | Directly requires ConfigKit v0.5.0 and PromptKit v0.12.0. Depends on ckconfig's public scope contracts and adapts engine configuration plus named definitions. |
| Deferred comparison | [DIFF-01](deferred.md#diff-01-semantic-configuration-comparison) records the target. Native semantics, selection syntax (OD-1), provider representation (OD-2), and the YAML-only report (OD-3) are accepted. |
The existing ConfigKit adapter acquires a typed `*configkit.Result[T]` and exposes
its effective, source, and explanation facets through `View`. That generic
adapter is a useful place to retain typed results for comparison. `View` alone
does not supply two results of the same `T` to `configkit.Diff`.
ConfigKit v0.7.1 identifies source commit
`6fffde4af8f31f99d3ee295c426eb3234cb0fb77`. Relative to the selected v0.5.0:
- v0.6.0 reduces repeated loading, resolution, metadata, and inspection work.
Its Go 1.26.0 minimum is already met by CommandKit.
- v0.7.0 adds native frozen effective-value comparison.
- v0.7.1 changes the upstream maintainer tool to ReleaseKit v0.4.1, with no
runtime/API change from v0.7.0.
The YAML dependency remains `go.yaml.in/yaml/v4 v4.0.0-rc.6`. Existing public
loading and inspection signatures are unchanged. The review found no apparent
adapter migration requirement for existing operations; that is static review
evidence, not a completed upgrade validation. See the pinned
[release notes](https://gitea.maximumdirect.net/eric/ConfigKit/src/commit/6fffde4af8f31f99d3ee295c426eb3234cb0fb77/docs/releases/v0.7.1.md)
and [comparison contract](https://gitea.maximumdirect.net/eric/ConfigKit/src/commit/6fffde4af8f31f99d3ee295c426eb3234cb0fb77/docs/config.md#effective-configuration-diffs).
## Targeted end state
### Dependencies and module boundaries
Both integration modules select ConfigKit v0.7.1 and carry normal dependency
metadata for their actual graphs. Consumer Go minimums remain 1.26.0, the selected
release toolchain remains 1.26.8, and PromptKit remains v0.12.0 for this work.
Core continues to have no ConfigKit or optional-integration dependency.
ConfigKit's declared tool requirement brings ReleaseKit v0.4.1 into the integration
module graphs. Reconcile that selection through normal Go module resolution and
accurate attribution. A combined development workspace can also select that
version. This does not by itself change the core module's separately declared
release tool or authorize release-launcher changes; assess core through its own
workspace-off graph.
Independent module versions remain independent of upstream ConfigKit's version.
Release preparation uses appropriate unused versions under the existing policy,
without moving tags or claiming unpublished dependencies are available.
### Ownership
| Owner | Target responsibility |
| --- | --- |
| ConfigKit | Frozen effective-value equality, changed-region selection/order, sensitivity union, paired safe snapshots, hidden-change detection, and load semantics. |
| ckconfig | Comparison capability and provider contracts; typed ConfigKit adapter; `config diff` input preparation, acquisition coordination, reports, and upstream diagnostic adaptation. |
| Core | Existing declaration installation, flag conversion/presence, help, dispatch, cancellation context, and outcome/delivery mechanics. No comparison model or engine. |
| ckprompt | Continue existing integration behavior with the upgraded shared dependency and ckconfig API. Do not claim native PromptKit comparison support through this upgrade. |
| Consuming application | Configuration definitions, acquisition callback internals, loader/cache choices, domain interpretation, startup, and process exit. |
### ConfigKit-backed comparison
The `config` family exposes `diff` when at least one scope declares the capability.
Help and `config scopes` report actual availability. A scope can support comparison
independently of other operations; enabling comparison elsewhere does not require
every scope to support it. Unsupported selections fail as usage errors before
acquisition.
`config diff` selects one scope, defaults to `app`, and does not accept
`--scope all`, as selected by ADR 0009. It compares earlier/left and later/right
effective configurations. Both file comparison and base-versus-overlay comparison
are supported through ordinary application loading choices. Exact side selection
and inheritance rules are accepted in OD-1 below.
ConfigKit-backed adapters use the same supplied typed loader for existing
inspection and both comparison selections. They delegate equality and paired
disclosure to `configkit.Diff`; no rendered-YAML comparison or assertion on
mutable `Result.Config` is introduced. Convenience wiring remains simple:
`AppCommand` and `FromConfigKit` should provide native comparison alongside their
existing supported operations, with ordinary capability narrowing available.
The provider representation is accepted in OD-2.
Declaration capture, metadata discovery, and help leave suite factories and
acquisition/comparison callbacks uninvoked. Invocation preparation uses original
typed binding identities and converted values, preserving explicit empty, false,
and zero selections. Supplied values are converted through core once, including
help paths; applying a value to both comparison inputs does not parse it twice.
Input/state timing for the pair is part of OD-2, rather than an implicit extension
of the existing one-selection lifecycle.
### Comparison meaning and disclosure
Retain ConfigKit's native changed-region records at the provider boundary, including
semantic paths, left/right `Present` and `Redacted` states, safe YAML values, and
`HiddenChanged`. The default report deliberately presents a reduced view under
OD-3 rather than exposing all native metadata.
The native contract owns detailed comparison semantics; the important operator
consequences are:
- Changes reflect frozen effective load values, including defaults and recorded
domain resolution. Provenance or source-layout differences alone are not
effective changes.
- Compare final values, not source presence alone. Omitted and explicit-null
inputs may resolve equally for a typed field; an absent map key and a present
null-valued key can remain distinct in the final configuration. Nil and empty
containers also remain distinct. Mappings compare recursively; sequences report
atomic changed regions.
- Both sides receive the union of sensitivity from both loads. Structural
replacements can mask an entire region.
- A difference can exist even when its safe left/right YAML is identical.
`HiddenChanged` reports an actual concealed change; redaction alone does not.
- An empty successful diff supports equality within ConfigKit's inspected value
model. Typed secret references compare by configured name; credential rotation
behind an unchanged reference is outside that comparison.
Reports use safe changed regions without inventing a coverage-gap inventory,
decoding redaction-marker text as metadata, identifying concealed members beyond
upstream disclosure, or adding separately rendered full configurations as if they
had received paired protection. Full paired document context and a sensitivity
audit are separate potential capabilities, not prerequisites here.
### Reports, diagnostics, and outcomes
Default `config diff` to one YAML document with `additions`, `changes`, and
`deletions`, as selected in OD-3. Use existing output streams. YAML is the only
supported report format, with explicit `--format yaml` accepted. The
view distinguishes deletion from null but omits separate redaction and concealed-
change metadata. Move the implemented report contract to the CLI reference.
ADR 0008 records this reduced view while retaining native metadata at the
provider boundary. Other commands retain their existing format defaults.
Successful comparisons return success whether they find differences, concealed
changes, or none. No assertion status or separate exit code is added. Load and
comparison failures retain operational classification and supported error
identity; selection/input errors retain usage classification. Output failures
use existing delivery and incomplete-report semantics.
The ConfigKit adapter retains diagnostics from the acquired results, including
warnings and any load failure encountered. Present them through existing core
diagnostic delivery; preserve meaningful left/right context when needed to
identify the failing selection without losing wrapped error identity. Retain
left-side warnings if the right load fails, and diagnostics from both successful
loads if native comparison fails. Finish acquisition and native comparison before
writing a successful diff report. Cancellation is checked at ordinary operation
boundaries; do not promise interruption inside a synchronous upstream call.
## Work needed from the baseline
These are work areas, not numbered implementation stages. Use the accepted
choices below to write the staged implementation plan.
- **Dependency adoption:** upgrade both integration requirements, reconcile sums
and indirect/tool dependencies, and assess PromptKit v0.12.0 against the newly
selected ConfigKit version. Preserve distributed manifests without sibling
replacements or committed workspace files.
- **Capability and provider API:** extend ckconfig's operation/capture/validation
model for the selected comparison provider. Preserve retained adapter errors,
capability narrowing, metadata-only scopes, and validation only of bindings
used by generated commands. Do not require an ordinary acquisition provider
for a comparison-only scope if its selected pair provider is sufficient.
- **Typed preparation:** capture comparison selection metadata and applicable
selector lists once. Prepare/check the selected pair through core bindings,
with state and callback timing from the chosen lifecycle. Keep caller-owned
objects and aliases under existing ownership rules.
- **Native adaptation:** extend the typed ConfigKit adapter/convenience factory
to load the selected pair and call `configkit.Diff`. Reuse diagnostic adaptation
and ordinary error handling without introducing a generic provider framework,
global cache, or untyped result transport.
- **Command and reports:** generate the conditional `config diff` declaration,
integrate it with discovery/help, and render the chosen report contract using
native safe values. Preserve existing operation/report behavior.
- **Documentation and distribution:** update GoDoc, CLI/consumer guidance,
maintained examples, affected module notices and pinned ConfigKit links, and
relevant release notes as implementation lands. Include a maintained comparison
example that loads genuinely different selections through shared state, with
cache keys accounting for the selection and typed-input values used by its
loader. Review transitive tool notices
for the actual graph. Move DIFF-01's implemented portion out of deferred work;
retain unresolved future PromptKit comparison and unrelated proposals.
- **Validation evidence:** run each affected module's deterministic offline suite
and representative consumers. Check module/package boundaries independently
with workspace mode off. If CommandKit prerequisite versions remain unpublished,
use disposable alternate module files for local-source wiring evidence, as
described in the [development guide](../development.md#documentation-validation).
Do not fabricate future checksums or turn migration checks into permanent gates.
Focused comparison coverage should protect the CommandKit boundary: capability
availability before callbacks, side selection/presence, chosen input/state
lifetime, native public and concealed changes, absent versus null values, and
empty successful comparisons. Include representative load/comparison errors,
diagnostics, cancellation, and output failure. Specifically cover left-side
warnings followed by a right-side load failure, and diagnostics from both loads
followed by a comparison failure. Verify that selection-aware caching does not
return the left result for the right selection. Include the three report
categories, explicit null versus deletion, concealed-only changes, and an
unchanged masked value inside a changed sequence. Existing inspection and composed
PromptKit behavior must continue to work. Reuse native collaborators and test
CommandKit's adaptation/reporting; do not duplicate ConfigKit's entire comparator
suite or impose performance thresholds.
## Scope limits and completion
This work delivers ConfigKit adoption and ConfigKit-backed `config diff`.
PromptKit upgrade/comparison/catalogs, JSON reports, per-element sequence matching,
resolved credential comparison, full paired document context, assertion mode,
provider caching, and core backend changes are outside this target.
Completion means upgraded integration dependencies, usable native comparison
through declared scopes and convenience wiring, selected public API/CLI/report
contracts in their current owners, relevant maintained examples, accurate notices,
and sufficient boundary validation. No open choices may be silently converted into
contracts by implementation. Publication follows the existing release procedure
and is not performed by this planning pass.
## Selected report design (OD-3)
**Selected:** the default operator-facing output of `config diff` is a single YAML
document with three primary keys: `additions`, `changes`, and `deletions`.
Additions and changes preserve configuration hierarchy and contain only safe
right-side values; deletions are canonical JSON Pointer paths. For example:
```yaml
additions:
server:
health_check: true
changes:
server:
port: 9090
logging:
destination: null
deletions:
- /server/legacy_timeout
```
Classify each native difference by its presence fields: absent-left/present-right
is an addition, present-left/absent-right is a deletion, and present on both sides
is a change. Explicit null remains a value; do not encode deletion as null.
Changed sequences remain whole sequences, and native structural replacements
remain whole reported regions. Do not derive a second set of changes by comparing
rendered values or expanding atomic regions.
Assemble additions and changes from ConfigKit's paired-safe right snapshots,
preserving YAML value types and semantic path components. Parsing and assembly
serve presentation only. Do not read unredacted configuration values or append
independently rendered full configurations. The result is a partial inspection
view, not a complete configuration or an automatically applicable patch.
Omit separate `Redacted` and `HiddenChanged` metadata from this default view.
Every reported region changed, but a masked value within it need not have changed:
a public field in an atomic sequence may change while a secret remains unchanged.
A concealed-only change still appears in its native reported region even if the
safe snapshots are identical. Preserve native masks without interpreting marker
text; a literal public string resembling `[REDACTED]` remains an ordinary value.
The default report intentionally does not expose every distinction available in
the native records. A detailed metadata format can be considered separately if
needed; it is not required for this delivery.
**Accepted edge-case treatment:** when a nonempty root mapping becomes an empty
mapping, report the native removed-key paths under `deletions`. The reduced report
need not add special metadata or a separate envelope solely to distinguish
exceptional whole-root empty/null transitions from an empty comparison. Ambiguity
in that case is acceptable to the user and is not a planning blocker. Native
comparison records retain their distinctions; ordinary field-level null values
and deletions retain the selected reporting rules.
**Accepted format and empty-category treatment:** YAML is the only report format
for this delivery; a separate plain text report adds no needed operator value.
Always include all three keys in the order `additions`, `changes`, `deletions`.
Empty additions and changes render as `{}`; empty deletions render as `[]`. An
empty comparison therefore produces:
```yaml
additions: {}
changes: {}
deletions: []
```
This stable shape supports automated consumption without missing-key handling
and retains the accepted tolerance for exceptional whole-root ambiguity. Other
commands keep their existing formats.
ADR 0008 records this selected presentation. Selection syntax and provider shape
are accepted below. No significant open decisions remain for this roadmap.
**Alternatives considered:** a full difference envelope with left/right snapshots
and explicit presence, redaction, and hidden-change fields preserves all native
metadata, but is more verbose than the chosen operator view. Embedding parsed
values in that envelope avoids YAML strings but retains the record-oriented
layout. A right-side-only view without deletion paths cannot represent removals
unambiguously. The selected three-category document balances configuration-shaped
output with explicit removal reporting.
Omitting empty categories would shorten the report but require consumers to
handle missing keys. A separate text report could label changes by path or group
them into sections, but YAML already provides readable operator output; another
renderer and presentation contract are unnecessary for this delivery.
## Accepted Decisions
### OD-1: How operators select the two sides
**Accepted:** use existing `--config` and `--overlay` for the left side and
add `--against-config` and `--against-overlay` overrides for the right side.
Omitted right fields inherit the left field, including its supplied state;
explicit right values, including an empty string, replace it. Use the scope's
applicable typed domain selectors on both sides. This supports two files and
base-versus-overlay comparison while reusing familiar syntax and preserving
provider-owned defaults. Allow equal selections; they can produce a legitimate
empty diff without an invented requirement that an override be supplied. Do not
short-circuit acquisition merely because the selections are equal: caller-owned
loading may produce different effective results.
For example, these show the accepted target syntax, not current commands:
```text
app config diff --config before.yml --against-config after.yml
app config diff --config base.yml --against-overlay candidate
app config diff --config base.yml --overlay current --against-overlay ""
```
Right-side inheritance is field-by-field. Overriding only the configuration still
inherits the left overlay. For either selection field:
| Left field | Right override | Effective right field |
| --- | --- | --- |
| Omitted | Omitted | Omitted, preserving provider defaults |
| Supplied value | Omitted | Same value and supplied state as left |
| Explicit empty string | Omitted | Explicit empty string, still supplied |
| Any value/state | Supplied value | Right value, supplied |
| Any value/state | Explicit empty string | Empty string, supplied |
The provider continues to interpret an explicitly empty overlay through its
ordinary loading contract; CommandKit does not define empty as disabling overlays.
Shared domain selector values are the initial scope;
independent per-side domain selectors can be considered with a concrete consumer
need, without precluding them as future API work.
**Alternative considered:** expose symmetric `--left-config`/`--right-config` and
`--left-overlay`/`--right-overlay` selections, resolving each side independently.
This makes two independent selections explicit and avoids inheritance rules. It
adds vocabulary beside existing configuration selectors and repeats flags for
the common single-base overlay workflow. It remains viable if symmetry is valued
more than reuse of familiar inspection syntax.
### OD-2: Comparison provider and input/state lifetime
**Accepted:** add a narrow optional scope comparison callback receiving
the pair's selections, separately prepared left/right typed inputs, and one
shared state value. Group each side's selection and input in a request value to
avoid several positional left/right arguments. Return
`commandkit.Acquired[[]configkit.Difference]` plus an error, following the existing
acquisition convention. Reuse upstream public record types rather than duplicate
them solely to rename fields. The selected target shape in ckconfig is:
```go
type ComparisonSide[I any] struct {
Selection Selection
Input I
}
type ComparisonRequest[I any] struct {
Left ComparisonSide[I]
Right ComparisonSide[I]
}
// Accepted target field on Scope[I, S]:
// Compare func(context.Context, ComparisonRequest[I], S) (commandkit.Acquired[[]configkit.Difference], error)
```
These names and the callback shape are selected for implementation; they are not
implemented exports. ADR 0012 records paired provider ownership and input/state
lifetimes; this roadmap owns the target declarations until the Go API is implemented.
The typed `FromConfigKit` adapter can retain its `T`, invoke the supplied loader
for each reached side, and call `configkit.Diff`. Existing single-view acquisition
remains responsible for ordinary operations. A comparison-only scope can supply
the pair callback without an unrelated `Acquire` callback. Declared capabilities
determine which callbacks must be wired.
For a reached comparison, initialize each selection's `I` separately, apply the
same applicable captured selector values, and check it with that side's
`Selection`. Finish both input checks before initializing `S` once and invoking
the pair provider. Omitted factories still supply zero values, including nil
pointers; separate calls do not guarantee distinct caller objects. Later factories
or loads need not run after failure. Help/discovery remain free of these calls.
ADR 0012 records this two-selection specialization of ADR 0011's suite lifecycle,
and the architecture policy reflects it. Document the exact implemented API
contract in Go declarations and GoDoc when it lands.
This keeps generic result handling inside the owning adapter and preserves
typed binding composition without reflection, private-core access, or result
type assertions. Shared state supports consumer caches whose keys distinguish
the effective selections and any typed-input values that affect loading. A cache
that stores only one configuration can incorrectly reuse the left result for the
right and report equality. Demonstrate correct keys in the maintained example;
CommandKit neither supplies nor repairs application caches. Cache policy and
factory objects remain caller-owned.
**Alternative considered:** acquire a comparison-capable facet with each ordinary `View`,
then compare the two acquired facets. This shares single-side acquisition between
all operations and may suit providers already retaining typed results behind a
stable comparison handle. It requires a public way to establish compatibility
between two heterogeneous facets, and can lead to type assertions or extra
opaque-handle machinery. Consider it if representative non-ConfigKit consumers
demonstrate enough benefit to justify that additional contract.

View File

@@ -21,23 +21,26 @@ an idea here does not select its syntax, API, priority, or delivery date.
### DIFF-01: semantic configuration comparison
Expose comparison of two independently resolved selections of one scope using
owning-library safe snapshots. [ADR 0008](../adr/0008-semantic-configuration-diff.md)
owns the accepted comparison semantics, sensitivity union, and coverage-gap
handling. The target includes text/YAML reports of differences and gaps;
completed comparisons can succeed even when they find either.
owning-library public comparison APIs. [ADR 0008](../adr/0008-semantic-configuration-diff.md)
owns native comparison, paired safe disclosure, and the reduced default report.
The target includes a YAML-only report of safe changed regions; completed
comparisons succeed whether or not they find differences.
The initial review of ConfigKit v0.5.0 and PromptKit v0.12.0 did not find the
complete public snapshot/sensitivity contract needed for that target. Reassess
released upstream APIs when planning it; do not treat that historical review as
a claim about newer versions. Hidden-path metadata and supported gap reasons
must be available without fetching secrets. Rendered YAML and sibling internals
are not substitutes for an agreed public comparison representation.
public comparison support needed for that target. ConfigKit v0.7.0 introduced
`configkit.Diff`, retained in v0.7.1, which supplies native comparison and paired
safe changed-region snapshots. CommandKit still uses ConfigKit v0.5.0; dependency
adoption and comparison delivery are targeted by the
[ConfigKit roadmap](configkit.md). Assess PromptKit's supported
public comparison capabilities separately. Rendered YAML and sibling internals
are not substitutes for the owning library's public comparison contract.
Comparison selection syntax, context inheritance, and report layout remain
future design work. Earlier possibilities included right-side root/overlay
selection, shared versus independent domain context, and an optional assertion
mode. Those choices are unselected. Add public types and handlers when the
capability is supported, rather than exposing placeholders.
The ConfigKit roadmap records accepted selection syntax and the paired provider
API, with lifecycle recorded in
[ADR 0012](../adr/0012-typed-configuration-comparison-pairs.md). The selected report
shape is recorded in that roadmap. Independent per-side domain selectors and
an optional assertion mode remain outside that delivery. Add public types and
handlers with their implementation rather than exposing placeholders.
### CAT-01: PromptKit catalogs