Repository navigation
[Feature]: Supported run-scoped access to a workflow run's definition #4792
Description
Problem Statement
Integrations that drive or visualise Spec Kit workflows (for example, a GitHub Action that renders workflow progress and human review gates in issue comments) need the workflow definition that a run was created with, not just its runtime state.
Today:
specify workflow status <run_id> --jsonreturns runtime state only (run_id,workflow_id,status,current_step_index, per-step statuses, timestamps). It does not include the step tree, step types, or gate configuration (message, options,on_reject, ...).specify workflow info <workflow_id>resolves the currently installed workflow by ID, has no--jsonoutput, and only lists top-level step IDs and types.- The only way to get the exact definition used by a run is to read
.specify/workflows/runs/<run_id>/workflow.ymldirectly. The engine already writes this snapshot at run start, andworkflow resumeprefers it over the installed workflow. However, the file is an internal run-state detail, not a supported contract.
As a result, integrations either depend on Spec Kit's internal storage layout and file format, or they load the installed workflow by ID. The second option breaks as soon as the workflow is updated or removed after the run started.
Proposed Solution
Add a supported, run-scoped view of the workflow definition. It should be separate from workflow status, which stays focused on execution progress.
The exact surface is open for assessment. One option is a CLI command with JSON output, for example specify workflow definition <run_id> --json (or a --definition flag on an existing command), backed by a library function that integrations can call directly.
The view should return:
- workflow metadata (
id,name,version, and other top-level fields as appropriate); - the step tree, including nested steps (e.g. inside
if/switch/ loop / fan-out constructs); - gate configuration: message, options, verdict/
on_rejectbehaviour; - the definition exactly as persisted for the run, resolved from the run and not from the currently installed workflow.
Illustrative shape (from the discussion, not final):
{
"run_id": "662bf791",
"definition": {
"workflow": {"id": "spec-review", "name": "Spec review", "version": "1.0.0"},
"steps": [
{"id": "draft", "type": "command", "command": "speckit.specify"},
{
"id": "approve",
"type": "gate",
"message": "Review the specification.",
"options": ["approve", "reject"],
"on_reject": "abort"
}
]
}
}This should be a stable abstraction over the run's definition, not a raw dump of the underlying YAML.
Alternatives Considered
- Reading
.specify/workflows/runs/<run_id>/workflow.ymldirectly. This is the current interim approach. It works, but it couples integrations to internal layout and format. - Loading the installed workflow by
workflow_id(e.g. viaworkflow info). This is inaccurate once the installed workflow changes or is removed after the run started. - Extending
workflow status --jsonwith the full definition. This mixes execution state with static definition data and bloats every status poll.
Component
Specify CLI (initialization, commands)
AI Agent (if applicable)
Not applicable
Use Cases
- A GitHub Action posts an issue comment that shows every step of a workflow run with its current status. It needs the full step tree, including steps that have not yet started, and the status alone does not provide these.
- At a human review gate, the Action renders the gate's message and available options (e.g. approve / reject) so reviewers can respond from the issue.
- After a workflow is updated or removed, the Action still renders historical or in-flight runs accurately against the definition they were started with.
Acceptance Criteria
- A supported interface (CLI with
--jsonand/or library API) returns the workflow definition for a givenrun_id - The definition is resolved from the run's persisted snapshot, not from the installed workflow
- Output includes workflow metadata, the full step tree (including nested steps), and gate configuration
- Clear error behaviour for unknown run IDs and runs without a persisted definition (JSON errors routed to stderr, consistent with
workflow status --json) - Positive and negative tests, including: definition still returned after the installed workflow is modified or removed; nested steps; gate fields
- Documentation of the output contract
Additional Context
- Discussion: Proposed enhancement: supported access to a run’s workflow definition #4727
- Open points for assessment: how to represent dynamic/nested steps (loops, fan-out, switch branches), whether the output should be normalised or mirror the authored structure, and the stability/versioning of the JSON contract.
AI Disclosure
Drafted with opencode using Claude Opus 5.5 (github-copilot/claude-opus-5.5), default reasoning settings, human-supervised. The agent wrote the issue text from discussion #4727 and a read of the workflow engine source (workflows/engine.py, command_status.py, command_info.py). It was reviewed and submitted by a human on behalf of the original discussion author.
Activity
mnriem commentedon Sep 30, 2026
Are you aware of the
specify artifactcommand? Would it appropriate you think to add support for it there?markuswondrak commentedon Sep 30, 2026
Yes, I looked at
specify artifactbefore opening this. I don't think it's the right home for this feature, for a few reasons:artifactlooks at installed commands, templates, scripts and hooks and shows how presets, extensions and the project layer compose them. This request is about a run: the definition snapshot saved when a run was created. It shouldn't change when the installed workflow is updated, overlaid or removed.artifactkeys everything by artifact name orlookupId. Supporting this would mean adding run IDs and a step/gate model to a group that has neither today.workflow status <run_id> --json: status reports what is happening, and this reports what the run was defined as. An integration would call both with the samerun_id.A
workflowkind inartifactcould make sense later for the installed definition and its overlay stack, similar to whatworkflow resolveshows. That would complement this request rather than replace it.I'd reuse
artifact's strict JSON contract, though:--jsonrequired, empty stdout on failure, and errors as{"error": ...}on stderr.If you'd still like it under
artifact, I'm happy to follow that direction.AI disclosure: This comment was drafted with opencode using Claude Opus 5.5 (
github-copilot/claude-opus-5.5), default reasoning settings, human-supervised, and posted on behalf of @markuswondrak after review.mnriem commentedon Sep 30, 2026
@markuswondrak I agree with you. Just wanted to make sure you were aware about the
specify artifactCLI command and yes on the strict JSON contract ;) So go for it!markuswondrak commentedon Sep 30, 2026
Ah yeah I saw (and liked it), but it didn't quite meet our requirements.
Will pick this up once my other open PRs are merged. Thanks!
markuswondrak commentedon Oct 3, 2026
@mnriem @digimangos: Regarding the open point: "the stability/versioning of the JSON contract."
If
definitionmirrors the authored/composed structure, its shape is tied to the workflowschema_version— so aschema_versionbump becomes a consumer-visible change to the CLI interface. Is that the intended version for integrations, or shouldworkflow definition --jsoncarry its own contract version independent of the workflow schema?Is that additional effort justified, or should we keep it simple for now and publish the internal structure as-is (mirroring the persisted definition), treating
schema_versionas the interface version?I lean toward mirroring unless there's a concrete need for a separate interface contract.
digimangos commentedon Oct 5, 2026
@markuswondrak personally I would keep it simple by mirroring the current schema version and internal structure. Ultimately it's the same data and likely that if that internal structure changes the interface would need updating anyway to expose or hide value changes.