fix(workflows): reject a non-string 'integration'/'model' in command & prompt steps#3597
fix(workflows): reject a non-string 'integration'/'model' in command & prompt steps#3597Noor-ul-ain001 wants to merge 3 commits into
Conversation
…& prompt steps
A non-string `integration` on a command or prompt step is passed to
`get_integration()`, which uses it as a dict key: an unhashable list/dict
raises a raw `TypeError` there — and because neither `validate()` nor
`validate_workflow` checked the type, this crashes even a *validated* run,
not just an unvalidated one. A non-string `model` likewise reaches
`build_exec_args()` and is fed into the CLI argv.
Guard both fields in `validate()` (reject a literal non-string, mirroring the
existing 'command'/'prompt'/'input'/'options' checks) and in `execute()`
(fail the step cleanly rather than take down the whole run, mirroring the
'input'/'options' guards). An explicit YAML-null (inherit the workflow
default) and a "{{ ... }}" expression both stay valid.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds validation and runtime guards for non-string command/prompt integration and model values.
Changes:
- Rejects invalid literal values during validation.
- Returns failed step results for invalid resolved values.
- Adds validation and execution tests.
Show a summary per file
| File | Description |
|---|---|
src/specify_cli/workflows/steps/command/__init__.py |
Validates command dispatch fields. |
src/specify_cli/workflows/steps/prompt/__init__.py |
Validates prompt dispatch fields. |
tests/test_workflows.py |
Tests new validation and failure behavior. |
Review details
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Files reviewed: 3/3 changed files
- Comments generated: 2
- Review effort level: Medium
| # A non-string integration/model — a literal list/dict/number that | ||
| # skipped validation, an unvalidated workflow-level default, or an |
| # A non-string integration/model — a literal list/dict/number that | ||
| # skipped validation, an unvalidated workflow-level default, or an |
|
Please address Copilot feedback and resolve conflicts |
… guard
Address Copilot review: `config.get("integration") or context.default_integration`
(and the model equivalent) coerced a *falsey* non-string ([], {}, 0, False) into
the workflow default before the type guard ran. On an unvalidated execute() such a
step was silently accepted and — with a configured default — could dispatch using
the wrong integration/model instead of failing with the contract error.
Fall back to the workflow default only for genuinely-unset values (missing /
YAML-null / empty string) so every non-string reaches the guard. Add parametrized
falsey execute() cases ([], {}, 0, False) to both TestCommandStep and
TestPromptStep; with the fix stashed all 8 fail (swallowed into the default).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…integration-crash # Conflicts: # tests/test_workflows.py
|
@ Copilot feedback (falsey non-string bypass). Both flagged spots resolved the value with Fixed in both integration = config.get("integration")
if integration is None or integration == "":
integration = context.default_integrationso every non-string now reaches the guard. Tests. Added parametrized Conflicts. Merged Full |
Problem
A command or prompt step whose
integration:(ormodel:) is a non-string crashes the run with a rawTypeErrorinstead of a clean validation/step error.In
execute(), the resolvedintegrationvalue is passed toget_integration(), which doesINTEGRATION_REGISTRY.get(key)— using the value as a dict key. An unhashable value (list/dict) raisesTypeError: unhashable typeright there:Crucially this is not limited to unvalidated runs: neither the step's
validate()norvalidate_workflowchecked the type ofintegration/model, sovalidate()returns[]forintegration: [claude]and the crash survives validation. A non-stringmodelsimilarly reachesbuild_exec_args()and would be fed into the CLI argv.This is the same recurring shape as the existing
command/prompt/input/optionsguards — a string-typed dispatch field thatexecute()passes downstream without a type check.Fix
Both
commandandpromptsteps:validate()— reject a literal non-stringintegration/model, mirroring the existing sibling type checks. An explicit YAML-null (integration:→ inherit the workflow default) and a"{{ ... }}"expression both stay valid.execute()— guard the resolved value (after theor-fallback to the workflow default and expression evaluation) and return a cleanFAILEDStepResultrather than letting the run crash. This also catches an expression that resolves to a non-string and an unvalidated workflow-level default.Tests
Added parametrized
validate()rejection tests, "accepts None / expression" tests, andexecute()-fails-loudly tests to bothTestCommandStepandTestPromptStep. Test-the-test: with the fix stashed, the new tests fail — including the rawTypeError: unhashable typetraceback atget_integration. Fulltests/test_workflows.pyshows no new failures (the 20 pre-existing failures are all Windows symlink-guard tests, identical on a clean tree).🤖 Generated with Claude Code