7.7 KiB
CLI Output Roadmap
Purpose
This roadmap defines the intended final shape for weatherreporter CLI output.
The current CLI has drifted:
runcommands emit JSON summaries to stdout and compact status lines to stderr.inspectcommands emit JSON to stdout.generatecommands perform substantial work but are silent on success.
The target is a predictable command-line contract that is useful for operators, easy to consume from scripts, and explicit enough that future commands naturally reuse the same output path.
Locked Decisions
- Keep application orchestration and domain decisions in
internal/app. - Keep CLI presentation, stdout/stderr policy, and quiet-mode behavior in
internal/cli. - Successful non-help commands should have a machine-readable JSON stdout
contract unless
--quietintentionally suppresses success output for an action command. - Help remains human-readable text.
- Stderr is for compact operational status and errors, not primary command payloads.
- Do not print partial JSON when command construction, flag parsing, config loading, or pre-run validation fails.
- Do not serialize large internal app objects directly as CLI output.
- Do not expose secret values in stdout or stderr.
- Add
--quietfor state-changing action commands. - Do not make
--quietsuppress requested inspection data.
Command Categories
CLI commands should be classified into one of these output categories.
Help Commands
Examples:
weatherreporter --help
Output:
- stdout: human-readable help text
- stderr: none on success
--quiet: not applicable
Action Commands
Examples:
weatherreporter generate todayweatherreporter generate daily --date YYYY-MM-DDweatherreporter run morningweatherreporter run evening
Output:
- stdout: compact JSON summary after the action completes
- stderr: compact status lines only when useful, especially for multi-report batch commands
--quiet: suppress success stdout and routine status stderr
Failure behavior:
- For flag/config/pre-run errors, stdout is empty and the command returns an error.
- For completed actions that produce an inspectable failure result, default output may still include a JSON failure summary before returning nonzero.
- With
--quiet, failure diagnostics should remain concise and actionable on stderr through the existing top-level error path; routine success summaries stay suppressed.
Inspection Commands
Examples:
weatherreporter inspect reportsweatherreporter inspect metadata RUN_IDweatherreporter inspect modules RUN_IDweatherreporter inspect data-package RUN_IDweatherreporter inspect prior RUN_IDweatherreporter inspect sources RUN_ID
Output:
- stdout: requested JSON data
- stderr: none on success
--quiet: not accepted unless a future inspection command has auxiliary status output to suppress
Inspection commands are already data-oriented. Their stdout payload should stay focused on the requested data rather than being hidden by quiet mode.
Target Action Summary Shape
Action command JSON should be small, stable, and path-oriented. It should expose what an operator needs to find artifacts, inspect a run, and understand notification status.
Generate Summary
Target shape:
{
"command": "generate",
"reportId": "today",
"reportName": "Today Report",
"promptId": "weather.today_generated_text",
"runId": "20260529T100000.000000000Z_today",
"status": "succeeded",
"generatedAt": "2026-05-29T10:00:00Z",
"validPeriod": {},
"reportPath": "workspace/reports/today/2026-05-29/report.20260529T100000.000000000Z_today.md",
"outputPath": "./today.md",
"metadataPath": "workspace/snapshots/today/2026-05-29/metadata.20260529T100000.000000000Z_today.json",
"dataPackagePath": "workspace/data-packages/today/2026-05-29/data_package.20260529T100000.000000000Z_today.yaml",
"preflightPath": "workspace/preflight/today/2026-05-29/render.20260529T100000.000000000Z_today.json",
"notificationPath": "workspace/notifications/today/2026-05-29/distributor.20260529T100000.000000000Z_today.json",
"notification": {
"status": "succeeded",
"runId": "distributor-run",
"pipelineId": "weatherreporter.today",
"bundleId": "weatherreporter.home.today",
"path": "workspace/notifications/today/2026-05-29/distributor.20260529T100000.000000000Z_today.json"
}
}
Rules:
- Omit absent optional paths with
omitempty. - Include generated-text artifact paths only for report modes that produce them.
- Include notification fields only when notification was attempted.
- Keep module snapshot contents, data package contents, raw generated text, render result bodies, and full notification adapter payloads out of the CLI summary.
Batch Summary
The current BatchResult shape is close to the target and should remain the
basis for run output. The target update is to make the summary explicitly
command-like and align status semantics with generate output:
{
"command": "run",
"batch": "morning",
"status": "succeeded",
"startedAt": "2026-05-29T10:00:00Z",
"finishedAt": "2026-05-29T10:01:00Z",
"total": 3,
"succeeded": 3,
"failed": 0,
"notification": {},
"reports": []
}
Rules:
- Keep per-report items compact and path-oriented.
- Keep batch notification status at the top level.
- Preserve nonzero exit behavior when one or more reports fail.
- Preserve the existing behavior that batch report failures do not prevent the JSON summary from being available in default output mode.
Quiet Mode
--quiet should be available on action commands:
weatherreporter generate today --quiet
weatherreporter run morning --quiet
Quiet mode means:
- no stdout on successful action commands
- no routine status lines on stderr on successful action commands
- errors still return nonzero and are still reported by the top-level CLI error path
- inspection output is not suppressed
Quiet mode does not mean:
- skipping artifact writes
- skipping distributor notification
- changing JSON shape when JSON is emitted
- hiding errors
Future action commands should opt into quiet mode by using the centralized action-output writer rather than implementing their own flag or writer logic.
Intended Code Structure
internal/cli should own a small output layer that future commands can reuse.
The output layer should make the consistent path the easiest path.
Target files:
internal/cli/root.go: command routing and flag parsinginternal/cli/output.go: stdout/stderr writers, quiet-mode handling, and output category helpersinternal/cli/result.go: CLI-safe summary structs and conversion helpers
Target app-layer shape:
- Add a detailed generate entry point that returns the generated report result.
- Keep
app.Generate(ctx, GenerateRequest) erroras a convenience wrapper for callers that do not need CLI output. - Keep
app.RunBatchDetailed(ctx, BatchRequest) (*BatchResult, error)as the batch command result source.
Target CLI output helpers:
writeJSON(io.Writer, any) errorwriteActionResult(stdout, stderr io.Writer, result actionResult, opts outputOptions) errorwriteBatchStatus(stderr io.Writer, result *app.BatchResult)writeGenerateStatus(stderr io.Writer, result GenerateSummary)only if single-report status lines become useful
The command router should not call json.NewEncoder directly outside the
central output helpers.
Deferred Questions
None of these are required for the initial harmonization:
- global
--formatsupport - NDJSON progress streams
- human-readable success output
- machine-readable error envelopes on stderr
- making inspection commands use a common envelope
These should remain deferred until there is a real consumer need.