Skip to content

fix(workflows): escape the step-progress line so step ids render (and / stops failing the run) - #3783

Open
jawwad-ali wants to merge 1 commit into
github:mainfrom
jawwad-ali:fix/workflow-step-progress-markup
Open

fix(workflows): escape the step-progress line so step ids render (and / stops failing the run)#3783
jawwad-ali wants to merge 1 commit into
github:mainfrom
jawwad-ali:fix/workflow-step-progress-markup

Conversation

@jawwad-ali

Copy link
Copy Markdown
Contributor

Problem

workflow run and workflow resume install the same step-progress callback:

engine.on_step_start = lambda sid, label: console.print(f"  ▸ [{sid}] {label} …")

Rich parses the bracketed step id as a style tag. Both arguments come from workflow YAML and neither is escaped, which produces three distinct failures on main:

1. The step id is silently swallowed on every run — the only identifying content on the line.

workflow with `id: greet`  ->  "  ▸  shell …"      ("[greet]" absent)

2. An id that forms a closing tag fails the whole run. validate_workflow places no charset restriction on step ids (it rejects only non-strings, :, and duplicates), so id: "/" is an accepted workflow. The callback then raises MarkupError, which propagates into execute()'s handler:

rich.errors.MarkupError: closing tag '[/]' at position 4 has nothing to close
-> run persisted as `failed`, step_results: {}   (the shell step never ran)
-> exit 1 with a Rich internals error

3. An id that is a real style (bold, red, dim) is applied as formatting to the rest of the line. The unescaped label (from step_config["command"]) compounds all of this.

Fix

Escape the literal bracket with \[ and escape both interpolated values — at both sites (workflow_run line 1051 and workflow_resume line 1173; the arrow appears exactly twice in src/).

This mirrors the \[<type>] step-graph precedent already in this file (workflow_info), including its comment rationale.

Escaping only the values is not sufficient — the f-string's own brackets are what Rich consumes. Verified:

step id before escape values only this fix
greet ▸ shell … ▸ shell … ▸ [greet] shell …
bold ▸ shell … ▸ shell … ▸ [bold] shell …
/ MarkupError MarkupError ▸ [/] shell …
a]b ▸ b] shell … ▸ b] shell … ▸ [a]b] shell …

Verification

Driven through the real CLI (CliRunner, workflow run):

id='greet' exit=0 id_visible=True  line='  > [greet] shell ...'
id='bold'  exit=0 id_visible=True  line='  > [bold] shell ...'
id='/'     exit=0 id_visible=True  line='  > [/] shell ...'      <- was a failed run
id='a]b'   exit=0 id_visible=True  line='  > [a]b] shell ...'
  • 5 new tests (3 parametrized ids, the / crash, plus a resume-side case that drives run --jsonresume <run_id> so the twin is covered independently): all fail before this change, pass after.
  • TestWorkflowRunExitCodes (the neighbouring pre-existing class) still green — 9 passed together.
  • uvx ruff@0.15.0 check src tests clean.
  • Nothing depended on the broken output: no test or doc references the progress line (docs/reference/workflows.md documents only the --json payload).

AI-assisted: authored with Claude Code. I reproduced all three failure modes on main through the real CLI, confirmed that escaping only the interpolated values leaves the bug, and matched the existing \[ convention in this file.

@jawwad-ali
jawwad-ali requested a review from mnriem as a code owner July 28, 2026 11:17
… `/` stops failing the run)

`workflow run` and `workflow resume` both print the step-progress line as
`f"  ▸ [{sid}] {label} …"`. Rich parses the bracketed step id as a style tag,
which produces three failures on main:

1. The id is SILENTLY SWALLOWED on every run -- the only identifying content on
   the line. `id: greet` prints "  ▸  shell …"; "[greet]" is absent.
2. An id that forms a closing tag FAILS THE WHOLE RUN. `validate_workflow`
   places no charset restriction on step ids, so `id: "/"` is a valid workflow;
   the callback then raises MarkupError, which propagates into execute()'s
   handler -> run persisted as `failed` with empty `step_results`, the step
   never executed, exit 1 with a Rich internals error.
3. An id that is a real style (`bold`, `red`) is applied as FORMATTING to the
   rest of the line.

The unescaped `label` (from `step_config["command"]`) compounds it.

Escape the literal bracket with `\[` and escape both interpolated values, at
both sites. This mirrors the `\[<type>]` step-graph precedent already in this
file (workflow_info). Escaping only the values is NOT sufficient -- the
f-string's own brackets are what Rich consumes.

Verified through the real CLI: ids `greet`/`bold`/`a]b` now render verbatim, and
`id: "/"` goes from a failed run to `Status: completed`.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@jawwad-ali
jawwad-ali force-pushed the fix/workflow-step-progress-markup branch from 2abbcad to 5fdc17a Compare July 28, 2026 14:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant