Repository navigation
Conversation
A command step's `args` that is a single `{{ }}` block keeps the
expression's typed value, so a reference with nothing behind it resolves
to None: an optional input that was not supplied, or an output key the
earlier step does not produce. The documented example
`args: "{{ steps.specify.output.file }}"` is one, since command steps
have no `file` output. `execute()` passed `str(None)` to the
integration, so the agent received `/speckit-plan None`. The same value
inside a longer template renders as empty text, and a missing `args` key
already means no arguments.
Treat a null `args` as no arguments. Values that are only falsy (0,
false, the empty string) are dispatched as before.
Assisted-by: Claude Code (model: Claude Opus 5.5, autonomous)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
When a command step's
argsis a single{{ }}expression that resolves to null, the agent is invoked with the literal textNoneas its arguments:/speckit-plan None. Two common ways to get there:The documented example
args: "{{ steps.specify.output.file }}"(workflows/README.md) is one, since command steps have nofileoutput.Reproduction with a stand-in
claudeonPATHthat logs its argv:Root cause:
CommandStep.execute()doesargs_str = str(resolved_input.get("args", "")). A lone{{ }}block keeps the expression's typed value (expressions.py), so a reference with nothing behind it isNone, andstr(None)is"None". The same value inside a longer template is rendered as empty text, and a missingargskey already means no arguments.The fix treats a null
argsas no arguments (6 lines). Values that are only falsy (0,false,"") and mixed templates are dispatched as before.Prompt and shell steps have the same
str(None)pattern, but they are deliberately left out. Forshell, an empty command would turn a visible failure into a silent success. Forprompt, sending nothing versus failing is a separate design question.Testing
Tested locally with
uv run specify --helpRan existing tests with
uv sync && uv run pytestTested with a sample project (if applicable): the stand-in
claudereproduction above, onmainand on this branchNew parametrized test
TestCommandStep.test_dispatch_args_resolving_to_null_send_no_argumentsintests/test_workflows.py. It covers 2 null cases ({{ inputs.feature }}unset,{{ steps.specify.output.file }}) and 4 controls (0,False,"", a mixed template).The null cases fail on
mainwithAssertionError: assert '/speckit-plan None' == '/speckit-plan'and the controls pass. With the change all 35TestCommandSteptests pass.Full suite (macOS, Python 3.13): 12336 passed, 376 skipped. 11 failures in the
tests/test_*_python_parity.pyfiles occur identically on unmodifiedmainon this machine under full-suite load; those files pass when run on their own (257 passed, 112 skipped).uvx ruff@0.15.0 check src tests: cleanAI Disclosure
AI disclosure: Claude Code (model: Claude Opus 5.5, max reasoning effort, autonomous agent mode) opened this PR on behalf of @yxshee. A Claude Code subagent (same model, autonomous) found the bug and wrote the fix and tests. The lead agent independently re-ran the new tests against
mainand the branch, reproduced the argv above with the real CLI, reviewed the diff, and wrote this description. No human reviewed the change line by line before it was opened.🤖 Generated with Claude Code