Skip to content

fix(workflows): strip the resolved value before matching switch cases - #4143

Open
jawwad-ali wants to merge 1 commit into
github:mainfrom
jawwad-ali:fix/switch-strip-resolved-value
Open

fix(workflows): strip the resolved value before matching switch cases#4143
jawwad-ali wants to merge 1 commit into
github:mainfrom
jawwad-ali:fix/switch-strip-resolved-value

Conversation

@jawwad-ali

Copy link
Copy Markdown
Contributor

Problem

SwitchStep.execute matches with:

str_value = str(value) if value is not None else ""

No strip. But the values a switch dispatches on are overwhelmingly captured command output, and ShellStep stores proc.stdout verbatim (shell/__init__.py:67). So run: echo approve resolves to "approve\n", which matches no approve: case key.

Reproduction on current main (bf88c9f)

stdout stored : 'approve\n'
matched_case  : '__default__'      <-- silently wrong
next steps    : ['fallback']

The switch silently falls through to default: — or, with no default, completes having dispatched nothing — while still reporting COMPLETED.

A workflow author cannot work around it: the registered filters are default / join / map / contains / from_json. There is no trim.

This is already treated as a bug everywhere else

spec-kit strips before matching a resolved string against declared literals in every comparable site:

Site Code
evaluate_condition result.strip().lower() — fixed for this exact shell-newline reason
InitStep._resolve_bool resolved.strip().lower()
workflows/catalog.py raw_install.strip().lower() in (...)

Switch case keys are such declared literals, and this was the only site not stripping.

Fix

One line, plus the class docstring which said "exact match, string-coerced" and is now accurate.

Narrow. expression_value still reports the raw value, so nothing downstream loses information — pinned by an assertion. A genuine mismatch still falls through: a second test checks "approve-later\n" still routes to default.

The only inputs whose behaviour changes are ones that previously took the wrong branch.

Verification

  • Fail-before / pass-after: 2 new-vs-baseline failures with the source reverted; all pass with the fix. Parametrized over "approve\n", "approve\r\n", " approve " and "approve".
  • Tests go into the existing TestSwitchStep class — several open PRs touch this file, and a duplicate class name silently shadows.
  • Scoped regression: no new failures vs a clean-main baseline captured on bf88c9f9 (21 pre-existing in scope).
  • uvx ruff@0.15.0 check src tests → clean

One note on the run: an unrelated test_resume_rejects_malformed_run_state_origin_fields case failed once during verification. I checked rather than assumed — it fails on a different parametrization each time and passes 4/4 in isolation both with and without this change, so it is a pre-existing Windows flake (os.replace PermissionError). A clean re-run of the full gate passed.


Written with assistance from Claude Code. Bug found, reproduced, and verified by me on current main.

`SwitchStep.execute` matched with `str(value)` and no strip. The values a
switch dispatches on are overwhelmingly captured command output, and
`ShellStep` stores `proc.stdout` verbatim, so `run: echo approve` resolves
to "approve\n" — which matches no `approve:` case:

  stdout stored : 'approve\n'
  matched_case  : '__default__'      <-- silently wrong
  next steps    : ['fallback']

The switch falls through to `default:` (or dispatches nothing at all) while
still reporting COMPLETED. A workflow author cannot fix it themselves: the
registered filters are default/join/map/contains/from_json — there is no
`trim`.

spec-kit already treats exactly this as a bug wherever else it matches a
resolved string against declared literals — `evaluate_condition` strips for
this same shell-newline reason, and `InitStep._resolve_bool` does
`resolved.strip().lower()`. Switch case keys are such literals, and this was
the only site not stripping.

`expression_value` still reports the raw value, so nothing downstream loses
information, and a genuine mismatch ("approve-later") still falls through.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@jawwad-ali
jawwad-ali requested a review from mnriem as a code owner August 15, 2026 15:11
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