Skip to content

Pipeline Design 2

Seth Ford edited this page Feb 8, 2026 · 3 revisions

Now I have complete context. Here's the ADR:


Design: Add --json flag to shipwright status for machine-readable output

Context

shipwright status (scripts/cct-status.sh, 169 lines) is a read-only dashboard that displays three sections: tmux windows, team configurations, and task progress. It currently accepts no arguments — it runs straight through with ANSI-colored output only.

Machine-readable output is needed for scripting, CI dashboards, and programmatic consumption. The project already has an established --json pattern in scripts/cct-daemon.sh (line 2884) where daemon_metrics uses jq -n with --arg/--argjson to build structured JSON, gated behind a json_output boolean flag.

Constraints:

  • Bash 3.2 compatible (no associative arrays, no ${var,,})
  • set -euo pipefail required
  • jq is available as a dependency (already used in daemon, fleet, cost scripts)
  • Script currently has no argument parsing loop — one must be added
  • The three data sections (windows, teams, tasks) each gather data inline while rendering — data collection and rendering are interleaved

Decision

Add a --json flag to cct-status.sh following the exact pattern from daemon_metrics:

  1. Argument parsing: Add a while [[ $# -gt 0 ]] loop at the top (before the header) with --json and --help flags.

  2. Separate data collection from rendering: Refactor each of the three sections to first collect data into shell variables, then conditionally output either ANSI-formatted text (default) or JSON. This is the key structural change — the current code interleaves echo statements with data parsing.

  3. JSON structure (via jq -n):

{
  "timestamp": "2026-02-08T22:15:00Z",
  "windows": [
    { "name": "claude-build", "session": "main:2", "panes": 3, "active": true }
  ],
  "teams": [
    { "name": "build-team", "members": ["lead", "dev", "tester"], "member_count": 3, "status": "configured" }
  ],
  "tasks": [
    {
      "team": "build-team",
      "total": 10,
      "completed": 7,
      "in_progress": 2,
      "pending": 1,
      "progress_pct": 70
    }
  ]
}
  1. Build JSON with jq: Use jq -n --argjson to compose the output. Collect windows/teams/tasks as JSON arrays in variables, then merge them into the final object. This avoids string interpolation of user data into JSON (per Common Pitfalls in CLAUDE.md).

  2. --help flag: Add a show_usage() function consistent with other scripts (e.g., cct-logs.sh).

  3. Exit early: When --json is set, output the JSON and exit 0 — don't fall through to the ANSI rendering.

Alternatives Considered

  1. Separate script (cct-status-json.sh) — Pros: no refactoring of existing script, zero risk to current behavior / Cons: code duplication, two scripts to maintain in sync, inconsistent with project patterns (daemon metrics uses a flag, not a separate script)

  2. Tab-separated / CSV output (--tsv) — Pros: simpler to implement, no jq dependency / Cons: harder to parse nested data (teams with member lists), not as useful for dashboards, jq is already a project dependency and used extensively

  3. Keep data collection interleaved, capture ANSI output and strip/parse it — Pros: minimal refactoring / Cons: fragile, regex-dependent, can't produce structured nested JSON from flat text, bad engineering practice

Implementation Plan

  • Files to create: scripts/cct-status-test.sh (new test harness for status command)
  • Files to modify:
    • scripts/cct-status.sh — add argument parsing, refactor data collection, add JSON output path
    • package.json — add cct-status-test.sh to the test script chain
  • Dependencies: None new (jq is already required by daemon, fleet, cost scripts)
  • Risk areas:
    • The tmux data collection (tmux list-windows) depends on a live tmux server. In --json mode when no tmux is running, the script should output an empty windows array rather than erroring. The existing 2>/dev/null || true handles this, but the refactored code must preserve that behavior.
    • The find + while read loops for teams/tasks currently set HAS_TEAMS/HAS_TASKS booleans — these must work both for the ANSI path (unchanged behavior) and the JSON path (populating arrays).
    • The grep -c in team member counting (line 78) is a known pipefail hazard — must use || true pattern (already does, but verify during refactor).
    • The task status parsing with grep -o + sed (line 122) is brittle — for JSON mode, consider using jq directly on task files if available, with fallback to existing grep approach for ANSI mode.

Validation Criteria

  • shipwright status --json outputs valid JSON (parseable by jq .)
  • shipwright status --json | jq '.windows' returns an array (possibly empty)
  • shipwright status --json | jq '.teams' returns an array with name, members, member_count fields
  • shipwright status --json | jq '.tasks' returns an array with total, completed, in_progress, pending, progress_pct fields
  • shipwright status (no flag) produces identical ANSI output to current behavior — zero regression
  • shipwright status --help prints usage information and exits 0
  • shipwright status --json with no tmux server running outputs {"windows": [], ...} without error
  • shipwright status --json with no teams/tasks directories outputs empty arrays
  • All numeric fields in JSON are numbers (not strings) — validated with jq 'type'
  • npm test passes with new cct-status-test.sh included
  • Test harness covers: --json output structure, empty state, --help flag, unknown flag error

Clone this wiki locally