Files
commandkit/docs/roadmap/01-cli-plan.md

50 KiB

CommandKit CLI standard proposal

Status: proposed; no commands in this document are implemented by CommandKit.

Date: 2026-10-06.

This proposal defines the command suite, operator behavior, and ownership boundary recommended for CommandKit. Audita, Narratio, Notarius, and Scriptorium provide evidence, rather than compatibility requirements. Adoption is expected to produce breaking changes. The decisions recorded below are accepted; other recommendations remain proposed. Implementation details and exact Go API declarations remain future work.

Accepted planning decisions

  • Use namespace/resource first: app config show and app promptkit show. Whether the first noun describes a resource or a library namespace does not change the command grammar or its ownership boundary.
  • Assemble families from declared capabilities. Each family defines its own supported verbs; configuration scopes expose their available operations.
  • Keep ordinary version output focused on the application. Explicit version --scope commandkit reports embedded CommandKit identity.
  • Govern CommandKit's own behavior and provider contracts. Shared loading and callback organization are consumer guidance.
  • Put PromptKit definition inspection under the single promptkit family. CommandKit does not reserve the root prompt and profile names.
  • Support config show --scope all, with the application configuration followed by the PromptKit configuration when registered, as separate YAML documents.
  • Defer JSON output from the initial implementation. YAML supports automated consumption as well as operator inspection; JSON may never be needed and is not a promised later milestone. JSON Pointer remains the configuration path syntax and is independent of report format.

1. Purpose and ownership

Recommendation: CommandKit owns the entire config command family, root help, and version reporting. It also offers the standard promptkit inspection family for applications that integrate PromptKit. Applications own commands outside these families, such as narratio session restore, narratio publish, audita process, and scriptorium serve.

Ownership includes command names, accepted flags and arguments, defaults, help, report shapes, diagnostic presentation, streams, and exit behavior. Applications provide configuration schemas, source acquisition, domain resolution, validation, build metadata, and PromptKit engine construction. These collaborators supply data and perform domain work; they do not replace CommandKit's handlers or renderers.

CommandKit assembles standard families from registered capabilities and preserves its contracts for enabled operations. It rejects replacement standard handlers and private operations under its reserved families. Each family defines its own operation set; providers can supply supported subsets. Help and discovery expose that availability. New shared operations enter through versioned library changes. ADR 0001 owns this decision.

Applications may register context selectors and typed configuration overrides through the extension mechanism described in section 5. This is a defined input extension, not permission to alter standard operations or output.

Alternatives:

  • Share helpers while applications own their handlers. Easy to integrate, but permits the same naming, scope, and output drift that motivates CommandKit.
  • Allow arbitrary subcommands beneath standard families. Convenient initially, but makes the standard unpredictable and creates future name collisions.
  • Own every command in every application. Unnecessary for the stated goals; domain operations have responsibilities that a shared inspection library cannot usefully prescribe. CommandKit can supply common parsing and help machinery without owning their business semantics.

2. Evidence and conclusions

The current checkouts demonstrate both reuse opportunities and choices that need to be replaced rather than preserved.

Evidence Conclusion for the standard
Audita uses config print-effective; Notarius uses config effective; Narratio and Scriptorium use config show. Select one verb: show.
Audita combines settings, selected profiles, and provenance under --view; Notarius uses --scope; Scriptorium has a separate framework configuration family. Separate the operation from the inspected configuration scope.
Narratio's config sources reports field ownership; Scriptorium lists acquired source records. Give source inventory and field explanation distinct contracts.
Scriptorium's application and framework handlers repeat parsing, loading, warnings, dispatch, and output handling. CommandKit should implement one inspection command family.
Notarius and Scriptorium inspect named PromptKit profiles; Scriptorium also inspects prompts. Definition inspection is another demonstrated shared capability.
Narratio compares resolved overlays, rather than YAML file text. Include semantic comparison of effective configuration.
Help, flag placement, validation success output, and error statuses differ. Specify those behaviors centrally.
Narratio invokes the other applications and interprets Scriptorium's status 2 as output validation failure. Breaking adoption requires coordinated consumer updates.

Evidence sources, relative to this roadmap in the sibling workspace checkouts:

These links document review evidence; they are not build dependencies. When the standard becomes implemented behavior, its durable reference should be owned by CommandKit rather than relying on sibling checkout links.

Standard configuration operation inventory

Command Operator question or task
app help [COMMAND ...] How do I use this command?
app version Which application build am I running?
app config scopes Which configuration scopes can I inspect?
app config validate Is the selected configuration valid?
app config show [--scope app|promptkit|all] What effective configuration will the application use?
app config sources Which inputs contributed to this configuration?
app config explain --path POINTER Why does this path have this value, or why was it removed?
app config diff --against-config PATH or --against-overlay NAME How do two effective configurations differ?

app is a placeholder for the application's executable name. Section 4 defines scope defaults; section 5 defines selectors shared by configuration operations.

Optional PromptKit suite

Command Operator question or task
app promptkit list prompts Which prompt IDs and versions are available?
app promptkit list profiles Which execution profiles are available?
app promptkit show prompt ID [--prompt-version VERSION] What is the selected prompt's definition metadata?
app promptkit show profile ID What execution target does this named profile resolve to?

Accepted: put this suite under promptkit, using the same namespace/resource first pattern as config. The root names prompt and profile remain available to applications.

Recommendation: use explicit resource operands after list and show. Plural collection names select catalogs; singular names select one definition. This keeps prompt IDs and execution profile IDs unambiguous even when they overlap. Registration reserves the promptkit family and supplies handlers for its declared definition capabilities. Its configuration scope is a separate capability; definition operations and configuration inspection can be integrated independently. Configuration inspection remains config show --scope promptkit; the definition commands report metadata and resolved execution targets, not a second configuration display path.

Definition catalogs require upstream API work; their absence today is an implementation dependency, not evidence against a useful command standard.

Alternatives:

  • Bare promptkit list. Could display a combined catalog with resource type, ID, and prompt version. Useful for discovery, but potentially noisy. An explicit resource gives each catalog a clear report shape; a combined listing can be considered as an additional operation later.
  • promptkit show ID --type profile. Explicit and compact, but places the resource identity partly in a flag. Prefer the resource operand.
  • promptkit show ID. Requires cross-collection lookup and ambiguity rules. Do not make inspection depend on prompt and profile IDs being globally unique.
  • promptkit profiles list/show. Retains the accepted namespace boundary and is viable, but adds another resource grouping before the operation. The proposed promptkit list/show RESOURCE layout puts the operation immediately after its owning namespace, as in config show.
  • Put definitions under config, such as config profile. Mixes source configuration with backend-completed execution settings and gives config an unrelated resource vocabulary.
  • Include only the core suite initially. A reasonable delivery phase. Keep individual PromptKit capabilities pending until their public APIs are available; their absence leaves existing inspection capabilities available.

run, execute, render, serve, session, and artifact catalogs are not CommandKit-owned commands in this proposal. Recommend run for a new application's principal execution operation, but retain application ownership of that choice.

4. Configuration scopes

Recommendation: use --scope NAME on configuration operations, with these standard identifiers:

  • app: the application's effective configuration, including defaults, imports, the selected overlay, explicit overrides, and recorded domain resolution. Each application's configuration schema remains its own.
  • promptkit: the captured configuration of the PromptKit engine assembled from that application's selected inputs and registered assets, when integrated.
  • all: a selector available to config validate and config show, meaning every registered scope. It is not itself a configuration scope or a merged document.

show, sources, explain, and diff default to app. validate defaults to all: a bare validation command should establish that the configured application and its integrated framework definitions are valid. --scope app provides the explicit narrower check without constructing PromptKit. A non-PromptKit application's all selection consists only of app. show --scope all is an explicit request for a YAML stream of all selected scopes. sources, explain, and diff continue to select one scope and reject all in this proposal.

The differing default for validation is intentional: routine inspection starts with the application configuration, while general validation performs a complete configuration check. An operator can request the combined inspection explicitly. Help must state these defaults prominently.

config scopes lists scope ID, description, and whether it is the default for inspection, together with supported operations. It uses registration metadata, so it works when files are missing or configuration is invalid. List app first, promptkit second when registered, then additional scopes sorted by ID; use this same order for show --scope all. It accepts --format text|yaml and no loading selectors. Unsupported scope names are usage errors, with the available names in the diagnostic; there is no fallback to app.

Scopes use stable semantic names rather than executable names such as notarius. Additional independently meaningful configuration scopes declare their supported inspection operations and applicable providers. CommandKit validates declared wiring and coordinates explicit dependencies, reusing compatible provider results within an invocation. Scope inventory and help report the supported operations. An unsupported scope-operation selection receives a usage diagnostic before acquisition. all retains its all-scope meaning and checks coverage for the requested operation. ADR 0009 owns the model.

