# Roadmap: CLI Output Policy ## Purpose Define a shared CLI output policy before adding machine-readable JSON output to `distributor` commands. Current CLI output is human-readable text. That is appropriate as the default, but upcoming features need a common machine-readable contract so each command does not invent a separate JSON flag, envelope, warning policy, or error shape. ## Current Implementation Grounding The current CLI has these output-producing commands: - `version`; - `run`; - `validate`; - `inspect`. Current output is text-only. `run` has the most complex output because it can print warnings, pipeline summaries, planned destination actions, partial destination failures, and a final status line. `validate` and `inspect` are currently local-path commands. `version` prints a single text line. Planned `distributor manifest create`, remote `validate` and `inspect`, link generation, and latest path destinations increase the need for structured output that scripts can consume consistently. ## Goals - Define one CLI-wide policy for text and JSON output. - Preserve current human-readable text output as the default. - Add JSON output consistently across output-producing commands when this roadmap is implemented. - Keep warning, error, and partial-failure behavior predictable. - Avoid external dependencies; use Go's standard `encoding/json`. ## Non-Goals - Do not add YAML, table, NDJSON, streaming JSON, or template output formats. - Do not change source manifest, destination state, config, or backend schemas. - Do not make help or usage output JSON. - Do not turn ordinary fatal setup errors into structured JSON results. - Do not add a root-global output flag in the first implementation. ## Shared CLI Policy Each supported command should accept: ```sh --format text --format json ``` Policy: - `text` is the default and preserves existing output unless a feature explicitly changes text output. - `json` writes exactly one JSON document to stdout. - help and usage output remain text-only. - invalid `--format` values are usage errors. - `--format` is a per-command flag, not a root-global flag. - a command must not accept `--format json` unless it emits the shared JSON envelope for that command. The first implementation should add JSON support for all current output-producing commands rather than leaving a mixed CLI where some commands support `--format` and others do not. ## Stdout, Stderr, Warnings, and Errors Text mode keeps the current behavior: - normal command output goes to stdout; - warnings may be printed as text; - command errors are printed to stderr by CLI error handling. JSON mode: - successful commands write one JSON document to stdout; - warnings are included in a top-level `warnings` array and are not duplicated to stderr; - fatal setup errors that prevent construction of a result write text errors to stderr, write no JSON stdout, and exit non-zero; - commands with meaningful partial results may write a JSON document with `ok: false`, structured failure details, and a non-zero exit status. `run --format json` should use the partial-result behavior when planning or execution has begun and one or more destinations fail. This lets automation inspect successful actions, failed actions, warnings, and final counters even when the process exits non-zero. ## JSON Envelope All JSON-mode commands should use a common top-level envelope: ```json { "schema_version": 1, "command": "inspect", "ok": true, "warnings": [], "result": {} } ``` Envelope fields: - `schema_version`: integer version of the CLI JSON output schema. - `command`: command name, using the public CLI command path where useful, such as `manifest create`. - `ok`: boolean success indicator for the command result. - `warnings`: array of structured warning objects. - `result`: command-specific result object. - `errors`: optional array of structured error objects for commands that can return partial results. Field rules: - use stable snake_case field names; - use RFC3339 timestamps; - use numeric JSON values for sizes and counts; - use slash-separated logical paths for bundle, source, destination, and output paths; - never include secret values; - keep command-specific data under `result`; - add fields compatibly where practical. Warning objects should include at least: ```json { "message": "secret OBJECT_STORAGE_KEY ignored because the real environment already has that variable" } ``` Error objects for partial results should include enough context for automation and troubleshooting, such as pipeline id, destination id, backend, bundle path, and message when those values are available. ## Command Adoption `version`: - text mode keeps the current version string; - JSON mode reports application name and version under `result`. `validate`: - text mode keeps the current validation summary; - JSON mode reports bundle count and selected bundle identifiers; - future remote validation uses the same envelope and adds pipeline/source context under `result`. `inspect`: - text mode keeps human-readable bundle metadata; - JSON mode reports normalized bundle metadata, including source-relative path, bundle id, created timestamp, digest, file count, total size, and file records. `run`: - text mode keeps the current progress and final status style unless a future feature intentionally changes it; - JSON mode reports warnings, pipeline summaries, destination action records, output records, final counters, dry-run status, and partial failure records; - JSON mode may write `ok: false` with partial results and still exit non-zero. Future `manifest create`: - should use `--format text|json`; - text mode should keep concise human success output; - JSON mode should report the generated manifest summary under `result`; - the command should not introduce a separate `--json` flag. ## Relationship To Other Roadmaps `distributor manifest create` should depend on this roadmap for JSON summary output and should not add a command-specific JSON flag. Remote `validate` and `inspect` should use this policy for machine-readable source validation and inspection. Link generation should expose primary links through JSON command output only after this policy is implemented or selected for implementation. Latest path destinations should report fixed-path selection and destructive replacement summaries through the same `run --format json` result model. ## Testing Expectations Suggested coverage: - CLI parsing accepts `--format text` and `--format json` for supported commands; - CLI parsing rejects invalid `--format` values as usage errors; - help and usage output remain text-only; - each JSON-capable command emits exactly one valid JSON document to stdout on success; - text mode preserves existing output; - JSON-mode warnings appear in `warnings` and are not duplicated to stderr; - fatal setup errors write no JSON stdout and return non-zero; - `run --format json` emits partial-result JSON with `ok: false` and exits non-zero when one or more destination failures occur after planning begins; - JSON output uses RFC3339 timestamps, numeric sizes and counts, and slash-separated logical paths; - no JSON output includes secret values; - documentation consistency checks find no unsupported `--json` references. ## Documentation Updates After Implementation - Update `docs/cli.md` with the shared `--format text|json` policy. - Update command examples only for implemented JSON behavior. - Update `docs/operations.md` where JSON output materially improves automation workflows. - Update `docs/troubleshooting.md` only for implemented JSON-mode recovery behavior. - Update relevant roadmap files when JSON output is no longer deferred. Keep this roadmap under `docs/roadmap/` until implemented. ## Decisions - Use per-command `--format text|json`; do not introduce `--json`. - Keep `text` as the default for backward compatibility. - Implement JSON support for all current output-producing commands when this roadmap is selected. - Keep help and usage output text-only. - Put JSON-mode warnings in the top-level `warnings` array. - Allow `run --format json` to emit partial-result JSON with `ok: false` and a non-zero exit status. - Keep fatal setup errors as text stderr with no JSON stdout. ## Future Work - Consider a root-global output flag only if the command parser is later refactored around shared root options. - Consider additional formats only if a concrete consumer requires them. - Consider a versioned JSON schema reference after the first JSON-capable release.