Files
commandkit/docs/roadmap/implementation.md

922 lines
55 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Shared reporting and PromptKit v0.20.0 implementation plan
Status: implementation in progress; Stages 1–6 complete.
## Scope, authority, and execution
Implement the accepted target in the [feature roadmap](promptkit-v0.20.0.md).
That document owns operator intent, selected behavior, OD-1 through OD-11, and
exclusions. This plan owns the transition, API design choices, stage order, and
verification. Read [development](../development.md), the
[agent policy](../policy/agents.md), and applicable policies before each stage.
Use [architecture](../policy/architecture.md), [testing](../policy/testing.md),
[documentation](../policy/documentation.md), [OSS](../policy/oss.md), and
[release](../policy/release.md) for their respective work. The
[ADRs](../adr/README.md) explain the accepted architecture.
Execute the numbered stages in order. Each stage is sized for one focused Luna
implementation prompt. This is one end-to-end sprint: intermediate stages may
fail to compile while dependent callers are being converted. Do not introduce
compatibility layers or temporary public APIs to keep those stages green.
Carry relevant tests and GoDoc with their owning change; defer broad suites
until their dependencies exist. Record completed stages and actual validation
here, commit completed reviewable units under repository policy, and preserve
unrelated work. A stage boundary is not a publication or compatibility boundary.
The inspected baseline is `cb7e05284c554f0da170dc17a0e2e3eac2aed0bc`.
It has core v0.3.0 requirements in both integrations, ckconfig v0.3.0 in
ckprompt, PromptKit v0.18.0, and ConfigKit v1.1.0. The feature roadmap records
the pinned PromptKit v0.20.0 and ConfigKit v1.1.0 evidence. Use those released
sources only. Do not follow unreleased upstream work or change sibling repos.
Use Go 1.27.0 on PATH as described in the [release procedure](../release.md).
Preserve the ignored aligned `go.work`, `go.work.sum`, and local credentials.
Never inspect token contents. Do not commit workspace files, local replacements,
or invented checksums. Core retains its Go 1.26.0 consumer minimum; integrations
retain 1.27.0. During joint development use the aligned workspace. Stages 24–26
explicitly handle released requirements and independent builds.
Public names below select a concrete design, not existing API. Private file
splits and helper names may follow idiomatic implementation needs. If a concrete
API adjustment is necessary, preserve the selected behavior, explain the reason,
and update dependent stages; do not reopen settled scope or silently add policy.
No arbitrary deep-copy, concurrent-dispatch, exact-byte output, transactional
writer, or upstream-domain correctness guarantee is added by this plan.
## Coverage and checkpoints
| Target owner in the feature roadmap | Implementing stages |
| --- | --- |
| Shared reporting, safe conversion, universal formats, and output inventory | 1–4, 7–9, 23 |
| Public CLI JSON and ordered typed bindings | 5–6 |
| Released adoption, resource reports, native errors, and smoke evidence | 8–9 |
| OD-1 prepared preview and OD-2 controls/input/aliases | 10–11, 13 |
| OD-3 namespace defaults and applicability | 12 |
| OD-4 metadata discovery and OD-8 focused inspection | 14, 17–18 |
| OD-5 detailed diff and OD-10 assertion outcomes | 19 |
| OD-6 failure evidence and aggregate validation | 14, 18, 20 |
| OD-9 independent comparison and OD-11 overrides | 15–16 |
| Maintained consumers and canonical contracts | 21–23 |
| OD-7 releases, real dependency alignment, and final validation | 24–26 |
## Stage 1: Shared report representation and safe YAML conversion
**Complete.** JSON normalization and safe YAML conversion are implemented.
Focused conversion tests and the root workspace suite (`GOTOOLCHAIN=local go
test ./...` with Go 1.27.0) passed.
Start in the root package beside [reporter.go](../../reporter.go). Add private
report normalization using `encoding/json`: marshal the original report value,
then decode with `UseNumber` into maps, sequences, scalars, null, and
`json.Number`. Honor JSON tags, omissions, and custom serialization once for
all core-serialized formats. Keep numeric tokens intact instead of routing them
through float64. Strings remain ordinary strings. Unsupported serialization is
an observable error; no stringification fallback.
Add public `DecodeYAMLReport(io.Reader) (any, error)` for one safe inspection
YAML document. Use the existing YAML v4 dependency and a private node walk to
produce ordinary JSON-compatible values. Preserve scalar types, presence,
null/empty distinctions, sequence order, and numeric value. Standard
`json.Number` may retain JSON numeric tokens without imposing Stage 5's CLI
number policy. Convert supported YAML numeric notation faithfully; report an
unrepresentable value rather than rounding or silently treating it as text.
The helper is explicitly for the restricted inspection value model, not a
lossless YAML editor. Reject unsupported meanings, such as a non-string mapping
key or multiple logical documents, with a conversion error. Do not parse report
strings heuristically or read application configuration structs or source files.
Build one private conversion from the normalized representation to YAML nodes,
including numeric nodes rather than quoted numeric strings. This representation
also feeds JSON and automatic shape selection. No public document tree or new
serialization tags.
Verify JSON field/custom-serialization behavior and meaningful scalar, number,
presence, empty-container, and redaction preservation with focused core tests.
Include large integers and strings resembling numbers or booleans. Tests compare
logical values, not YAML quoting or mapping order.
## Stage 2: Writer rendering and standard text templates
**Complete.** Writer rendering and reusable text templates are implemented.
Focused rendering tests and the root workspace suite (`GOTOOLCHAIN=local go
test ./...` with Go 1.27.0) passed.
Add these public root-package contracts:
- `Format` with `FormatText`, `FormatYAML`, and `FormatJSON`; its zero value means
automatic selection for direct writer use.
- `Report{Value any; TextTemplate *template.Template}`.
- `ParseTextTemplate(name, source string, funcs template.FuncMap)
(*template.Template, error)`, using standard `text/template`; nil funcs is valid.
- `RenderReport(io.Writer, Report, Format) error`.
Use one implementation for all formats. Follow the feature roadmap's
[selection table](promptkit-v0.20.0.md#agreed-universal-and-automatic-format-selection):
explicit format first, otherwise a supplied template selects text, otherwise
select by the normalized outer shape. Untemplated strings, numbers, booleans,
and nonempty string lists use plain text; structured values, empty collections,
and null use YAML. Explicit text for that second group uses the same YAML
renderer. For scalar text emit a terminating newline; for string lists emit one
item per line. Execute templated text as supplied without another formatting pass.
Use conventional readable YAML and JSON encoding; whitespace and map order are
not additional contracts. An unknown nonzero writer Format returns an error
rather than selecting a fallback.
Templates receive the original Go value. Templated text does not marshal JSON
first; explicit YAML/JSON does not execute the template. Template preparation,
execution, unsupported serialization, and writer errors remain observable, with
no renderer fallback on failure. Callers can prepare reusable templates during
assembly and attach preparation errors through existing declaration-error APIs.
Use normal template functions initially; no helper framework or public template
value model. Caller-supplied template objects retain ordinary Go ownership.
Verify all automatic shapes, explicit precedence, original-value template access,
JSON-ignored fields in templates, unsupported JSON values, template errors,
large-number YAML/JSON fidelity, and short/error writers. Keep expectations
semantic except where the selected text behavior itself matters.
## Stage 3: Reporter handoff through the existing lifecycle
**Complete.** Reporter handoff uses the existing delivery lifecycle. Focused
reporter tests and the root workspace suite (`GOTOOLCHAIN=local go test ./...`
with Go 1.27.0) passed.
Add `(*Reporter).Report(Report) error`. Delegate to Stage 2 through the existing
`RenderOutput` coordination and destination writer. Store the invocation format
selection in reporting state; do not create another reporter or delivery state.
Keep existing raw writes and `RenderOutput` available as deliberate raw paths.
Preserve quiet-mode rendering through discard, closed-reporter handling,
separate presentation evidence, and existing final outcome precedence. Do not
add a context-cancellation gate to `Report`: handlers decide whether to submit
already-acquired evidence. Do not close or flush caller writers. Avoid recursive
report submission from inside a rendering callback.
Exercise rendering errors under quiet mode, a successful operation with delivery
failure, a nonzero domain outcome with delivery failure, supported cause identity,
and submission of acquired content after cancellation. Reuse existing reporter
and outcome coverage where unchanged. Document the public handoff and raw-output
boundary in GoDoc.
## Stage 4: Universal format input, core version, and a core consumer
**Complete.** Core format selection, version reporting, and a maintained core
consumer use the shared report path. Focused parser, routing, completion, and
reporting tests and the root workspace suite (`GOTOOLCHAIN=local go test ./...`
with Go 1.27.0) passed. Integration migrations remain in later stages.
Extend neutral core flag metadata and parser selection in
[metadata.go](../../internal/commandmeta/metadata.go),
[parser.go](../../parser.go), and the internal Cobra adapter. Add core-owned
`--format` with `text`, `yaml`, and `json` completion. Omission means automatic;
explicit empty or unknown formats are usage errors before input factories,
acquisition, or handlers. Validate supplied occurrences using normal singleton
conversion behavior; the last valid supplied choice selects the format. Preserve
entered spellings, consumed values, `--`, reporting-mode ordering, and fresh
invocation state. Do not treat the value-taking flag as a boolean when generating
completion or reducing core flag occurrences.
Use existing core-flag placement and alias mechanisms. Reserve the long name
`format` at installation, with no built-in short spelling. A local binding or
conflicting alias source produces the existing installation collision error;
a deliberate alias to core `--format`, such as `--fmt`, remains supported.
Remove the version command's local format binding and switch its report to
`Report` plus an ordinary version text template. Do not encode help, usage,
completion protocol output, or diagnostic messages as reports.
Convert one maintained [core consumer](../../examples/core) command to a data
handoff and optional template, with no format switch. Exercise core/application
reports in all three formats, unknown formats before domain work, format aliases,
metadata/help without acquisition, reservation collisions, and repeated
invocations. Update the core CLI and consumer reporting sections with the landed
boundary; later documentation stages complete the integration inventory.
## Stage 5: Public CLI JSON parsing
**Complete.** CLI JSON parsing and numeric conversion are implemented. Focused
boundary tests and the root workspace suite (`GOTOOLCHAIN=local go test ./...`
with Go 1.27.0) passed.
Add `ParseCLIJSON(string) (any, error)` in core. Decode exactly one JSON value
with `UseNumber`, require completion of that value, and recursively convert
numbers under [OD-2](promptkit-v0.20.0.md#od-2-request-controls-and-provider-parameter-grammar).
Integer syntax becomes int64, or uint64 for representable nonnegative overflow;
decimal/exponent syntax becomes float64. Return syntax and numeric range errors;
accept ordinary float approximation. Preserve nested arrays, objects, null,
booleans, and strings. Keep conversion and its required policy/rationale comments
in one private CLI helper. This parser is separate from report normalization.
Test nested values, signed/unsigned integer boundaries, out-of-range values,
decimal and exponent syntax, floating range failures, trailing JSON, and ordinary
float approximation. Do not add arbitrary-precision types or a public numeric
conversion API. Each integration will enforce its flag's outer shape.
## Stage 6: Ordered typed occurrences and reusable binding identity
**Complete.** Ordered typed occurrences and independently renamed bindings are
implemented, including captured subset replay. Focused binding tests and the
root workspace suite (`GOTOOLCHAIN=local go test ./...` with Go 1.27.0) passed.
Extend [binding.go](../../binding.go), [flag_values.go](../../flag_values.go),
and the neutral preparation path to retain ordinal positions from existing
`FlagOccurrences`. Add:
- `OccurrenceFlag[I,V](FlagMeta, func(string) (V,error),
func(*I, Supplied[V])) Flag[I]`: repeatable, converted once per supplied
occurrence, no fallback occurrences; invokes its setter once per occurrence.
- `RenameFlag[I](Flag[I], name string) Flag[I]`: independent binding identity,
same parser/setter/default/required/completion behavior, replacement long name,
and no inherited short spelling. Normal declaration validation still applies.
Apply participating occurrence bindings in actual command-line order across
canonical spellings and aliases. Ordinary bindings keep their existing grouped
semantics. During input construction apply ordinary flag values, then ordered
occurrences, then operands, before `CheckInput`. The relative ordering contract
is needed among participating occurrence bindings, not between arbitrary caller
side effects. All conversion failures still occur before input construction.
Extend private prepared values with typed occurrence closures and ordinals.
`CaptureFlags` retains those records against original binding identities;
`FlagValues.ApplyTo` checks the selected subset and required presence, then
replays only its participating records in order without parsing again. Repeated
applications retain independent core-owned parsed collections. Preserve existing
wiring-versus-usage classification; expose neither raw arguments nor backend
objects. `RenameFlag` must preserve ordered behavior when used on such a binding.
Verify interleaved distinct flags and aliases, repeated names, explicit empty
values, omitted bindings, subset selection, conversion failure, required checks,
renamed handles, repeated application, and fresh invocations. Do not change the
ordinary `RepeatFlag` replacement semantics to simulate ordering.
## Stage 7: Migrate existing ckconfig reports to core
Convert the existing `scopes`, `show`, `sources`, `explain`, `diff`, and concise
`validate` report paths in [ckconfig](../../integrations/ckconfig) to the public
handoff. Remove local format flags, validators, encoding switches, and encoders.
Give integration-owned projections JSON tags for their selected content; thin
path/location/snapshot projections remain appropriate.
For effective display, capture native `WriteEffective` output, call
`DecodeYAMLReport`, and hand off that logical value without a text template.
For `--scope all`, acquire all requested views and complete required validation
before output; combine their safe values into one map keyed by scope ID, even
with one registered scope. Single-scope output is unwrapped. Retire document
markers and scope-comment framing; do not promise mapping order. An acquisition
failure prevents the combined display. Conversion/rendering failures use the
presentation path, including under quiet mode.
Keep current content where the target does not change it. Use concise text
templates for scopes/source inventories and existing explanation summaries when
appropriate; default diff remains a structured mapping without a template.
For concise validation, collect reached-scope results before one submission;
stop acquisition on failure/cancellation and preserve its outcome. Stage 20
extends that same aggregation with structured evidence rather than introducing
another validation delivery path.
Verify universal formats, safe-value conversion, aggregate shape, selected
single-string output, provider failure before combined display, and separate
presentation failures. Replace obsolete native-byte/document-order assertions
and update current display/validation contracts and the corresponding testing
policy wording as behavior changes. Do not add domain comparison tests.
## Stage 8: Adopt PromptKit v0.20.0 and native resource inspection
Update ckprompt's PromptKit requirement and authentic sums to v0.20.0; keep
ConfigKit v1.1.0 and existing Go minima. Review the released notices and update
[module attribution](../../integrations/ckprompt/docs/oss/attribution.md) and
applicable transitive notices. Do not fabricate future CommandKit dependency sums.
Implement the [resource inspection target](promptkit-v0.20.0.md#resource-inspection).
Remove duplicated prompt/profile report structs and per-field formatters; pass
the native `PromptInspection` and `ProfileInspection` to core, without templates.
Add `BackendRequest{Selection ckconfig.Selection; ID string}` and an independently
optional `Suite.InspectBackend` callback returning
`commandkit.Acquired[*promptkit.BackendInspection]`. Wire `FromEngine` through
native `InspectBackend`, and install `show backend BACKEND_ID` when available.
Stage 12 adds the common namespace field to these requests.
Use native JSON omission/presence and returned captured messages. Do not complete
profile settings, infer readiness, resolve credentials, or reconstruct provenance
in these commands. Preserve errors, including native backend-not-found identity,
acquisition diagnostics, cancellation, and presentation failures. Adapt only
fixtures and callers affected by the released API; retain legitimate collection
release/version evidence.
Verify native handoff fidelity across all formats, independent callback absence,
no loading for help/completion, positional IDs, omitted versus explicit resource
fields, native errors, and cancellation. Assert adaptation against returned
native evidence rather than rechecking upstream inheritance or rendering.
## Stage 9: Consolidate remaining ckprompt reports
Move requirements show/validate, all catalogs, and smoke reports onto core
rendering. Remove remaining local format bindings, allowlists, encoders, and
format-specific report paths. Preserve selected report content with JSON tags
and text templates where a concise text view already serves operators. Keep
smoke reports as structured values without a dedicated text template.
Single smoke reports retain native returned evidence, even with an operation
error. `test all` submits one report containing its ordered reached test results;
stop under the existing operation/cancellation lifecycle and retain partial
results. Core does not aggregate tests. Do not drop an acquired report merely
because the context became canceled; do not fabricate a report when native
preparation returned none. Preserve authentication response-limit unknown facts,
preparation-error causes, and operational failures. Findings that are false,
unknown, unsupported, or incomplete do not independently change command status.
Verify one JSON document for aggregation, partial report plus error, no-report
preparation errors, cancellation after evidence acquisition, quiet rendering,
and delivery failure without losing domain evidence. Use deterministic native
fixtures/test doubles rather than network probes. Update current report guidance
for the converted commands.
## Stage 10: Reusable execution and resource flag collections
Implement CLI-specific collections in ckprompt using ordinary public core flags.
Use typed flag-set structs exposing named original `Flag[I]` handles and an
`All() []Flag[I]` convenience method; callers select subsets with ordinary slices.
Constructors take a destination accessor into the application's own input type.
This avoids a new core collection API, string-keyed value lookup, or enum-driven
request builder.
Select these collection/value boundaries:
- `ExecutionInput` holds native `*ExecutionTargetOverride` and `*int`
transient retries. `NewExecutionFlags` supplies the eleven execution controls
from OD-2 through named handles. Allocate the native override only when a
relevant value is supplied; retain pointer zero/empty values and upstream
scalar semantics.
- `ResourceInput` holds presence-aware prompt/profile/backend/session IDs.
`NewResourceFlags` exposes those handles independently.
- `PromptInputs` holds native variables and artifact-reference maps.
`NewPromptInputFlags` supplies variable/text/file handles; Stage 11 fills in
ordered text/file construction. No application-internal execution helper is
introduced.
Use canonical names from the [OD-2 table](promptkit-v0.20.0.md#od-2-request-controls-and-provider-parameter-grammar).
Define each name, usage, and alias relationship once in ckprompt metadata reused
by Stage 13. Preserve existing scalar syntax and integer timeout units.
`--llm-extra-params` is a singleton object parsed by `ParseCLIJSON`; reject a
non-object through the usage path and forward an explicit empty object unchanged.
Leave upstream value validation and whole-map replacement to PromptKit.
Use execution controls on validation, preview, and smoke. Profile selection is
applicable to all three; prompt selection remains positional for validation and
preview. Backend selection is for native smoke requests; ordinary RunRequest has
no backend-selection field. Session, variables, and prompt inputs apply only to
the request operations that consume them. Requirements show uses only its
profile/session/variable context. Resource inspection gets no execution flags.
Retain authoritative application builders: seed the native request once, allow
a complete replacement, and never reapply flags afterward. Preserve nil versus
explicit retries and the different native ordinary/smoke defaults. Verify
collection use with an application-owned input, selected subsets, zero/empty
forwarding, JSON object parsing, and builder replacement at the boundary.
## Stage 11: Ordered literal/file inputs and concrete preview
Use `OccurrenceFlag` for named text/file assignments. Split at the first `=`,
preserve literal empty text, construct native `Inline` or `File` references, and
assign into the same `PromptInputs.Inputs` map as occurrences are replayed.
The last occurrence per name wins across both forms. Use canonical flags and
aliases exactly as OD-2: `--input` now denotes literal text; file access uses
`--llm-input-file` or its later scoped alias. Do not infer files from text.
No prompt-input flags go onto smoke tests.
Add `PreparationRequest` carrying selection and native RunRequest, an optional
`Suite.PrepareRequest` callback returning `Acquired[*promptkit.PreparedRun]`, and
`WithRequestPreparation` taking the same loader/builder pattern as
`WithRequestValidation`. Install `show prepared PROMPT_ID` independently of
validation/resource inspection. Forward one final built request to native
`Engine.Prepare` exactly once; do not call ValidateRequest first or retain a
prepared execution handle. Hand off native PreparedRun without a template.
Stage 12 extends the common request envelope with namespace selection.
Reuse input/state preparation and diagnostics. Explain in command help that
preparation does resource I/O and reports rendered application content without
calling the model. No execution-success or public-disclosure guarantee.
Verify alternating text/file occurrences, duplicate names, aliases after Stage 13,
empty literals, values containing `=`, fresh invocations, builder replacement,
preview callback independence, request/context forwarding, native result/error
identity, and no model invocation. Derive expected preview evidence from the
native result rather than asserting PromptKit's rendering algorithm.
## Stage 12: Opt-in namespace composition
Add `WithNamespaces(suite, promptkit.Namespaces) Suite` and store explicit
application defaults in an optional suite field. Add `Namespaces
*promptkit.Namespaces` to applicable ckprompt request envelopes, including
preparation and smoke. Nil means leave the loader's view untouched. With the
option enabled, start with its declared defaults and replace only supplied CLI
fields, including explicit empty clearing; populate the final native value in
the operation request. Native adapters apply one `Engine.WithNamespaces` call
after loading and before the selected upstream operation.
Implement only the flags in [OD-3's applicability table](promptkit-v0.20.0.md#od-3-namespace-flags-and-loader-defaults).
Keep catalog filters and global configuration inspection separate. `FromEngine`
callbacks and validation/preparation/smoke adapters consume request namespace
selection consistently; custom callbacks receive the selection without an extra
hidden loader. Builders retain authority over their native requests. Loaders
using the standard option leave view-default selection to the adapter; loaders
without it remain usable as before.
Explain native requirements profile identity in help, including explicit short
profile selection versus the prompt's stored default reference. Do not rewrite
configured references or try to make discovery profile-independent.
Verify defaults plus one override preserve unrelated defaults, explicit clearing,
qualified IDs, applicable flag availability, custom loader behavior without the
option, and native view/error forwarding. Test boundary equivalence, not native
namespace membership or requirements-analysis correctness.
## Stage 13: Generated scoped aliases and complete-family installation
Add `Declarations(suite) []commandkit.Declaration`, assembling the canonical
family and generated exact-command `FlagAliasFor` rules. Retain `Command(suite)`
for canonical-only installation. Generate aliases from the selected flags and
shared metadata built in Stages 10–12, following OD-2/OD-3; do not keep a separate
command-by-flag matrix. Generate only for installed commands and applicable
bindings. No aliases are application-wide by default.
Use the existing routing/installation boundary, original binding identities,
and conflict checks. Application-owned commands can install deliberate aliases
through the public core API. Do not add subtree scopes, wrapper commands, or a
new declaration-bundle type. Canonical-only help must not claim convenience
aliases that were never installed.
Exercise convenient and canonical-only assembly, optional callback subsets,
command aliases with scoped flag aliases, unknown/inapplicable names, native
binding presence, mixed-alias occurrence order, and ordinary installation
conflicts. Metadata/help/completion remain independent of engine acquisition.
## Stage 14: Metadata and failed-acquisition evidence facets
Extend [ckconfig contracts](../../integrations/ckconfig/contracts.go) with
`Scope.Features ScopeFeatures`: `PathDiscovery`, `PathShow`, and `Overrides`
booleans. These describe optional modes within existing commands, not new
subcommands. Native adapters advertise the first two; overrides remain explicit
loader opt-in in Stage 16. Retain existing `Operations` and the six-command
family. Capture features with the suite, check requested mode availability
before acquisition, and include supported feature names in scope discovery/help.
A declared mode missing its required acquired facet is an operational provider
wiring error, not an empty successful report. Detailed validation itself remains
available on validation scopes and can report unavailable native findings.
Add `View.Metadata *configkit.Metadata` and `View.Findings *Findings`, with
`Findings{Records []configkit.Diagnostic; Messages []commandkit.Diagnostic}`.
Nil findings means unavailable; a nonnil empty record set means acquired but
empty. Existing `View.Sources` supplies optional inventory, including after an
error. Keep metadata as a deliberate upstream public value dependency; no
parallel metadata engine or generic diagnostic framework.
Put native-generated ordinary warning presentations in `Findings.Messages`,
with their known association to the native record set. Keep unrelated application
messages in `Acquired.Diagnostics`. Ordinary handlers queue both groups;
detailed validation reports native records and queues only unrelated messages,
so filtering does not produce duplicate ordinary warnings. Keep the existing
terminal explanation, even when it describes a finding. Do not deduplicate by
message text or discard caller messages with no known relationship.
Update `FromConfigKit` and `ckprompt.ConfigScope`: successful loads expose native
safe writers, sources, metadata, and findings. A matched `*configkit.LoadError`
exposes its captured Sources/Diagnostics alongside the original error. Use that
error snapshot as the failure evidence rather than appending a duplicate result
inventory; do not expose effective configuration or metadata from a failed load.
Other failures may have no structured evidence. Keep unavailable distinct from
available-empty inventory via the optional source reader. Preserve error chains
and existing diagnostic severity/terminal ownership. Ordinary PromptKit loading
outside ConfigScope continues its normal diagnostic delivery.
Update acquisition-report helpers to queue native presentations from their new
owner without changing unaffected operations. Test both native adapters, success,
matched/wrapped errors, empty versus unavailable evidence, no effective facet on
failure, independently supplied diagnostics, and metadata/help without loading.
## Stage 15: Independent comparison selector inputs
Replace inheritance in [input.go](../../integrations/ckconfig/input.go).
Add `Selector.AgainstName string`; an empty name declares no right-side flag.
For a declared counterpart use Stage 6's `RenameFlag` to retain domain parsing
and setter behavior with a fresh identity and no copied short alias. Capture
left and right values into distinct `FlagValues[I]` stores. Validate name
collisions through normal assembly; do not auto-generate counterpart names.
Prepare each side with a separate `NewInput`, applying only that side's bindings
and selection. Omitted fields preserve the factory's ordinary defaults and
unsupplied state. Remove `inheritComparisonSelection`; config and overlay
counterparts also start independently. No selector value or repeatable collection
is copied to the opposite side, including when that side is partly supplied.
Required checks apply to the applicable declared subset on its side. Complete
both input checks before one `NewState` and the existing pair provider call.
Command-wide scope/format/detail/assertion controls stay outside side inputs.
Equal selections still acquire both sides and compare.
Verify OD-9's left-only/right-only/both/neither and partial-side cases, custom
repeated selectors, required inputs, supplied state, fresh invocation storage,
shared state, and checks before acquisition. Keep application factory objects
and caches application-owned; no arbitrary deep copying. Update selector GoDoc
and replace the canonical comparison inheritance description with the selected
model. Explicit-empty custom collections remain application-defined.
## Stage 16: Opt-in transient override acquisition
Add `Selection.Overrides []configkit.Override`. Preserve ordered records; nil
means no CLI records. A loader opting in via `scope.Features.Overrides = true`
reads those records during its ordinary load and supplies them to ConfigKit.
Reuse the existing loader signature instead of adding a second loader or trying
to modify an already-loaded result. `FromConfigKit` and `ConfigScope` do not
claim that arbitrary application loaders consume overrides automatically.
Install repeatable `--set POINTER=JSON` on acquisition commands (`validate`,
`show`, `sources`, `explain`, and `diff`), and `--against-set` on comparisons.
Scope discovery gets no acquisition flags. Parse with a shared ckconfig assignment
helper: split at the first `=`, parse the pointer with native ConfigKit path
syntax, parse one JSON value with `ParseCLIJSON`, and set the safe provenance
label to `--set` or `--against-set`. Syntax/JSON conversion failures are usage
errors. Native restrictions on admissible override paths and combinations remain
load-time ConfigKit errors. Do not prevalidate root/index/schema restrictions,
deduplicate, collapse records, resolve overlaps, or retry a rejected load.
Only loaders that declared support receive installed override flags for their
scope; reject an unsupported selected mode before acquisition, including mixed
aggregate scope coverage. Supply the same CLI records independently to each
selected ordinary scope that accepts them. Copy CLI-owned record slices and
parsed JSON containers when applied to independently prepared selections; do not
copy arbitrary application runtime objects. For comparison, left and right lists
are independent under OD-9; absence never erases application-owned overrides or
inherits the other side's CLI records. No new clearing flag.
Document the unsupported `=` in pointer keys and preserve `=` in JSON values.
Verify nested typed values, empty/false/zero/null, duplicate/overlapping records
forwarded in order, safe labels, capability availability, comparison separation,
and propagation of native load errors/provenance. Domain precedence and override
algorithms remain upstream tests.
## Stage 17: Explanation discovery and metadata
Extend `config explain` under [OD-4](promptkit-v0.20.0.md#od-4-configuration-path-and-metadata-presentation).
Track supplied `--path` separately from its value; root `--path ''` is an
operation. Bare invocation shows successful command help before input/state
factories or acquisition. Select `--list` or `--path`, with both a usage error.
List-only flags are `--historical`, `--under POINTER`, and
`--explicit=true|false`; reject their use in path mode or without list selection
as an invalid combination rather than silently ignoring them.
For listing, require declared PathDiscovery and acquired metadata. Enumerate
native HistoricalPaths, use Lookup for current presence and explicitness, and
include historical-only paths only when requested. Obtain History even when
Lookup is absent where detailed explanation requires it. Represent unavailable
current metadata separately from false explicitness. Do not label all
non-explicit values as defaults or enumerate schema fields that never existed.
Use parsed pointer token boundaries for subtree tests, including root and escaped
keys. A small private pointer-token comparison can consume native paths' String
representations; it must not invent another domain path parser or equate textual
prefixes. Retain native paths and list indexes. Historical-only entries match
neither current explicitness filter. Native path enumeration order is sufficient;
do not add a new ordering guarantee.
Use a compact discovery report `{scope, paths: [...]}` with per-entry path,
present, and optional metadata containing explicitness when current. Attach a
compact text template; values/history stay out of the list. Extend path-specific
explanation's existing report with native available field metadata, avoiding
repeated evidence. Existing explanation remains usable when PathDiscovery is
absent. Update help, GoDoc, and focused tests for mode/presence/filter behavior,
capabilities, historical-only evidence, and result delivery.
## Stage 18: Focused effective values and source evidence
Add `config show --path POINTER` for one scope with PathShow support. Parse the
pointer before loading, call the acquired native `Explain`, and select its safe
effective snapshot. An absent effective path is operational failure; explicitly
present null succeeds. A historical-only explanation does not supply a current
value. Convert the snapshot YAML through core and submit only the selected value,
without snapshot wrappers or a text template. Whole-document aggregate show
continues Stage 7's map; path plus `--scope all` is a usage error.
Add `config sources --source-id ID` to existing Sources capability. Filter the
acquired inventory rather than invoking another loader. Retain the ordinary
source report envelope and return its selected single entry; unknown IDs are
operational failures. On acquisition error, report available captured source
inventory, filtered when requested, while preserving the acquisition failure.
If a filter also misses during a failed load, preserve the original cause and
include the selection problem in the explanation rather than replacing the load
error. If evidence is unavailable, say so; do not fabricate an empty inventory.
Source IDs are local to this acquisition. Preserve native locations, version
and destination evidence, and redaction in the existing thin source projection.
Verify mapping/string/false/zero/null focused display across formats, missing
and historical-only paths, optional capability failure, known/unknown source
selection, available-empty versus unavailable failure evidence, native cause
identity, and presentation failure alongside acquisition failure. Do not read
source files again to recover evidence.
## Stage 19: Detailed differences and assertion outcomes
Reuse existing Scope.Compare and native Difference records. Keep concise
additions/changes/deletions as default and add `--details`. The detailed report
is `{scope, differences: [...]}`; entries carry native `path`, `before`, `after`,
and `hidden_changed`. Each snapshot has explicit `present`, `redacted`, and a
safe decoded `value` (null when absent, distinguished by present=false).
Decode only present native snapshots; do not interpret marker-looking strings
as redaction. Use one projection path and ordinary JSON tags, no mirrored
upstream graph or custom omission framework. Empty differences is `[]`, not null.
Both modes have no text template, so automatic output is YAML.
Add opt-in `--check`. Use native record presence, including concealed changes,
to return a domain outcome with status 3 for differences; equality stays status 0.
Usage and operational errors retain 2 and 1 respectively. Without --check,
successful differences remain success. Render the report before returning the
chosen operation outcome. Preserve a nonzero domain result if presentation also
fails, retaining separate presentation evidence under core outcome rules.
Verify the same provider calls/loads serve concise and detailed views, native
additions/deletions, null versus absence, hidden changes with equal safe values,
empty results, sequence snapshots remaining atomic, all formats, and assertion
outcomes with writer/render failure. Do not compute equality from displayed YAML,
add a comparison callback, reload data, or implement list matching.
## Stage 20: Detailed validation and reached-scope aggregation
Complete Stage 7's validation aggregation using Stage 14's evidence. Add
`--details`, repeatable `--severity`, and repeatable `--code` on validation;
filters require detailed mode. Parse severity using native known severities;
codes remain exact native code strings, including future/unmatched strings.
Within one filter match any selected value; across filters match both. Filter
only presentation, never operation outcome.
Acquire scopes in existing order, stopping at first acquisition failure or
cancellation. Retain completed-scope results and available evidence from the
failing/reached scope. Do not create records claiming unvisited scopes passed.
Submit once after acquisition stops, even if cancellation caused the stop. This
handler must not use an ordinary inspection helper's pre-submission cancellation
guard. Keep native-generated messages in their existing diagnostic lifecycle;
aggregate report timing does not buffer the whole invocation.
Use one report `{scopes: [...]}` in concise and detailed modes. Each entry names
the scope and its outcome (`valid`, `failed`, or `canceled`); detailed entries
add findings adapted from native records with safe source references, and
distinguish `unavailable`, `empty`, `filtered`, and `present` evidence states.
Use existing thin path/location/source projections, converting native Paths to
operator pointer strings instead of serializing their private storage. Keep
required sources for selected findings, including related locations. Apply the
known-group warning coordination from Stage 14, preserving
unrelated messages and the terminal failure explanation. Concise mode retains
ordinary warnings and a compact text template; detailed mode has the selected
readable text template with scope/outcome followed by findings and locations.
Explicit YAML/JSON renders that same selected report value.
A failed load establishes no effective configuration. Successfully rendering its
evidence never changes failure to success. Preserve cancellation/load cause and
separate presentation failure. Output errors occur after acquisition has stopped
and cannot undo loads or stop loads already completed. Update the CLI and tests
that formerly stopped on per-scope output failures.
Verify success, warnings, matched load failure, unavailable/empty evidence,
filters with no matches, source references, known duplicate suppression,
unrelated warnings, aggregate failure/cancellation, one JSON document, quiet
mode, and combined operation/presentation failures. Avoid exact cosmetic
snapshots and upstream validation-rule tests.
## Stage 21: Simple consumer and core/configuration API exercise
Update the maintained [simple ckconfig consumer](../../integrations/ckconfig/examples/simple)
to install an explicitly configured FromConfigKit scope with Overrides enabled,
replacing AppCommand convenience wiring there. Append the selected native CLI
records to its ordinary ConfigKit Options.Overrides during loading, leaving
native conflict handling intact. Declare explicit right-side names for any
selected application selectors. Keep ordinary configuration discovery/default
behavior on omitted sides. Demonstrate metadata
listing, focused values, detailed validation, source filtering, detailed diff,
and assertions through the standard family, not custom duplicate handlers.
Use the existing before/after fixtures and add `fixtures/invalid.yml` with a
valid schema header plus `greeting: [invalid]`, which cannot decode into the
example string field. Use it for acquired-source and diagnostic delivery, not
to independently test upstream type validation. Extend existing consumer tests
for a representative normal task, a failed-load evidence task, and an
independent comparison/override task. Reuse
lower-layer parsing and rendering coverage; do not duplicate the full mode
matrix here. Show a structured application report and text template in the core
consumer without optional integration imports. Ensure metadata-only operations
do not initialize application runtime state.
## Stage 22: Composed consumer and application-owned flag reuse
Update the maintained [composed consumer](../../integrations/ckprompt/examples/composed)
to install `ckprompt.Declarations`, explicit empty native namespace defaults
(enabling root defaults plus opt-in selectors), request preparation, extended
ConfigScope facets, and explicit `against-campaign`/`against-limit` comparison
bindings for their existing applicable scopes. Opt the application scope into
overrides and append its CLI records to ordinary ConfigKit overrides; native
duplicate/overlap errors remain native errors. Reuse the public
CLI flag collections on an application-owned command with its own input type,
showing canonical-only behavior there unless it deliberately installs aliases.
Do not introduce application-internal upstream convenience wrappers.
Keep the PromptKit configuration scope's Overrides feature false in this
consumer. Released PromptKit Config/ConfigurationSources accept sources but do
not expose a ConfigKit Options.Overrides handoff. OD-11 is explicitly a loader
opt-in; it does not require simulating missing native engine support through
source rewriting. All native inspection/evidence features remain enabled.
Application-owned loaders can declare override support when their own acquisition
boundary actually accepts those records. Aggregate --set preflight must account
for this scope difference rather than dropping an unsupported scope silently.
Keep successful-load caching application-owned and based on each side's complete
selection, overrides, and supplied domain inputs. A source-selection-only cache
must not incorrectly reuse a result for different overrides or comparison sides.
Use a small invocation-local cache-entry list and `reflect.DeepEqual` on owned
selections and relevant supplied domain fields, retaining those inputs with each
entry. Do not invent a universal hash scheme or cache across invocations.
Retain invocation state lifetime and do not cache failed loads as successes.
Update request fixtures to use released resource/source contracts and explicit
file-input names. Include short inline input, preview, namespaces, and effective
provider parameters. Keep the native default bundle and existing domain commands
unless the selected target changes their usage; do not freeze catalog counts.
Retain the local fake LLM/probe setup and native finding-versus-operation outcome
semantics. No external credentials or model services in default examples/tests.
Exercise representative config and prompt workflows, aliases and presence,
builder replacement, rendered preview, failure evidence, independent campaign
selections with overrides, ordinary domain dispatch, and help/version without
loading. Expected native evidence should derive from the selected engine/result;
do not reimplement native resolution tests.
## Stage 23: Canonical contracts and obsolete infrastructure audit
Review every path in the feature roadmap's
[output inventory](promptkit-v0.20.0.md#output-implementation-inventory).
Remove remaining handler-local format inputs, validators, encoders, legacy
prompt/profile mirrors, inherited comparison helpers, and old document framing.
Do not route raw help, diagnostics, shell protocols, or intentional caller raw
writes through report serialization. Remove direct integration YAML dependencies
only if no legitimate domain/fixture usage remains after `go mod tidy`.
Complete GoDoc and [CLI reference](../cli.md) updates: universal formats,
representation and text-template behavior, native resources versus prepared
content, flags and scoped aliases, mode/capability availability, metadata and
failure evidence, independent selections/overrides, outcomes, cancellation, and
changed aggregate show/validation delivery. Remove obsolete migration sections
and legacy variants from current guidance as required by the clean-adoption
scope. Historical released notes and tags stay immutable.
Update the [consumer guide](../consumers/guide.md), current internal inventory,
applicable README/development examples, dependency guidance, and testing policy's
obsolete native-YAML/order requirements. Refer to upstream for domain semantics
rather than copying a parallel specification. Align architecture-policy wording
that describes the former shared comparison selectors with the deliberate new
model; do not add unrelated policy changes or successor ADRs for these
presentation/selector choices. Use maintained examples as complete artifacts.
Check scope against all roadmap completion criteria and deferred exclusions.
Perform focused documentation/link/fragment, whitespace, notices, and API-boundary
review. No new generic report framework, runtime wrappers, output-contract or
appended-message flags, persistence, live probes, discovery tracing, or secret
bootstrap work enters this sprint.
## Stage 24: Core release candidate and publication handoff
Inspect authoritative release state under the [release procedure](../release.md)
before selecting versions. Use the next unused core minor release (expected
v0.4.0 if still unused), a later ckconfig minor release (expected v0.4.0), and
update ckprompt v0.3.0 only if it remains unpublished. If that candidate has been
published, preserve it and select the next unused minor instead. These are
mechanical applications of the existing release policy, not new product decisions.
Record actual versions and source commits here as they become available.
Prepare the core note, notices, and canonical contracts for the full shared
reporting/input API, with immutable footer links. Explain changed core contracts
concisely; no compatibility adapter or separate migration guide. Use the selected
release toolchain for root standalone tests and the normal candidate preparation.
Commit the reviewed candidate before any publication.
Core publication is a maintainer handoff requiring separate explicit
authorization. This plan does not authorize push, tags, publication, or hosted
delivery. Without that authorization, finish all available workspace development
and candidate work, report the release dependency pending, and do not claim
final independent integration validation. Do not add polling, CI gates, or an
alternative fake module release to bypass this handoff. After authorized source
publication, proceed to Stage 25. Hosted delivery remains a separate operation.
## Stage 25: Released core alignment and ckconfig candidate
Pin the actual released core version supplying Stages 1–6 in both integrations.
Download ckconfig dependencies with GOWORK off and collect authentic sums; tidy
that module with the selected toolchain. Defer ckprompt standalone preparation
until Stage 26 supplies its released ckconfig requirement. Keep ConfigKit v1.1.0
and PromptKit v0.20.0.
Verify core's module graph still has no optional integration dependency.
Run ckconfig's standalone suite with released core and its maintained simple
consumer. Prepare and commit the ckconfig minor note, attribution, dependency
metadata, and applicable guidance. The note covers shared reporting, new CLI
modes/override opt-in, independent selections, and assertions directly, with the
module-specific immutable footer. Do not edit older released notes.
Publication of this candidate is the second separately authorized maintainer
handoff. Until it occurs, ckprompt workspace results remain useful but do not
prove its independent dependency graph. Preserve that distinction in status.
After authorized ckconfig source publication, continue with its real released
version; hosted delivery remains separately tracked under the release procedure.
## Stage 26: Final ckprompt alignment and end-to-end verification
Pin released core and ckconfig versions providing the new APIs in ckprompt.
Download and tidy with GOWORK off; retain real sums. Update the unpublished
ckprompt candidate note (or fresh minor selected in Stage 24) with the final
capabilities and pinned dependencies, correct product/version title, and
module-specific immutable licensing footer. Record final module identities here.
No code may depend on the temporary workspace to hide old requirements.
With Go 1.27.0 selected, run the independent suites from the repository root:
```sh
GOWORK=off GOTOOLCHAIN=local go test ./...
(cd integrations/ckconfig && GOWORK=off GOTOOLCHAIN=local go test ./...)
(cd integrations/ckprompt && GOWORK=off GOTOOLCHAIN=local go test ./...)
```
Then validate the aligned workspace:
```sh
GOTOOLCHAIN=local go test ./...
GOTOOLCHAIN=local go test ./integrations/ckconfig/...
GOTOOLCHAIN=local go test ./integrations/ckprompt/...
```
Confirm tidy produces no remaining module changes after preparation. Format
changed Go files and check public facade/backend import direction, documentation
links/fragments, attribution pins, intended files, and staged/unstaged whitespace.
Use graph tools first for code discovery and ordinary searches for literals,
configuration, and docs. Reuse successful checks for unchanged content; root
`./...` alone is not integration coverage.
Run maintained consumers outside the workspace for these representative tasks,
using their final installed features and fixtures:
- Automatic and explicit text/YAML/JSON application/version reports.
- Whole/aggregate/focused config display; metadata listing and root explanation.
- Independent campaign/config comparisons, --details, --check, and both override
sides, including zero/null/list values through standard syntax.
- Invalid-load sources and detailed validation retaining a nonzero operation
outcome, and all-scope validation producing one reached-scope report.
- Native prompt/profile/backend inspection; prepared preview with inline and file
inputs, aliases, namespaces, and provider parameters; requirements validation.
- A deterministic single smoke and `test all` with the configured local client,
retaining native findings and separate operation/presentation semantics.
From `integrations/ckconfig/examples/simple`, representative final invocations
are:
```sh
GOWORK=off GOTOOLCHAIN=local go run . config explain --list
GOWORK=off GOTOOLCHAIN=local go run . config show --path /greeting --format json
GOWORK=off GOTOOLCHAIN=local go run . config diff --config fixtures/before.yml --against-config fixtures/after.yml --details
GOWORK=off GOTOOLCHAIN=local go run . config validate --config fixtures/invalid.yml --details --format json
GOWORK=off GOTOOLCHAIN=local go run . config sources --config fixtures/invalid.yml
```
The invalid-load invocations intentionally fail while reporting available
evidence. From `integrations/ckprompt/examples/composed`, run:
```sh
GOWORK=off GOTOOLCHAIN=local go run . config show --scope all --format json
GOWORK=off GOTOOLCHAIN=local go run . config validate --scope all --campaign spring --limit=0 --details
GOWORK=off GOTOOLCHAIN=local go run . config diff --scope app --campaign spring --limit=0 --against-campaign autumn --against-limit=0 --details
GOWORK=off GOTOOLCHAIN=local go run . config diff --scope app --set '/campaigns/spring/model="left"' --against-set '/campaigns/spring/model="right"' --details
GOWORK=off GOTOOLCHAIN=local go run . promptkit show prompt briefing --campaign spring --prompt-namespace field-notes
GOWORK=off GOTOOLCHAIN=local go run . promptkit show profile spring --campaign spring
GOWORK=off GOTOOLCHAIN=local go run . promptkit show backend local --campaign spring
GOWORK=off GOTOOLCHAIN=local go run . promptkit show prepared briefing --campaign spring --input topic=sprint --profile spring --extra-params '{"campaign":"preview"}'
GOWORK=off GOTOOLCHAIN=local go run . promptkit requirements validate briefing --campaign spring --input-file topic=fixtures/topic.txt
GOWORK=off GOTOOLCHAIN=local go run . promptkit test all --campaign spring --prompt briefing --profile spring
```
Record actual outcomes and final fixture identities. Use existing consumer tests
to assert native application statuses (including --check status 3); the `go run`
wrapper itself does not preserve a child program's nonzero status code. Existing
focused unit/integration tests own injected cancellation and writer failures;
do not build another exhaustive consumer harness. Default verification stays offline
after dependencies are prepared.
Audit each completed stage against its behavior and the feature roadmap, remove
leftover migration scaffolding, review the complete diff, and commit the final
candidate. Keep feature/implementation status accurate and preserve deferred
items in future.md when retiring completed records under repository policy.
ckprompt publication and hosted delivery remain separate authorized operations;
preparing this candidate does not perform them. Do not mark the full dependency
alignment complete until both preceding release handoffs and independent suites
have actually succeeded.
## Open Questions
None requiring a product or scope decision. API and transition choices are
selected above. Core and ckconfig publication authorization is an explicit
external dependency in Stages 24–25, already required by OD-7 and release policy;
it is not implicit authority granted by an implementation prompt. Exact unused
versions and release commits are established from authoritative state at those
handoffs, without weakening independent-module completion criteria.