A configuration scope targets configuration, while a command family targets its own resources. A future FeedKit family could supply list and show; FeedKit configuration inspection could separately register a configuration scope.

Alternatives:

  • Default every operation to app, requiring validate --scope all. Very uniform syntax, but easy to mistake application-only validation for validation of all definitions needed by the application.
  • Separate paths, such as config promptkit show. Makes the scope visible in the command path; produces duplicated operation trees and complicates adding scopes. This remains a viable alternative if scope flags feel too indirect.
  • A scope-keyed wrapper for show --scope all. Scope identity survives parsing in one document, but requires additional presentation work and indentation of the native reports. The accepted initial approach keeps separate native YAML documents; its output and ordering contract are defined below.
  • Require every inspection operation per scope. Gives a uniform inventory. A missing upstream operation then blocks useful existing capabilities. The selected approach exposes operation availability explicitly.

5. Shared inputs and command syntax

Configuration selection and overrides

Recommendation: register selection inputs according to provider applicability. --scope NAME selects the configuration. Providers using an application root accept --config PATH; ConfigKit overlay integrations accept --overlay NAME. These names retain the same meanings wherever applicable. Additional source models can declare their own selectors through the standard input extension mechanism.

--config selects the application root configuration even for promptkit scope; the application constructs the selected framework inputs from that root and its other explicit selectors. A supplied empty path is a usage error. A supplied missing or invalid file is a load failure and never triggers discovery fallback. Relative CLI paths use the invocation working directory. File-relative resources retain the owning library's source-context semantics.

When --config is omitted, CommandKit invokes the registered acquisition callback with that absence preserved. It exposes supplied discovery, environment, and default-only metadata in help. Sharing acquisition code with ordinary execution is recommended consumer practice; execution loading and filesystem discovery remain application choices. ADR 0010 owns the parsing and propagation contract.

--overlay always selects a configuration overlay, never a PromptKit execution profile. Follow ConfigKit's naming and resolution semantics: omission, an empty name, and the exact name default select the base; other names select one overlay. An overlay name is not a file path. Replace --config-overlay with this standard spelling rather than silently maintaining two conventions.

The registration API accepts domain context selectors, such as Narratio's --campaign and --campaign-file, and typed configuration overrides. CommandKit parses them, exposes their declared applicability in help, and passes values to the selected callback. Field mapping and domain checks remain callback behavior. CommandKit validates declared combinations and applicability before loading and reports collisions with standard flags. Shared selector names keep their defined meaning on the operations where they are registered.

CommandKit preserves explicit flag presence. Omission, an empty string, false, zero, and null retain their registered meanings. Using ConfigKit's recorded overrides and domain resolution is recommended for coherent values and provenance; CommandKit passes typed inputs to the registered integration callback. Execution-only controls such as resume, output directories for generated artifacts, or lane execution do not belong on generic configuration commands.

Alternatives:

  • Require --config on every operation. Simple and explicit, but makes inspection use a different entry path from normal execution and limits defaults-only applications.
  • Prescribe one global discovery sequence now. Could improve consistency, but expands this command proposal into deployment and configuration discovery policy. Evaluate that separately rather than preserving current differences by assumption or inventing paths without deployment evidence.
  • Only expose root, overlay, and scope flags. Keeps the surface small but cannot inspect Narratio's resolved campaign contributions or explicit settings used by execution. A controlled registration mechanism is more effective.
  • Offer a generic --set path=value. Convenient for advanced operators, but requires a new value syntax and typing contract and makes overrides harder to discover. Defer until typed, documented flags prove insufficient.

Flag placement and repeated flags

Recommendation: put the complete command path before its options; allow flags and operands to be interspersed within the selected leaf command. For example:

app config show --config ./config.yml --scope promptkit
app promptkit show profile team.summary --config ./config.yml
app promptkit show profile --config ./config.yml team.summary

Standard flags use --long-name; -h is the standard help shorthand. Accept --name value and --name=value; boolean false uses --name=false. -- ends option parsing and makes subsequent tokens operands. Standard configuration operations reject positional operands; promptkit list accepts a collection name, and promptkit show accepts a resource name and its one documented ID. Flags may appear before or after these operands. help accepts command-path operands.

Singleton flags reject repeated occurrences, including mixed --name value and --name=value spellings. Repeatable source or domain flags combine in documented order. Do not infer singleton presence from its parsed value. Unknown flags are usage errors. New names use hyphens rather than underscores.

This is the contract for CommandKit-owned commands. Recommend the same grammar for application commands and supply reusable parsing machinery, while leaving their operand counts and domain rules application-owned.

