Files
distributor/docs/roadmap/cli_output_policy.md

8.4 KiB

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:

--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:

{
  "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:

{
  "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.