feat(workflows): add JSON output for workflow run resume and status#2814
feat(workflows): add JSON output for workflow run resume and status#2814doquanghuy wants to merge 2 commits into
Conversation
90e40f7 to
c734b8e
Compare
There was a problem hiding this comment.
Pull request overview
Adds an opt-in --json flag to specify workflow run, workflow resume, and workflow status so automation can consume machine-readable workflow outcomes (run id + terminal status) without scraping Rich-formatted text.
Changes:
- Added shared JSON payload/emission helpers and a
--jsonoption to the three workflow CLI commands. - Added pytest coverage for JSON output shape and for “no Rich markup/ANSI” on the JSON path.
- Updated workflow reference docs to describe the new flag.
Show a summary per file
| File | Description |
|---|---|
src/specify_cli/__init__.py |
Implements --json options and emits JSON payloads for run/resume/status. |
tests/test_workflows.py |
Adds CLI-level tests validating JSON output and ensuring it round-trips without ANSI/markup. |
docs/reference/workflows.md |
Documents the new --json flags for run/resume/status. |
Copilot's findings
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comments suppressed due to low confidence (1)
src/specify_cli/init.py:4253
--jsonmode currently doesn't ensure stdout remains a single parseable JSON object because workflow steps can still stream output to stdout during execution. In particular,commandsteps callIntegrationBase.dispatch_command(..., stream=True)by default (streams subprocess stdout/stderr directly to the terminal), andpromptsteps runsubprocess.run(...)without capturing output—both will interleave human output before the final JSON and breakjson.loads()for real workflows likeworkflows/speckit/workflow.ymlthat usecommand:steps.
try:
state = engine.execute(definition, inputs)
except ValueError as exc:
console.print(f"[red]Error:[/red] {exc}")
raise typer.Exit(1)
- Files reviewed: 3/3 changed files
- Comments generated: 1
| specify workflow run wf.yml --json | ||
| # {"run_id": "662bf791", "workflow_id": "wf", "status": "paused", | ||
| # "current_step_id": "review", "current_step_index": 0} | ||
| ``` |
There was a problem hiding this comment.
Addressed: the docs example is now pretty-printed (indent=2) to match exactly what the CLI emits, with a note that it's plain stdout with no Rich markup.
Adds an opt-in `--json` flag to `workflow run`, `workflow resume`, and `workflow status` that emits a single machine-readable object (run_id, workflow_id, status, current step; status also reports per-step states and a runs list) for automation and external orchestrators. JSON is written via a small `_emit_workflow_json` helper using plain stdout, so Rich markup, highlighting, and line-wrapping can never alter the emitted object. Default human-readable output and exit codes are unchanged when `--json` is omitted. Reference docs updated. Closes github#2811. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
c734b8e to
94f33e6
Compare
|
Thanks @copilot — fixed in the latest push. The @mnriem ready for review. |
| "current_step_id": state.current_step_id, | ||
| "steps": { |
There was a problem hiding this comment.
Addressed: the single-run status --json payload is now built on the shared _workflow_run_payload(state) ({**_workflow_run_payload(state), created_at, updated_at, steps}), so current_step_index — and every other common field — is identical across run/resume/status. A test asserts that equivalence so it can't drift again.
| "workflow_id": "wf", | ||
| "status": "paused", | ||
| "current_step_id": "review", | ||
| "current_step_index": 0 |
There was a problem hiding this comment.
Addressed: the example now uses my-pipeline.yml with "workflow_id": "build-and-review" and a one-line note that workflow_id is the YAML workflow.id, not the file name.
…json # Conflicts: # docs/reference/workflows.md # src/specify_cli/__init__.py # tests/test_workflows.py
|
Pushed an update that brings the branch up to date with Up to date with Copilot's notes.
The payload shape is otherwise unchanged from the original proposal. @mnriem a steer on whether this is wanted, and on the field set, would be appreciated. |
Description
Closes #2811.
Adds an opt-in
--jsonflag toworkflow run,workflow resume, andworkflow status, giving automation a machine-readable view of workflow terminal states (completed/paused/failed/aborted) and the run id, instead of scraping human prose or reading internalstate.jsonfiles.status --jsonadditionally reports per-step states (single run) or arunslist (no run id).What changed
--jsonoption on the three commands; a shared_workflow_run_payload()builder for run/resume and a shared_emit_workflow_json()emitter.console.print) so Rich markup, highlighting, and line-wrapping can never alter the object — verified by a test asserting the output contains no ANSI/markup and round-trips. (This is intentionally not routed through Rich the wayversion --features --jsonis.)--jsonis set, the step-progress callback and banners are suppressed so stdout is a single parseable object.docs/reference/workflows.md).Compatibility
--json) output and exit codes are unchanged.Testing
uv run specify --helpuv sync --extra test && uv run pytest— 3313 passed, 40 skipped;ruff check src/cleanrun/status(single + list)/resume--jsonproduce parseable output and that default output is unchangedTests in
tests/test_workflows.py::TestWorkflowJsonOutputcover completed/paused run JSON, status (single + list), resume JSON, default output staying human (non-JSON), and that--jsonoutput carries no markup/ANSI.AI Disclosure
Used Claude to implement the flag, tests, and docs. Behaviour was verified locally and the diff reviewed before submission.
Per CONTRIBUTING.md (new CLI arguments should be agreed first), #2811 is the discussion issue and this PR is the concrete proposal — happy to adjust the payload shape or hold it. @mnriem a steer on whether this is wanted and on the field set would be appreciated.