Alternatives:

  • Verb first, such as app show config. Considered and not selected. It groups operations by action but spreads one namespace across root verb families and requires ownership at individual routes. Namespace/resource first gives each standard family a clear ownership boundary.
  • Require flags before all operands. Fits Go's flag parser directly but retains the ordering burden the user wants to remove.
  • Allow common flags before or between command-path components. Familiar in some command frameworks; increases command routing complexity. A single documented position after the complete path is sufficient initially.
  • Let the last singleton occurrence win. Convenient for argument assembly, but conceals conflicting operator instructions. Applications already have useful repeatable inputs; declare those explicitly instead.

Go's standard flag parser stops at the first operand; pflag supports interspersed options. The accepted backend decision selects Cobra with pflag for the initial implementation. Any required adaptation must use registered flag metadata, honor --, and correctly handle values beginning with -; do not replicate Notarius's hand-maintained flag-name lists in every consumer.

6. Configuration command contracts

config validate

Recommendation: invoke each selected scope's declared validation capability. CommandKit checks support and coordinates required provider calls. The provider implements acquisition, composition, defaults, decoding, and domain resolution according to its public result contract. The PromptKit adapter requests upstream configuration validation with all-definition coverage. Missing upstream support is a dependency of that capability, rather than the entire integration. ADR 0008 owns this boundary.

CommandKit's validation handler requests the declared configuration-validation operation and writes its result. Request preparation, execution, and operational preflight belong to custom domain handlers, such as session validate. Source acquisition and effects inside callbacks remain provider behavior. Registered input applicability determines the selectors used by standard validation.

Default text output is one concise success line identifying the validated scopes. --format yaml returns a structured success report containing the scope IDs. Warnings go to stderr and do not alone make configuration invalid. Failure emits no success report and returns status 1. Complete each scope's dependency loading once; stop on a fatal loading failure rather than promising a report of every possible defect across invalid inputs.

Credential requirements follow the genuine configuration lifecycle: ConfigKit loads can resolve typed secret references and therefore require the referenced environment values, whereas PromptKit inspection does not itself resolve provider credentials. Describe these requirements in scope help. Offline means no provider or remote execution; it does not promise credential-independent loading. Existing explicit credential-file bootstrap may run as part of acquisition, with its safe report through the supplied diagnostic channel. Callback effects and bootstrap policy remain application-owned; focused acquisition is recommended practice.

Alternatives:

  • Only check decoding/schema validity. Useful as a distinct lower-level operation, but too weak for an unqualified application validation command.
  • Validate only definitions selected by a planned run. Audita provides useful evidence for selected-profile checks, but a generic configuration command should find invalid configured definitions before a later selection reaches them.
  • Silent success. Scriptorium demonstrates this and scripts can use the exit status. A short scope summary is clearer for interactive use; structured output supports automation without parsing prose.
  • Skip secret resolution by default. Valuable for inspection without runtime credentials, but requires an explicit upstream ConfigKit lifecycle design. Do not simulate credentials or bypass native validation to deliver it.

config show

Recommendation: show the complete final effective configuration for the selected scope, or each registered scope when --scope all is supplied. Use the captured inspection snapshot, including recorded resolution, rather than raw file text, an application-selected subset, or a fresh marshal of mutable runtime structs. Loading necessarily performs the validation required to obtain a usable snapshot. Single-scope display loads only that scope and its dependencies; all loads every registered scope and reuses their shared dependencies.

Output is native redacted YAML without a status line. A single-scope report has no heading or document marker added by CommandKit. Preserve ConfigKit's distinction between null, empty containers, and absent values, its stable mapping order, and sequence order. This is an inspection document, not a guaranteed reloadable configuration file.

Accepted: --scope all writes the application configuration followed by the PromptKit configuration when registered, then any additional scopes in the order reported by config scopes. Each scope is a separate document, introduced by --- and a # scope: NAME comment. CommandKit adds those separators and labels around the native writer output without merging mappings or serializing runtime structs. Even a one-scope all report uses this labeled document form.

---
# scope: app
server:
  addr: localhost:8080
---
# scope: promptkit
profiles: {}

The stream provides a complete inspection in one invocation, preserves native rendering and redaction, and avoids collisions between scopes' mapping keys. Its tradeoffs are larger output and the need for automated consumers to decode multiple documents. Comments are human labels, not parsed metadata; scope identity for automated consumers follows the documented order. This is suitable YAML for automation, but it is not one configuration document or a reloadable combined file.

