Files
weatherreporter/docs/roadmap/cli.md

7.7 KiB

CLI Output Roadmap

Purpose

This roadmap defines the intended final shape for weatherreporter CLI output.

The current CLI has drifted:

  • run commands emit JSON summaries to stdout and compact status lines to stderr.
  • inspect commands emit JSON to stdout.
  • generate commands 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 --quiet intentionally 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 --quiet for state-changing action commands.
  • Do not make --quiet suppress 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 today
  • weatherreporter generate daily --date YYYY-MM-DD
  • weatherreporter run morning
  • weatherreporter 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 reports
  • weatherreporter inspect metadata RUN_ID
  • weatherreporter inspect modules RUN_ID
  • weatherreporter inspect data-package RUN_ID
  • weatherreporter inspect prior RUN_ID
  • weatherreporter 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 parsing
  • internal/cli/output.go: stdout/stderr writers, quiet-mode handling, and output category helpers
  • internal/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) error as 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) error
  • writeActionResult(stdout, stderr io.Writer, result actionResult, opts outputOptions) error
  • writeBatchStatus(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 --format support
  • 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.