Files
commandkit/docs/roadmap/implementation.md

30 KiB
Raw Blame History

Implement PromptKit requirements inspection

Status: in progress; Stages 1 through 5 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_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, and existing adapter coverage in 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:

    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 and command_test.go.

Steps

  1. 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.
  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.

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

  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.

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

  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:

    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.

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

  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. 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.
  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:

    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:

    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.