Check operation availability, acquire all selected views, and complete validation required by their provider contracts before starting output. A scope load failure returns 1 without a partial successful report. Writer failures can still leave partial output. A broken PromptKit configuration can therefore prevent --scope all from displaying otherwise usable application settings; --scope app remains available for that narrower inspection.

config show has one initial report format, YAML, and does not need a --format selector. JSON is deferred without a delivery commitment. The combined display uses existing native writers and does not require a new structured effective snapshot API solely for formatting.

Alternatives:

  • A scope-keyed single YAML document. Scope names become machine-readable keys, but the renderer must wrap and indent each native document. This could replace comment/order identification if consumers need scope identity in parsed data.
  • Emit each scope as soon as it loads. Reduces waiting before output, but a later load failure would leave an incomplete combined report. Prefer loading all scopes first.
  • Raw composed input. Shows directives and authored values, but omits defaults and resolution and therefore answers a different question.
  • Application-defined compact views. Audita's selected-profile summary is useful domain reporting, but does not replace a complete effective snapshot.
  • A reloadable export command. Potential future feature with a different contract; do not imply that inspection output supports round trips.

config sources

Recommendation: list the acquired and synthetic input sources for the selected scope in the owning library's recorded order. Preserve every source's ID, label, path when available, layer, kind, underlay index, and import parent location. Do not sort sources alphabetically or flatten them into one winning file per field. Composite and inheritance provenance belongs to explanation records.

Default text output is a readable table; --format yaml returns the common source report with an ordered sources sequence. Defaults-only loading may legitimately produce an empty sequence. Defaults and domain derivations are not fabricated file sources. Both application and PromptKit scopes use the same complete record shape, replacing Scriptorium's differing source projections.

Alternatives:

  • Field ownership under sources. Narratio's report is useful but answers why a field has its value. Use config explain for that task.
  • Only list filenames. Short, but loses underlays, non-file sources, import relationships, and CLI contributions that help diagnose configuration.
  • YAML by default. Suitable for automation and consistent with native configuration reports. Prefer a table for interactive discovery and an explicit YAML format for structured consumption. Scriptorium's current JSON default is evidence to evaluate, not a format requirement for CommandKit.

config explain

Recommendation: require --path POINTER; an explicitly supplied empty string selects the root. Use ConfigKit JSON Pointer semantics, including ~0 and ~1 escaping, mapping keys containing dots, and effective or historical paths. Omitted path and malformed pointer are usage errors; a well-formed unknown path is a command failure. Do not impose additional canonical-spelling restrictions beyond the owning library's supported path contract without a demonstrated need.

Default text output delegates to the native safe explanation writer. It includes the effective value and presence, effective origin, composite source locations, inheritance, chronological before/after values, resolution reasons and dependencies, and safe secret-reference metadata when available. Historical deleted paths remain explainable. Values receive native historical redaction.

--format yaml projects the native structured explanation into CommandKit's structured report. Keep value snapshots as records with present, redacted, and yaml fields, matching the upstream representation rather than inventing a typed value model for historical values. The yaml field holds native snapshot text as a string rather than decoding it into a new value. History remains chronological.

app config explain --path /server/addr
app config explain --scope promptkit --path '/profiles/team~1summary/model'
app config explain --path ''

Alternatives:

  • One positional pointer. Notarius demonstrates this and it is concise, but a named flag makes the required empty root explicit and allows a future distinct operand without ambiguity. Either convention could work if used everywhere.
  • Value-free provenance only. Audita and Narratio demonstrate useful compact reports; native redacted history gives a more complete explanation. A future summary format could omit values without changing this command's meaning.
  • explain --all listing every field. Useful for audits but potentially very large and expensive. Root explanation provides a starting point; defer exhaustive field-origin reporting until its traversal and summary contract are designed.

config diff

Recommendation: compare two independently resolved effective configurations in the same scope. The ordinary selectors define the left side. At least one of --against-config PATH or --against-overlay NAME explicitly defines the right side; together they can change both root and overlay. Unspecified selectors are copied from the resolved left selection, rather than running discovery again. An omitted right overlay inherits the left overlay; --against-overlay default explicitly selects the base. Domain context selectors and typed overrides apply identically to both sides in this version.

app config diff --config ./config.yml --overlay staging --against-overlay production
app config diff --config ./old.yml --against-config ./new.yml --against-overlay default

