Files
commandkit/docs/roadmap/implementation.md

461 lines
27 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.
# Implement PromptKit requirements inspection
Status: in progress; Stages 1 and 2 are complete.
## Authority and working rules
The accepted [feature roadmap](requirements.md) owns the target state and product
choices. This document owns the ordered work to reach that state. Start from
commit [`49b7e68`](https://gitea.maximumdirect.net/eric/CommandKit/src/commit/49b7e686b3072037834574738441e14d3ce061bc),
whose implementation is unchanged from the baseline described in the roadmap.
Read [development guidance](../development.md) and the applicable
[agent](../policy/agents.md), [architecture](../policy/architecture.md),
[documentation](../policy/documentation.md), and [testing](../policy/testing.md)
policies before changing their governed areas.
Follow Stage 1 through Stage 7 in order. Each stage is sized for a separate Luna
implementation prompt. Complete its work and focused validation, then record
brief completion evidence beneath that stage: files changed, commands actually
run, results, and material limitations. Commit completed nonempty units under
the existing commit policy. Distinguish focused checks from final whole-module
validation; never label an unrun check as passing.
The implementation is an unreleased prototype. Do not add compatibility shims
or require every intermediate commit to satisfy the complete final suite.
Preserve explicitly selected contracts and existing unrelated operations. Prefer
the simplest coherent final implementation over preserving incidental private
structure. Any temporary integration gap must have a named later stage that
closes it.
Use the graph tools for code discovery as instructed by AGENTS.md. Source symbols
below are starting points, not restrictions on private helper organization.
Keep work in ckprompt and its documentation/example unless an actual defect in
an existing shared path prevents the selected behavior. Do not introduce a new
module, core domain API, backend abstraction, or upstream discovery model.
## Settled implementation choices
These choices come from the accepted feature roadmap; do not reopen them merely
to match backend defaults:
- Install `app promptkit requirements show PROMPT_ID` when the requirements
callback is present. Do not install an alias under `promptkit show`.
- Add `ckprompt.RequirementsRequest` with `Selection ckconfig.Selection` and
`Inspection promptkit.RequirementInspectionRequest`, and add the optional
`Suite[I, S].InspectRequirements` callback specified by the roadmap.
- Forward native discovery context through that request. Keep configuration
and application-selector presence; native version/profile/session strings
retain native empty-value semantics.
- Accept singleton `--config`, `--overlay`, `--version`, `--profile`,
`--session-id`, and `--format`, plus suite selectors and repeatable
`--var KEY=VALUE`. Overlays do not accumulate. Default the format to YAML and
accept only YAML for this operation.
- Split each variable occurrence at its first `=`; retain exact keys and values,
including whitespace, commas, extra `=` characters, and empty keys/values.
Missing separators are usage errors. Parse every occurrence and apply
duplicate keys in occurrence order, so the last value wins.
- Render the complete native evidence in the selected YAML schema. Successful
delivery of a partial inspection is success; completeness and issues do not
independently establish readiness or failure.
- Use existing preparation, diagnostic, error-identity, cancellation, and
buffered-delivery paths. Application callbacks retain their own effects.
PromptKit v0.14.0 already supplies this capability. Keep the current dependency
versions and Go 1.26.0 consumer minimum. Use the existing Go 1.26.8 maintainer
toolchain. No dependency upgrade, release-version selection, publication,
consumer migration, policy change, or ADR revision is assigned by this plan.
## Stage 1: Add the public request and native engine adapter
**Outcome:** callers can supply requirements inspection independently, and
`FromEngine` exposes PromptKit's native inspection through that callback.
**Starting points:** `Suite`, `PromptRequest`, `FromEngine`, and
`loadPromptKitEngine` in
[adapters.go](../../integrations/ckprompt/adapters.go), and existing adapter
coverage in [adapters_test.go](../../integrations/ckprompt/adapters_test.go).
**Steps**
1. Preserve existing local changes and an ignored `go.work`. Ensure the workspace
includes core, ckconfig, and ckprompt using the developer guide's procedure.
Do not add sibling-module replacements to distributed manifests. Record any
baseline validation limitation that will affect later stages.
2. Add the exported `RequirementsRequest` type and the exact optional callback:
```go
InspectRequirements func(
context.Context, RequirementsRequest, I, S,
) (commandkit.Acquired[*promptkit.RequirementInspection], error)
```
Document selection, native context, optional availability, and the ordinary
suite input/state lifecycle. Extend existing suite comments that mention
only catalogs and named definitions to include requirements inspection.
3. In `FromEngine`, supply the callback. Call `loadPromptKitEngine` with the
request's configuration selection, supplied `I` and `S`, and the ordinary
diagnostic heading, then call `engine.InspectRequirements(ctx,
request.Inspection)`. Return the native result/error and reached diagnostics
in the existing `Acquired` form.
4. Retain nil-loader construction issues, nil-engine failures, native
`errors.Is`/`errors.As` identity, and post-load cancellation handling. Reuse
the current helper rather than implementing another loader or performing
named profile inspection as a prerequisite.
5. Add focused adapter coverage through public PromptKit APIs. Use a small real
engine fixture and compare the callback's returned inspection with the
engine's direct native result for the same request. Observe loader selection,
input, and state; exercise supplied profile/version/variables/session context.
Cover a load failure with ConfigKit diagnostics, nil engine, and a reachable
native selection error.
**Validation and handoff**
Format changed Go files and run the affected adapter tests in ckprompt. Reuse
existing fixture helpers such as `adapterEngineFrom` where useful. This stage
adds the callback; its CLI availability arrives in Stage 3. Update existing
absence tests when the command assembly starts counting the fifth callback in
that stage, rather than treating their old four-callback assumption as policy.
**Completed:** Added `RequirementsRequest`, the optional suite callback, and
the `FromEngine` native adapter in `integrations/ckprompt/adapters.go`; added
adapter coverage in `adapters_test.go`. The ignored workspace already included
core, ckconfig, and ckprompt, and the baseline ckprompt suite passed. After the
change, `GOTOOLCHAIN=local go test ./integrations/ckprompt -run
'TestFromEngine(InspectsRequirementsWithNativeRequest|RequirementsRetainLoadDiagnosticsAndSelectionErrors|RequirementsHonorCancellationAfterLoad|PassesRequestsAndRetainsNilLoaderIssue)$'
-count=1` and `GOTOOLCHAIN=local go test ./...` from `integrations/ckprompt`
passed. The tests verify native result equivalence, loader arguments, retained
ConfigKit diagnostics, nil-engine handling, native selection error identity, and
cancellation after loading. No material limitations were encountered.
## Stage 2: Implement the requirements YAML report
**Outcome:** native inspections map to the accepted YAML shape without losing
partial evidence or metadata presence.
**Starting points:** `promptReportFromInspection`, `encodePromptYAMLBytes`,
`encodePromptYAML`, and existing report tests in
[command.go](../../integrations/ckprompt/command.go) and
[command_test.go](../../integrations/ckprompt/command_test.go).
**Steps**
1. Add private report types with explicit YAML tags for the exact field inventory
in [the roadmap's report section](requirements.md#initial-report). Keep these
types private; the callback already returns the native public inspection.
2. Add a straightforward conversion from `*promptkit.RequirementInspection` to
those report types. Preserve selected identities, returned collection order,
classifications, explanations, structured issue associations, and issue
descriptions. Do not recompute completeness or sort/deduplicate native data.
3. Populate input, variable, and issue slices as non-nil empty slices when the
native inventory is empty. Emit `complete`, `declared`, and `supplied` even
when false. Keep nullable metadata fields present: nil pointers render null
and pointers to empty strings render empty strings. Include the other listed
fields even when their values are empty; avoid `omitempty` on this schema.
4. Carry native kind and issue-code strings without a CommandKit enum whitelist.
Do not add supplied variable values, artifact payloads, prompt contents, or
a readiness field to the report. Native resolved input identifiers remain
part of the evidence.
5. Encode through the existing YAML v4 buffer helper. Add report coverage for
empty inventories, false values, null versus empty metadata, complete and
partial results, and preservation of strings that YAML might otherwise
interpret as numbers or booleans. Decode YAML semantically; assert neither
incidental indentation/key order nor exact upstream explanatory wording.
**Validation and handoff**
Run the focused report tests. Use only the module's declared
`go.yaml.in/yaml/v4` package in new tests. Stage 3 will connect this report to
invocation and delivery; do not create a parallel public report writer.
**Completed:** Added private requirements report types and a direct conversion
from PromptKit's native inspection in `integrations/ckprompt/command.go`, with
semantic YAML coverage in `command_test.go`. `GOTOOLCHAIN=local go test
./integrations/ckprompt -run 'TestRequirementsReport' -count=1` passed. Coverage
confirms empty sequences, present false/empty values, nullable metadata,
ordering, unrecognized kind/code strings, and complete and partial results.
No material limitations were encountered.
## Stage 3: Assemble and invoke the optional requirements command
**Outcome:** an installed requirements capability has the selected command,
flags, input lifecycle, and buffered report delivery.
**Starting points:** `Command`, `newDefinitionCommand`, `newCatalogListCommand`,
`prepareSuiteInput`, `definitionFailure`, and `writePromptReport` in ckprompt's
`command.go`; public `CaptureFlags`, `ValueFlag`, `RepeatFlag`, and
`ValueArgument` in core.
**Steps**
1. Add a private requirements command input containing configuration selection,
captured suite selectors, selected `I`, native discovery context, and format.
Give the core declaration a private input factory as existing inspection
commands do; the application's `Suite.NewInput` still runs only when selected
input preparation is reached.
2. Capture suite selectors with the existing `CaptureFlags` composition. Bind
one required prompt operand to native `PromptID`. Bind config and overlay to
their `Supplied[string]` fields, preserving explicit empty selection. Bind
version, profile, and session ID to native request strings with empty defaults.
Bind format with a `yaml` default. Add no built-in `--scope` flag.
3. Use `RepeatFlag` for `--var` with a private assignment parser using
`strings.Cut(raw, "=")` and a small private key/value representation. Return
a parse error when no separator exists. In the setter, build a fresh map for
that invocation from all converted occurrences, in order. Keep zero
assignments equivalent to the native empty context; do not invent a public
nil-versus-empty-map distinction.
4. In command input checking, reject formats other than YAML before preparing
application input. Call `prepareSuiteInput` for the captured selectors and
configuration selection, retaining its error handling. Cache that selected
input for the handler. Core's required-operand and converted-value paths
handle syntax; do not parse raw argv inside the handler.
5. Implement the handler following existing inspection flow: check cancellation
at the established operation boundaries, initialize state after checks pass,
invoke only `InspectRequirements`, retain diagnostics on errors, reject a
nil inspection with no error as an operational provider failure, and check
cancellation after acquisition. Convert/encode through Stage 2 and deliver
through `writePromptReport`; use `definitionFailure` for operational failures.
Completeness and structured issues do not select a failure status.
6. Extend `Command`'s absence test to include `InspectRequirements`. Add
`Group("requirements", ..., showCommand)` only when that callback is present,
beneath the existing root-only `promptkit` declaration. Retain its attached
construction errors, copied selectors, and optional existing groups. Adjust
root help summaries to describe the expanded inspection surface.
7. Add installed-command coverage for requirements alone, all callbacks present,
requirements omitted, and all callbacks absent. Update existing tests that
clear a `FromEngine` suite to clear the fifth callback where they intend total
absence. Preserve tests' actual intended capability subsets.
8. Test the accepted variable table through public `App.Execute`, including
comma/whitespace preservation, empty key/value, additional separators,
duplicate keys, and an earlier malformed occurrence followed by valid input.
Test missing/extra operands, unsupported format, singleton last-value behavior,
and request forwarding. Parse failures and format preflight leave the
application factories/provider uninvoked.
**Validation and handoff**
Run focused requirements command tests and existing optional-family tests.
Confirm help shows the requirements group and its operands/flags without invoking
suite factories or loaders. Normal flag conversion still applies on help;
do not add a special bypass for malformed variable occurrences. Namespace
collision/root-placement enforcement stays with the existing core installer.
## Stage 4: Verify native semantics, lifecycle, and failure delivery
**Outcome:** the installed command demonstrably delegates discovery to PromptKit
and preserves the existing coordination and failure contracts.
**Starting points:** new requirements tests from Stages 1–3, ckprompt's existing
lifecycle and delivery tests, and external composition tests. Keep generic
parser/backend behavior with its current test owner.
**Steps**
1. Install a real `FromEngine` suite in a public App. Use a prompt with the
roadmap's conditional template and no input declarations, plus a selected
profile. Compare decoded report evidence with direct native inspection for
missing `mode` and supplied `mode=full`. Both delivered invocations return
success; the first is incomplete and contains its native issue, while the
second resolves the dependency. Also protect complete discovery with an
unsupplied required variable, using a native fixture that demonstrates it.
2. Cover omitted versus explicit prompt version/profile selection through the
native request. Demonstrate that a profile need not be execution-ready and
that a supplied session ID changes session-template discovery as the native
API defines. Test missing/ambiguous native selections with error identity,
rather than duplicating upstream's full recognition or selection matrix.
3. Exercise a custom requirements callback with captured suite selectors,
input checks, and state. Observe one selected input and one state when their
lifecycle points are reached; checks precede state/provider acquisition.
Preserve omitted-factory zero values, including nil pointer types, without
automatic allocation. Repeated executions start with fresh core-owned
variable values and selector capture. Do not assert distinct caller-owned
factory results or concurrent dispatch.
4. Verify command capture keeps later suite metadata edits and callback
replacement from changing the captured declaration, while retaining the
existing ownership of callback-referenced objects. Cover a failed input check
and cancellation before acquisition using the established classifications.
5. Use a small custom callback for success with warning diagnostics, provider
failure with diagnostics, and cancellation observed after callback return.
Verify reached diagnostics and supported cause identity survive. Structured
requirement issues stay in YAML rather than being presented a second time as
CommandKit errors.
6. Exercise a nil-result provider, failing/short report writer, and diagnostic
writer failure. Confirm existing operational status, `ReportIncomplete`,
`DeliveryError`, and diagnostic ownership where applicable. Preserve partial
bytes on a report failure without promising transactional output. Reuse
generic delivery coverage where it already protects the path.
**Validation and handoff**
Run the ckprompt module suite, including its existing composed example tests.
Use native APIs and deterministic in-memory/filesystem fixtures; no model calls
or real credentials are needed. PromptKit v0.14.0 has no public path for adding
warnings to a successfully acquired engine configuration. Use custom callback
diagnostics for the command path and existing helper evidence for that native
limitation; do not add a test-only engine injection API or claim an unavailable
native warning scenario was exercised.
## Stage 5: Demonstrate requirements in the maintained composed consumer
**Outcome:** the existing consumer shows partial discovery and contextual
resolution through the ordinary public registration API.
**Starting points:**
[composed main.go](../../integrations/ckprompt/examples/composed/main.go),
[main_test.go](../../integrations/ckprompt/examples/composed/main_test.go), and
its fixtures. `newApp` already installs the suite returned by `FromEngine`.
**Steps**
1. Keep the existing public `FromEngine`/`Command` composition; the new default
callback makes the command available without another registration block.
2. Extend the loader's synthetic prompt message with a conditional dependency
using `{{if .mode}}{{input "notes"}}{{end}}`. Retain the existing briefing
prompt identity, campaign-selected profile, declared topic input, and
configuration comparison fixtures. The absence of a `notes` declaration
makes that reference distinguishable from the existing declared input.
This example does not execute or need payloads for either input.
3. Add consumer coverage for these invocations:
```sh
go run . promptkit requirements show briefing --campaign spring
go run . promptkit requirements show briefing --campaign spring --var mode=full --format yaml
```
In tests, invoke the assembled App directly and decode YAML. Observe the
spring identity, missing versus supplied `mode`, conditional versus required
`notes`, incomplete versus complete discovery, and success for both reports.
Retain other native inventory entries rather than expecting an artificial
one-entry report.
4. Preserve the existing config diff, catalogs, named inspections, and custom
session restore tests. Adjust fixture-derived expectations only where the
deliberate message change affects them. Avoid position-based catalog or
inventory assertions when only membership is intended.
**Validation and handoff**
Run the composed example's tests and both commands from its directory. Record
actual report semantics, not explanatory wording snapshots. Documentation of
these copyable commands belongs in Stage 6.
## Stage 6: Align canonical documentation with the delivered feature
**Outcome:** API, operator, and consumer documentation describes the implemented
capability without leaving four-callback assumptions or stale deferral claims.
**Steps**
1. Finish GoDoc in `adapters.go`, `command.go`, and
[doc.go](../../integrations/ckprompt/doc.go): the optional callback, thin native
request, selected input/state timing, capability capture, and adapter meaning.
Keep exact exported contracts there rather than in consumer walkthroughs.
2. Extend [the CLI reference](../cli.md) with the installed command, all its
operands/flags/defaults, variable grammar, report schema, and outcome rules.
Explain discovery completeness versus satisfaction/readiness and native
profile/default/version/session interpretation with a pinned upstream API
link. Document partial reports and their successful status. Other formats are
unsupported for this operation, not prohibited across CommandKit.
3. Update [consumer guidance](../consumers/guide.md), the module README, and root
README summaries as needed. Show independent requirements availability and
ordinary wrapping/clearing/replacement of the callback. Fix narrowing examples:
a suite described as catalogs-only must now also clear `InspectRequirements`.
Keep operator grammar/schema in the CLI owner and exact API in GoDoc.
4. Update [internal inventory](../internal/overview.md) and relevant developer
orientation summaries to include requirements. Extend the composed example's
README with the two Stage 5 commands and a short explanation of their results.
Keep complete runnable artifacts in that maintained example.
5. Remove the now-delivered requirements deferral from
[deferred.md](deferred.md), retaining unrelated deferred targets. Reconcile the
feature roadmap's historical deferral link so it points to an existing owner
or an immutable historical record rather than a removed fragment.
6. Leave accepted ADR 0004 and the architecture policy unchanged: this operation
fits their selected ownership/capability boundaries. Dependency versions and
incorporated material are unchanged; do not churn attribution or manifests.
Release notes/version selection remain with actual release preparation under
the feature roadmap and current release procedure.
**Validation and handoff**
Review changed examples against observed behavior. Check local links/fragments,
whitespace, and remaining references to four callbacks or deferred requirements.
Historical release notes and pinned retired plans remain historical evidence;
do not rewrite them as current reference pages. Current canonical docs must not
depend on this temporary plan for their implemented contracts.
## Stage 7: Validate the final module boundaries and complete the sprint
**Outcome:** the implemented feature works with its committed dependency graph,
the complete repository remains coherent, and completion evidence is accurate.
**Steps**
1. Format changed Go files. With the ignored local workspace, run each module's
suite separately; root traversal misses the nested modules:
```sh
GOTOOLCHAIN=local go test ./...
(cd integrations/ckconfig && GOTOOLCHAIN=local go test ./...)
(cd integrations/ckprompt && GOTOOLCHAIN=local go test ./...)
```
These suites include the maintained consumers. No new race, benchmark,
coverage threshold, platform matrix, or release gate is required.
2. Run root tests with `GOWORK=off GOTOOLCHAIN=local` and `-mod=readonly`.
For each integration, copy its committed `go.mod` and `go.sum` into a separate
temporary directory as `go.mod` and `go.sum`. With `go mod edit -modfile=...`,
add only replacements for unpublished CommandKit prerequisites: core for
ckconfig; core and ckconfig for ckprompt. Use absolute checkout paths. Do not
replace PromptKit/ConfigKit with sibling checkouts or tidy these copies to
make undeclared test dependencies pass.
3. From each integration's own directory, run its tests, module graph, and
production/test import enumeration with that prepared module file:
```sh
GOWORK=off GOTOOLCHAIN=local go test -mod=readonly -modfile=<temporary-go.mod> ./...
GOWORK=off GOTOOLCHAIN=local go list -mod=readonly -modfile=<temporary-go.mod> -m all
GOWORK=off GOTOOLCHAIN=local go list -mod=readonly -modfile=<temporary-go.mod> -deps -test ./...
```
Also enumerate root graph/imports with workspace disabled. Verify temporary
module/sum bytes remain unchanged during the readonly checks, after the
intentional replacements. If a check reveals a genuine distributed-metadata
problem, fix and explain it in the owning module, then repeat the relevant
checks; never hide it with extra temporary requirements or fabricated sums.
4. Confirm ckprompt still selects PromptKit v0.14.0 and ConfigKit v0.7.1,
ckconfig acquires no ckprompt/PromptKit dependency, and core acquires no optional
integration or owning-library dependency through production/tests/examples/
tools. Disposable replacements establish compatibility with local prerequisite
sources, not availability of future CommandKit tags. Retain workspace files
and temporary graph artifacts outside the tracked distribution.
5. Exercise both requirements example commands alongside representative existing
catalog, named inspection, config diff, and session restore commands. Use
semantic report assertions and the existing example fixtures. Reuse unchanged
successful checks from preceding stages where they apply to the final state.
6. Audit the roadmap target against code, coverage, examples, and canonical docs.
Check changed-document links/fragments and staged/unstaged whitespace with
the developer guide's commands. Record environmental limits as pending checks
rather than declaring them successful.
7. Record final evidence here and update the feature roadmap's status to reflect
actual delivery. Stage only this sprint's intended files, review the final
diff, and commit the last nonempty unit. Report remaining worktree changes
accurately. Do not push, tag, publish, or delete the planning documents as an
implicit part of implementation.
**Completion criteria**
- Independent requirements capability, optional groups, complete absence, and
retained wiring errors follow the existing installation contracts.
- Native discovery context reaches the selected callback/engine accurately;
variable parsing and selected input/state lifecycle match the accepted roadmap.
- YAML retains the native evidence and metadata presence; partial inspection is
successful when acquisition and delivery succeed.
- Actual failures preserve diagnostics, supported error identity, and established
outcome/delivery handling without a readiness checker or extra domain rules.
- The composed consumer and canonical documentation demonstrate the implemented
behavior. Relevant module and isolated dependency/import checks pass, or any
uncompleted required check is explicitly reported as pending.
- No new owning-library dependency appears in core or in the reverse integration
direction, and no local workspace/replacements enter distributed metadata.
All product and API decisions required for this plan are settled. Private helper
names and equivalent implementation organization remain ordinary coding choices.