32 KiB
Implement PromptKit requirements inspection
Status: complete; Stages 1 through 7 are complete.
Authority and working rules
The accepted feature roadmap owns the target state and product
choices. This document owns the ordered work to reach that state. Start from
commit 49b7e68,
whose implementation is unchanged from the baseline described in the roadmap.
Read development guidance and the applicable
agent, architecture,
documentation, and testing
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_IDwhen the requirements callback is present. Do not install an alias underpromptkit show. - Add
ckprompt.RequirementsRequestwithSelection ckconfig.SelectionandInspection promptkit.RequirementInspectionRequest, and add the optionalSuite[I, S].InspectRequirementscallback 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, and existing adapter
coverage in adapters_test.go.
Steps
-
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. -
Add the exported
RequirementsRequesttype and the exact optional callback: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.
-
In
FromEngine, supply the callback. CallloadPromptKitEnginewith the request's configuration selection, suppliedIandS, and the ordinary diagnostic heading, then callengine.InspectRequirements(ctx, request.Inspection). Return the native result/error and reached diagnostics in the existingAcquiredform. -
Retain nil-loader construction issues, nil-engine failures, native
errors.Is/errors.Asidentity, and post-load cancellation handling. Reuse the current helper rather than implementing another loader or performing named profile inspection as a prerequisite. -
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 and
command_test.go.
Steps
- Add private report types with explicit YAML tags for the exact field inventory in the roadmap's report section. Keep these types private; the callback already returns the native public inspection.
- Add a straightforward conversion from
*promptkit.RequirementInspectionto 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. - Populate input, variable, and issue slices as non-nil empty slices when the
native inventory is empty. Emit
complete,declared, andsuppliedeven 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; avoidomitemptyon this schema. - 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.
- 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
- 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'sSuite.NewInputstill runs only when selected input preparation is reached. - Capture suite selectors with the existing
CaptureFlagscomposition. Bind one required prompt operand to nativePromptID. Bind config and overlay to theirSupplied[string]fields, preserving explicit empty selection. Bind version, profile, and session ID to native request strings with empty defaults. Bind format with ayamldefault. Add no built-in--scopeflag. - Use
RepeatFlagfor--varwith a private assignment parser usingstrings.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. - In command input checking, reject formats other than YAML before preparing
application input. Call
prepareSuiteInputfor 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. - 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 throughwritePromptReport; usedefinitionFailurefor operational failures. Completeness and structured issues do not select a failure status. - Extend
Command's absence test to includeInspectRequirements. AddGroup("requirements", ..., showCommand)only when that callback is present, beneath the existing root-onlypromptkitdeclaration. Retain its attached construction errors, copied selectors, and optional existing groups. Adjust root help summaries to describe the expanded inspection surface. - Add installed-command coverage for requirements alone, all callbacks present,
requirements omitted, and all callbacks absent. Update existing tests that
clear a
FromEnginesuite to clear the fifth callback where they intend total absence. Preserve tests' actual intended capability subsets. - 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.
Completed: Added the optional promptkit requirements show command, typed
bindings, per-invocation variable assignment parsing, format preflight, and
buffered YAML delivery in integrations/ckprompt/command.go. Extended
command_test.go for requirements-only and all-capability trees, help, request
forwarding, selector/configuration presence, variable grammar, singleton
overrides, repeated executions, and usage preflight. Focused optional-family and
requirements command tests passed with GOTOOLCHAIN=local go test ./integrations/ckprompt -run 'Test(CommandBuildsIndependentOptionalTrees|CatalogCommandTreesAreIndependentAndOmitShow|RequirementsCommandCapturesContextAndVariableAssignments|RequirementsCommandUsageFailuresPrecedeApplicationPreparation)$' -count=1; GOTOOLCHAIN=local go test ./... from integrations/ckprompt also
passed. No material limitations were encountered.
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
- Install a real
FromEnginesuite 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 missingmodeand suppliedmode=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. - 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.
- 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.
- 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.
- 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.
- 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.
Completed: Added installed-command coverage in
integrations/ckprompt/requirements_command_test.go for native report
equivalence across conditional discovery, selected/default identities,
session-template discovery and bypass, and complete discovery with an
unsupplied required variable. The tests also cover captured selectors and
callback data, repeated invocation lifecycles, omitted pointer factories,
input rejection and pre-acquisition cancellation, warning diagnostics,
provider error identity, post-callback cancellation, nil results, partial report
delivery, short writes, and diagnostic writer failure. The focused command
GOTOOLCHAIN=local go test ./integrations/ckprompt -run 'TestRequirementsCommand(MatchesNativePromptKitDiscovery|PreservesNativeSelectionErrors|KeepsLifecycleAndCapturedDeclaration|ChecksInputAndCancellationBeforeAcquisition|PreservesDiagnosticsCancellationAndDelivery)$' -count=1 passed. GOTOOLCHAIN=local go test ./... from
integrations/ckprompt passed, including the composed example suite. Native
successful-acquisition warnings remain unavailable through PromptKit v0.14.0's
public API; custom callback diagnostics exercise the installed command path.
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,
main_test.go, and
its fixtures. newApp already installs the suite returned by FromEngine.
Steps
-
Keep the existing public
FromEngine/Commandcomposition; the new default callback makes the command available without another registration block. -
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 anotesdeclaration makes that reference distinguishable from the existing declared input. This example does not execute or need payloads for either input. -
Add consumer coverage for these invocations:
go run . promptkit requirements show briefing --campaign spring go run . promptkit requirements show briefing --campaign spring --var mode=full --format yamlIn tests, invoke the assembled App directly and decode YAML. Observe the spring identity, missing versus supplied
mode, conditional versus requirednotes, incomplete versus complete discovery, and success for both reports. Retain other native inventory entries rather than expecting an artificial one-entry report. -
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.
Completed: Extended the synthetic prompt in
integrations/ckprompt/examples/composed/main.go with the conditional
notes dependency while retaining the declared topic input. Added assembled
App coverage in main_test.go for selected spring identity, missing and
supplied mode, conditional versus required classification of undeclared
notes, incomplete versus complete discovery, and success for both reports.
GOTOOLCHAIN=local go test ./examples/composed -run 'TestComposedRequirementsDiscoveryUsesSelectedCampaign$' -count=1 and
GOTOOLCHAIN=local go test ./examples/composed passed from
integrations/ckprompt. Both roadmap commands passed from
integrations/ckprompt/examples/composed: the first report was incomplete with
a missing mode issue and conditional notes; the second was complete with
mode supplied and required notes. No material limitations were encountered.
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
- Finish GoDoc in
adapters.go,command.go, and 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. - Extend the CLI reference 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.
- Update consumer guidance, 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. - Update internal inventory 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.
- Remove the now-delivered requirements deferral from 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.
- 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.
Completed: Updated the exported API comments in
integrations/ckprompt/adapters.go, command.go, and doc.go; the command and
report contract in docs/cli.md; optional callback guidance in
docs/consumers/guide.md; module, root, internal, and contributor summaries;
and the composed example README. Removed the delivered capability from
docs/roadmap/deferred.md and updated
docs/roadmap/requirements.md to describe its historical baseline and point to
the immutable commit that contains the prior deferral. The two example commands
and their semantics match the Stage 5 test and observed output. gofmt -w integrations/ckprompt/adapters.go integrations/ckprompt/command.go integrations/ckprompt/doc.go and git diff --check -- '*.md' COPYRIGHT passed.
A local link and fragment check across nine changed Markdown documents passed;
the stale four-callback and deferred-requirement claim search found no current
documentation claims. No material limitations were encountered.
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
-
Format changed Go files. With the ignored local workspace, run each module's suite separately; root traversal misses the nested modules:
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.
-
Run root tests with
GOWORK=off GOTOOLCHAIN=localand-mod=readonly. For each integration, copy its committedgo.modandgo.suminto a separate temporary directory asgo.modandgo.sum. Withgo 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. -
From each integration's own directory, run its tests, module graph, and production/test import enumeration with that prepared module file:
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.
-
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.
-
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.
-
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.
-
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.
Completed: The root, ckconfig, and ckprompt workspace suites all passed
with GOTOOLCHAIN=local go test ./... from each module, including the maintained
consumers. Root tests and root module/import enumeration passed with
GOWORK=off GOTOOLCHAIN=local and -mod=readonly. Isolated ckconfig and
ckprompt checks passed using copies of their committed module files and only
the prescribed local CommandKit prerequisite replacements; for each, readonly
tests, go list -m all, and go list -deps -test ./... passed, and the copied
go.mod/go.sum hashes remained unchanged. The graphs confirm PromptKit
v0.14.0 and ConfigKit v0.7.1 for ckprompt, no PromptKit/ckprompt dependency in
ckconfig, and no optional integration or owning-library imports in core. The
ignored go.work remains untracked and distributed module files are unchanged.
Both requirements commands, prompt catalog, named prompt and profile
inspections, app config diff, and session restore succeeded from the composed
example. Existing semantic tests verify partial and complete report contents.
Changed-document local links/fragments and staged/unstaged whitespace checks
passed. No required check remains pending and no material limitations were
encountered.
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.