For a scope declaring safe comparison support, acquire both views through the registered provider's loading/validation contract. Compare effective values, including defaults and recorded domain resolution, rather than YAML formatting, source IDs, file order, or overlay names. Recursively compare mappings; keep sequences atomic, preserving their order. Distinguish absence, null, empty mappings, and empty sequences. Sort differences by JSON Pointer and report added, removed, and changed with safe before/after snapshots. Root or container replacement must not produce redundant descendant differences.

Default text output is a readable change report, or no differences when appropriate. --format yaml supplies a structured report with selection metadata, an ordered differences sequence, and explicit information about redacted comparison gaps. Successful comparison returns 0 whether differences exist or not.

Compare only values that the owning library makes available for inspection. Secret bytes must never be fetched to improve a comparison. A masked value is not an ordinary literal string and identical masks do not prove secret equality. Apply the union of both sides' sensitivity classifications to before/after values so a value classified on either side cannot leak from the other report. When sensitivity prevents comparison, identify the path as not comparable; do not print no differences as an unqualified equality claim. This needs upstream structured sensitivity metadata, not string matching on [REDACTED].

Alternatives:

  • Two positional overlay names. Narratio demonstrates a simple interface, but it cannot also compare different root files without another syntax.
  • Fully separate left/right domain selector sets. More flexible for campaign comparisons; adds substantial flag duplication. Shared context covers the existing overlay use case. Add independent contexts later if needed.
  • Diff rendered YAML lines. Easy to implement, but formatting changes obscure meaningful changes and identical masks conceal comparison gaps.
  • Return a special nonzero status for differences. Useful for CI assertions, but makes a successful inspection look like a failed command. Consider an explicit future check mode; keep ordinary comparison successful.

7. PromptKit definition inspection

Recommendation: optional definition commands use the same application root, overlay, and registered framework source selectors as config --scope promptkit. They always target PromptKit, so they do not accept --scope. They load the engine once and use its captured definitions without provider calls, capacity admission, request preparation, artifact loading, or credential lookup by PromptKit.

promptkit list prompts reports available (ID, version) pairs sorted by ID then version; promptkit list profiles reports exact profile IDs sorted lexically. IDs are identifiers, not filesystem paths, and prompt versions need not be semantic versions.

promptkit show prompt ID uses PromptKit's existing version-selection rules: omission of --prompt-version works only when that ID has one available version. Show the definition ID, selected version, opaque equality hash, declared default profile, declared inputs, and output contract. It does not resolve the declared default profile or render request-specific message templates. A command to show fully rendered requests would need inputs and belongs to application preparation such as render.

promptkit show profile ID reports the backend-completed named execution target: profile ID, backend, endpoint, model, configured provider controls, credential reference name, and whether a direct credential will be required. Do not suggest that a named profile is the profile selected by every request, that zero indicates a provider default, or that credential references establish credential availability.

All four commands default to readable text and accept --format yaml. Report fields and their structured YAML shapes are defined by CommandKit; arbitrary public Go structs are not an output contract. Unknown IDs or unavailable versions return 1; missing or empty required IDs and invalid argument counts return 2.

Provider parameter redaction belongs upstream in PromptKit. Notarius's curated profile report is useful evidence, while Scriptorium's profile output does not share configuration inspection's redaction contract. Establish a supported safe profile reporting API before adding arbitrary provider parameters to the standard report. Do not duplicate credential-name heuristics in CommandKit or assume that raw InspectProfile results have native configuration redaction.

Alternatives:

  • Inspect definitions only through config show/explain. Adequate for source presence and history, but does not clearly expose backend-completed targets or catalog discovery.
  • Show raw prompt message content by default. Can help authors, but the current public prompt inspection reports metadata, and request rendering is a different operation. Add content inspection deliberately upstream if needed.
  • List definitions by parsing effective YAML in CommandKit. Avoids new APIs, but takes ownership of PromptKit's schema and version model. Public catalog methods are the better boundary.

8. Help and version

Help

Recommendation: app, app help, app --help, and app -h print root help and succeed. A command group with no execution handler, such as app config, prints group help on bare invocation and succeeds. Runnable commands follow their registered handler and operand requirements, including when they also have subcommands. ADR 0006 records this distinction. app help config explain and app config explain --help print the same leaf help with status 0.

Help never acquires configuration, constructs PromptKit, resolves credentials, or allocates runtime state. Generate it from registered commands and flags, including application-owned commands, available scopes, selector applicability, format choices, and examples. An unknown command path is a usage error. A help request bypasses missing required operands; it does not make an otherwise unknown command or unknown flag valid.

