Repository navigation
[Bug]: Plan setup and prerequisite checks accept a whitespace-only plan template or plan.md #5035
Description
Bug Description
1.2.2 added checks so that a missing or empty plan template no longer produces an empty plan.md, and so that check-prerequisites rejects an empty plan.md. Both checks test byte length, so a template or plan.md that contains only whitespace passes. setup-plan copies a whitespace-only template and exits 0, and check-prerequisites accepts the result. The same happens in the bash and Python scripts.
Steps to Reproduce
In a project on 1.2.2 with a feature directory containing spec.md (SPECIFY_FEATURE_DIRECTORY set to it):
Case 1, whitespace-only template:
printf '\n \n\t\n' > .specify/templates/plan-template.md
python3 .specify/scripts/python/setup_plan.py --json
python3 .specify/scripts/python/check_prerequisites.py --json
Case 2, existing whitespace-only plan.md with a valid template:
printf ' \n\n' > specs/099-repro-plan/plan.md
python3 .specify/scripts/python/setup_plan.py --json
python3 .specify/scripts/python/check_prerequisites.py --json
Control, existing zero-byte plan.md:
: > specs/099-repro-plan/plan.md
python3 .specify/scripts/python/check_prerequisites.py --json
The bash scripts (setup-plan.sh --json, check-prerequisites.sh --json) behave the same.
Expected Behavior
A template or plan.md with no non-whitespace content is treated like an empty one: setup-plan refuses to create the plan from a whitespace-only template, and check-prerequisites fails with "plan.md is empty".
Actual Behavior
Case 1: Copied plan template to .../plan.md, exit 0; plan.md is 7 bytes of whitespace (\n \n\t\n); check_prerequisites.py --json prints {"FEATURE_DIR":"...","AVAILABLE_DOCS":[]}, exit 0.
Case 2: Plan already exists at .../plan.md, skipping template copy, exit 0; check_prerequisites.py --json exit 0.
Control: ERROR: plan.md is empty in ..., exit 1.
Specify CLI Version
1.2.2
AI Agent
Claude Code (also reproduced with the scripts run directly; agent-independent)
Operating System
macOS 26 (Darwin 25.6.0), arm64
Python Version
Python 3.15.0
Error Logs
(none; the scripts succeed when they should fail)
Additional Context
Code: setup_plan.py checks template_content == "" (~line 73); check_prerequisites.py checks paths.impl_plan.stat().st_size == 0 (~line 239); the bash equivalents are setup-plan.sh ~49-53 and check-prerequisites.sh ~151. A .strip() / non-whitespace test would cover both cases.
Related, not reproduced: setup-plan writes the template directly to plan.md, so an interrupted write could leave a partial non-empty file that the check then accepts. Writing to a temporary file and renaming it would avoid that.
AI Disclosure
The issue was first flagged by Sourcery's automated PR review on a downstream project. Claude Code (Claude Opus 5.5, human-supervised) reproduced it with the commands above and drafted this issue; I reviewed it before filing.
Activity
github-actions commentedon Oct 20, 2026
Bug assessment — whitespace-plan-template: Valid · severity medium
Bug Assessment: Plan setup and prerequisite checks accept a whitespace-only plan template or plan.md
Report (summarized)
The reporter states that Specify CLI 1.2.2 added empty-template and empty-plan checks, but those checks use byte length. A plan template or existing
plan.mdcontaining only whitespace therefore passes. The report provides Python and Bash reproduction commands, expected failures, observed successful exits, and notes the same behavior in the Bash and Python scripts. No comments added further reproduction context.Environment reported: macOS 26, arm64, Python 3.15.0, Claude Code; the behavior is described as agent-independent.
Symptom
setup-plancopies a whitespace-onlyplan-template.mdintoplan.md, andcheck-prerequisitesaccepts a whitespace-only existingplan.md, both with success status. Expected behavior is to treat files with no non-whitespace content as empty and fail with the existing empty-plan/template diagnostics.Reproduction
SPECIFY_FEATURE_DIRECTORY, write a template containing only whitespace, such as newlines, spaces, and tabs.python3 .specify/scripts/python/setup_plan.py --json.python3 .specify/scripts/python/check_prerequisites.py --json.specs/.../plan.mdwhile a valid template exists, then run the same two commands.setup-plan.sh --jsonandcheck-prerequisites.sh --json) to observe the same byte-length behavior.The report's zero-byte control case is rejected as expected. [NEEDS CLARIFICATION: whether the same reproduction has been run through the installed PowerShell command on Windows; the current PowerShell source shows the same length-only checks.]
Suspected Code Paths
scripts/python/setup_plan.py:73— rejects only whentemplate_content == ""; whitespace remains a non-empty string and is written toplan.md.scripts/python/check_prerequisites.py:239— rejects only whenpaths.impl_plan.stat().st_size == 0; whitespace bytes make the file pass.scripts/bash/setup-plan.sh:49—[[ ! -s "$template_file" ]]checks only whether the resolved template has zero bytes.scripts/bash/check-prerequisites.sh:151—[[ ! -s "$IMPL_PLAN" ]]likewise checks only file size.scripts/powershell/setup-plan.ps1:51—$content.Length -eq 0does not classify whitespace-only content as empty.scripts/powershell/check-prerequisites.ps1:111— file.Length -eq 0does not classify whitespace-onlyplan.mdas empty.tests/test_setup_plan_python_parity.py:136— the existing parity test explicitly expects a whitespace-only template to succeed, documenting the current behavior rather than the intended behavior.Root Cause Hypothesis
The empty-content validation was implemented as a byte/character count check rather than a semantic content check. Because whitespace characters contribute to both string length and file size, all three script implementations accept files that contain no usable plan text. Confidence: high; the relevant checks directly support the reported outcomes.
Proposed Remediation
Preferred: Change the empty-content predicate in all three script variants to require at least one non-whitespace character. In Python, use a stripped-content check for the resolved template and read/check the plan content (or an equivalent whitespace-aware file check) instead of
st_size. In Bash, replace-swith a whitespace-aware predicate over the template and plan files. In PowerShell, use.Trim().Length -eq 0for the template and plan content. Preserve the existing error messages, exit codes, JSON behavior, and no-overwrite behavior. Keep the implementations behaviorally aligned across languages.Update parity and direct tests to cover whitespace-only templates and existing plans in JSON and text modes, asserting failure, empty stdout in JSON mode where applicable, no plan creation for an invalid template, and preservation of existing files. Retain zero-byte and valid-content coverage.
Alternatives (optional):
Files likely to change:
scripts/python/setup_plan.pyscripts/python/check_prerequisites.pyscripts/bash/setup-plan.shscripts/bash/check-prerequisites.shscripts/powershell/setup-plan.ps1scripts/powershell/check-prerequisites.ps1tests/test_setup_plan_python_parity.pytests/test_check_prerequisites_python_parity.pyor related setup-task tests.Tests to add or update:
setup-planto exit 1 without creatingplan.md.plan.mdmust causecheck-prerequisitesto exit 1 with the empty-plan diagnostic.tests/test_setup_plan_python_parity.py:136that treats a whitespace-only template as valid.Risks & Considerations
Open Questions