Repository navigation
[Bug]: /speckit-specify copies "the resolved spec-template file", which drops append-strategy preset layers #5177
Description
Bug Description
What happens. The specify command says:
- Resolve the active
spec-templatethrough the Spec Kit preset/template resolution stack (equivalent tospecify preset resolve spec-template) - Copy the resolved
spec-templatefile toSPECIFY_FEATURE_DIRECTORY/spec.mdas the starting point
When a preset provides spec-template with strategy: "append", no single file holds the resolved template. The core .specify/templates/spec-template.md doesn't have the preset section, and the preset's file has only that section. scripts/bash/resolve-template.sh composes the layers correctly, but the command tells the agent to copy a file. In practice the agent copies the core template, and the preset's section silently goes missing from new specs. /speckit-plan (setup-plan.sh → resolve_template_content) and /speckit-tasks (TASKS_TEMPLATE_CONTENT) already use composed content, so only specify is affected.
Steps to Reproduce
mkdir -p .specify/presets/example/templates
cat > .specify/presets/example/preset.yml <<'EOF'
schema_version: "1.0"
preset: {id: "example", name: "Example", version: "0.1.0", description: "append repro"}
provides:
templates:
- {type: "template", name: "spec-template", file: "templates/spec-addendum.md", description: "extra section", strategy: "append"}
EOF
printf '## Team Section\n\nTeam-specific fields.\n' > .specify/presets/example/templates/spec-addendum.md
# register "example" in .specify/presets/.registry (or install it with the preset CLI), then:
bash .specify/scripts/bash/resolve-template.sh spec-template --json | jq -r .TEMPLATE_CONTENT | grep -c '## Team Section' # 1
grep -c '## Team Section' .specify/templates/spec-template.md # 0
grep -c '## Requirements' .specify/presets/example/templates/spec-addendum.md # 0Running /speckit-specify then gives a spec.md with no ## Team Section.
Expected Behavior
the new spec.md starts from the composed template: the core sections plus every append/prepend layer.
Actual Behavior
"The new spec.md contains only the core template. The append preset section is missing, and nothing warns about it."
Specify CLI Version
1.2.2 (still present in 1.2.5 and on main)
AI Agent
Claude Code
Operating System
Linux (WSL2)
Python Version
Run python3 --version wherever specify is installed. I couldn't check this, because the specify CLI isn't installed in this environment.
Error Logs
Additional Context
have the command run resolve-template.sh spec-template --json (or the PowerShell equivalent) and write TEMPLATE_CONTENT to spec.md, the way /speckit-tasks uses TASKS_TEMPLATE_CONTENT. Or add a setup script for specify that writes the composed file, like setup-plan.sh.
AI Disclosure
Found during an AI-assisted code review. I drafted and reproduced it with Claude Code, then reviewed and verified it myself.
Activity
github-actions commentedon Oct 29, 2026
Bug assessment — specify-template-append-layers: Valid · severity medium
Bug Assessment:
/speckit-specifydrops append/prependspec-templatelayers when it copies a file pathReport (summarized)
Issue author @greggy20 reports that
/speckit-specifyinstructs the agent to copy a single resolvedspec-templatefile intospec.md, which omits preset-provided append layers. They provided a concrete reproduction using a preset manifest entry withstrategy: "append", whereresolve-template.sh spec-template --jsonincludes the appended section inTEMPLATE_CONTENT, but neither the core template file nor the preset addendum file alone contains the full composed template.No follow-up issue comments were present at assessment time.
Symptom
When a preset composes
spec-templateviaappend/prepend, running/speckit-specifycan producespec.mdfrom a single file layer (typically core), so preset-composed sections are silently missing. Expected behavior is forspec.mdto start from fully composed template content.Reproduction
spec-templatewithstrategy: appendand add a distinctive section (e.g.,## Team Section) in the addendum file.scripts/bash/resolve-template.sh spec-template --jsonandTEMPLATE_CONTENT./speckit-specifyto generate a new spec.spec.mddoes not include the appended section when the command follows the current “copy file” instruction.[NEEDS CLARIFICATION: exact agent/integration path used in the failing run (Copilot/Claude/Gemini command rendering), though the command template text itself is already sufficient to establish the defect.]
Suspected Code Paths
templates/commands/specify.md:96-97— instructs to resolvespec-templatethen “Copy the resolvedspec-templatefile” tospec.md; this is the direct behavior mismatch for composed templates.scripts/bash/common.sh:517(resolve_template) — resolves to a single file path layer; using this forspec-templateloses composed append/prepend/wrap semantics.scripts/bash/common.sh:626(resolve_template_content) — implements the actual composition stack (append/prepend/wrap/replace) and is the correct primitive for this flow.scripts/bash/resolve-template.sh:38-56— returns composed content asTEMPLATE_CONTENT; confirms the expected data shape for commands that need full template materialization.scripts/bash/create-new-feature.sh:441-456andscripts/python/create_new_feature.py:411— both already createspec.mdfrom composedresolve_template_content(...), showing prior-art behavior aligned with expected outcome.scripts/bash/setup-plan.sh:48andscripts/bash/setup-tasks.sh:54-55— these paths already consume composed template content, matching the issue’s claim that the inconsistency is specific to/speckit-specifycommand instructions.Root Cause Hypothesis
The
/speckit-specifycommand template mixes “resolve through preset stack” language with a file-copy instruction that is only safe for purereplacestacks. In composed-template scenarios, there is no single canonical source file containing final content, so the instruction drives agents toward path-based resolution and drops additive layers. Confidence: high.Proposed Remediation
Preferred: Update
templates/commands/specify.mdso spec generation uses composed content (TEMPLATE_CONTENT) from template resolution, not file-copy semantics. Concretely: instruct the command to run the existing template resolver (resolve-templatescript family /specify preset resolve ... --jsonequivalent), parseTEMPLATE_CONTENT, and write that content toSPECIFY_FEATURE_DIRECTORY/spec.md. Keep terminology consistent with other command templates that already require composed content.Also add a regression test that asserts the specify command template explicitly references composed content (and does not instruct copying a resolved file path), similar to existing assertions for constitution/checklist command templates in
tests/test_presets.py.Alternatives (optional):
/speckit-specifyto reusecreate-new-featurescript output as mandatory setup, then require the command to edit the createdSPEC_FILEonly. Trade-off: stronger determinism but tighter coupling between command prose and script contract.setup-specifyscript that emitsSPEC_FILE+ composed template content to unify shell/PowerShell/Python behavior. Trade-off: more moving parts than necessary if command-template correction is sufficient.Files likely to change:
templates/commands/specify.mdtests/test_presets.py(or another command-template assertion suite)scripts/bash/create-new-feature.sh,scripts/powershell/create-new-feature.ps1,scripts/python/create_new_feature.pywiring/invocation docs onlyTests to add or update:
TEMPLATE_CONTENT) and avoid “copy resolved file” wording.Risks & Considerations
templates/commands/specify.md, they may need equivalent updates to avoid divergent behavior.Open Questions
/speckit-specifymandate the existingcreate-new-featurescripts as the only spec bootstrap path, or only update prose to require composed template content regardless of implementation path?]fix(templates): start spec.md from the composed spec-template (#5187)