Alternatives:

  • Only root help. Notarius's current leaf help errors demonstrate why this is insufficient for an operator trying to discover arguments.
  • Error on a bare root or family. Narratio and Scriptorium do this in some cases. Printing help successfully makes exploration consistent; an explicitly malformed leaf invocation still fails.
  • Maintain separate help strings. Straightforward, but they drift from parsing definitions. Registration should be the shared source.

Version

Accepted: app version prints app VERSION and a newline; root app --version reports the same application identity. The version command defaults to --scope app. app version --scope commandkit explicitly reports the embedded CommandKit release version. --format yaml changes presentation of the selected identity. Version subjects are separate from configuration scopes. CommandKit uses build metadata with acquisition callbacks left uninvoked.

Future component identities can use registered version metadata. ADR 0004 records version selection.

Applications supply build identity, with tagged Go build information usable when available. An unversioned development build reports dev; unknown revision or build time stays absent rather than being invented. The CommandKit release version identifies the library and its standard CLI contracts. Compatibility changes follow the release policy.

Alternatives:

  • Only the command or only the flag. Either could be consistent; supporting both familiar discovery paths costs little and gives them one implementation.
  • Emit all build and dependency information by default. Helpful for debugging, but unnecessarily verbose and unstable for a simple identity check. Structured output should expose the selected fields.
  • List dependency versions alongside application version by default. Adds diagnostic detail to an identity check. Explicit subject selection keeps each report focused.
  • Use a combined dependency-version option. Useful for a broad inventory. Scoped selection also supports querying one embedded component.
  • Add a separate integer CLI standard revision. Rejected during ADR review. The tagged CommandKit release version provides the shared compatibility identity.

9. Output, diagnostics, and failures

Recommendation: successful reports go to stdout; warnings, loading diagnostics, bootstrap reports, and errors go to stderr. Emit each diagnostic once, retain library source coordinates and source-ID legends, and use library-safe messages. No progress messages or banners may enter report output.

Operation Default Other initial format
help text none
version text YAML
config scopes, validate, sources, explain, diff text YAML
config show (one scope or all) YAML none
promptkit list/show text YAML

Format names are lowercase. Unsupported formats are usage errors. Use --format consistently for commands that offer text and YAML; do not add a selector to YAML-only config show. Structured reports other than the native config show documents use a defined operation-specific schema and the applicable scope or selection metadata. Define their exact YAML field shapes before implementation; do not make native explanation prose or Go struct field spelling a machine contract. Structured YAML reports contain one document with a trailing newline; config show --scope all deliberately uses a multi-document stream.

Accepted: defer JSON output for the initial implementation across the suite. YAML is suitable for automated consumption, and the native inspection writers already preserve its value semantics. There are no initial JSON format flags, JSON-specific report schemas, or JSON-driven upstream API requirements. Adding JSON is optional future work only if a concrete need emerges; it may never be needed. JSON Pointer paths remain supported independently of output format.

Inspection formats use upstream secret classification; there is no unredacted output option in this standard. Redaction does not classify every sensitive prompt or arbitrary application value. The proposal preserves library confidentiality semantics rather than promising that every report is safe for public disclosure.

Default to stdout and shell redirection; do not initially add --out or --output to standard reports. Ordinary writer failures may leave partial output and return 1; checking status before decoding is recommended caller practice. This avoids adding filesystem creation, replacement, and retention behavior to inspection.

Status CommandKit-owned command meaning
0 Successful operation, including help, valid configuration, or comparison that found differences.
1 Loading, validation, lookup, explanation, comparison, cancellation, diagnostics delivery, or output failed after valid syntax.
2 Unknown command/flag/scope, invalid format, missing or malformed argument, repeated singleton, or invalid flag combination.

Applications retain domain result statuses for their own commands. CommandKit must not globally reinterpret Scriptorium's execution validation status or Notarius's artifact rejection semantics. Common usage errors can use the shared status 2, but business outcomes remain explicit application mappings.

Alternatives:

  • Structured YAML everywhere by default. Works for automation, but text help, tables, and native explanations are useful for interactive inspection. Offer YAML explicitly where the default is text.
  • JSON as an additional output format. Deferred by the accepted decision. It would add serializers, report contracts, and possible representation issues without a demonstrated benefit over the initial YAML reports.
  • An output-file flag on every command. Convenient but duplicates shell redirection and introduces file semantics. Add it later if concrete consumers need behavior beyond redirection.
  • One nonzero status for every failure. Narratio and Scriptorium demonstrate simplicity, but distinguishing malformed invocation from a failed valid command is useful and already demonstrated by Notarius.
  • One exit policy for all application execution outcomes. Would exceed the ownership boundary. Coordinate changes to owned commands without claiming domain statuses for unrelated commands.

10. Implementation dependencies and delivery

Recommendation: implement the common command behavior against narrow inspection providers, with ConfigKit and PromptKit retaining ownership of configuration semantics. Publish upstream API improvements before adopting them in CommandKit and the applications. Do not import sibling internal packages or turn source checkout proximity into an architectural dependency.

Existing public APIs already support native YAML, native explanation text, structured explanation records, source records, and named definition inspection. They do not yet provide everything recommended here:

Needed capability Implementation work for that capability
Semantic diff A safe structured effective snapshot from ConfigKit, preserving value semantics and per-path sensitivity; expose it through PromptKit inspection. This serves comparison rather than JSON output.
Combined configuration display Compose native writers as labeled separate YAML documents after all selected scopes load; no new snapshot API is needed just to concatenate these reports.
Prompt/profile catalogs Public PromptKit methods enumerating captured IDs and prompt versions without reparsing source YAML.
Safe rich profile reports PromptKit-owned redacted inspection data for provider parameters.
Narratio's final application snapshot Adapt its configuration boundary so campaign-derived effective values and provenance are recorded in the inspected result; avoid an unrelated typed-struct dump.
Cross-scope validation Registered validation capabilities and dependencies, with their actual acquisition and credential requirements exposed in help.

Suggested sequence:

  1. Resolve the remaining proposed command contracts and alternatives, preserving the accepted planning decisions above.
  2. Define the structured YAML report schemas and minimal upstream inspection additions. Record accepted ownership decisions in durable architecture documentation.
  3. Implement core dispatch, syntax, help, version, scopes, validation, native display (including show --scope all), source inventory, and explanation using existing APIs.
  4. Integrate Scriptorium and Notarius to exercise both scopes and remove duplicated handlers. Register and report the supported capabilities; additional upstream features can be integrated independently.
  5. Add safe semantic comparison and additional PromptKit definition capabilities as their upstream APIs are released.
  6. Integrate Audita and Narratio, moving domain reports outside reserved families and aligning their standard operations. Update consumer adapters and docs for the intentional breaking releases.

CommandKit installs supported operations with their standard meanings and exposes actual capabilities in help and scope metadata. Missing comparison or catalog support leaves other operations available. Each family's resources determine its supported verb set. Initial ConfigKit/PromptKit reports retain their chosen formats; future families define suitable report contracts for their own resources.

Later implementation validation should exercise genuine contract risks: help with invalid configuration, leaf flag placement and --, explicit empty/false/zero overrides, source order, historical paths, independent diff selections, sensitivity changes between sides, multi-document scope order, all-scope load failures before output, warning delivery and failed writers, and Narratio's subprocess consumers.

Breaking adoption replaces divergent spellings and behavior. Compatibility aliases are not required; introduce one only for a concrete transition need with an explicit retirement decision. Application execution commands outside the reserved suite remain independently owned, even when their implementations use CommandKit helpers.

Alternatives:

  • Extract one existing application's command family unchanged. Fast, but carries its omissions and incidental decisions into the standard. Use existing implementations to inform and exercise the selected contract instead.
  • Build one universal runtime/application framework. Could absorb more code, but would expand ownership into lifecycle and domain execution without improving the demonstrated inspection boundary.
  • Implement missing upstream semantics inside CommandKit. Avoids release coordination, but duplicates configuration projection, redaction, and definition knowledge. Those responsibilities should remain with the libraries that own them.

11. Explicitly deferred commands

Recommendation: defer configuration mutation (set, edit, init, migrate), reloadable export, credential diagnostics, live provider probes, completion installation, exhaustive field-origin inventories, request preparation, and domain artifact catalogs from this standard. Their semantics are not established by the repeated inspection code under review. Add a shared command when its operator purpose and ownership can be specified as clearly as the suite above.

JSON output is also deferred, with no assumption that it will eventually be added.

Alternatives:

  • Add a generic doctor command now. Attractive for troubleshooting, but mixes offline configuration, credentials, network health, and domain resources. Specify those checks before choosing a shared name and success meaning.
  • Standardize run in this first suite. Could further improve vocabulary, but requires comparing execution semantics and their consumer contracts. A recommended verb is useful now; reserving its complete contract is separate work.