Bug Description
The Bash template resolver's wrap strategy can loop indefinitely when the resolved base content contains the literal {CORE_TEMPLATE} token.
The current loop replaces one token in layer_content with content, then scans the modified layer_content again. If content contains {CORE_TEMPLATE}, every iteration reintroduces a token and the resolver never terminates.
This is present on current main at commit 31e6b84f701b108a848847a8bc9ea86a1e2890b7 in scripts/bash/common.sh.
Steps to Reproduce
-
Initialize a project with Bash scripts.
-
Add this preset registry:
.specify/presets/.registry
{
"presets": {
"a-wrap": { "enabled": true, "priority": 1 },
"b-base": { "enabled": true, "priority": 2 }
}
}
-
Add a wrapping preset:
.specify/presets/a-wrap/preset.yml
provides:
templates:
- type: template
name: review-template
file: wrapper.md
strategy: wrap
.specify/presets/a-wrap/wrapper.md
BEFORE
{CORE_TEMPLATE}
AFTER
-
Add a base preset whose content intentionally contains the literal token:
.specify/presets/b-base/preset.yml
provides:
templates:
- type: template
name: review-template
file: base.md
strategy: replace
.specify/presets/b-base/base.md
-
Run:
timeout 5 .specify/scripts/bash/resolve-template.sh review-template
echo $?
Expected Behavior
The resolver replaces each {CORE_TEMPLATE} occurrence that existed in the original wrapper exactly once, preserves literal token text contained in the inserted base content, and terminates. For the example above, the output should contain:
BEFORE
BASE {CORE_TEMPLATE}
AFTER
Actual Behavior
The process consumes CPU until timeout terminates it with exit code 124. Each iteration replaces a token with base content that contains another token, so the loop condition remains true forever.
Specify CLI Version
1.0.3.dev0
AI Agent
Codex CLI
Operating System
Linux 7.2.0 x86_64
Python Version
Python 3.14.5 (Specify CLI runtime)
Error Logs
$ timeout 5 .specify/scripts/bash/resolve-template.sh review-template
$ echo $?
124
Additional Context
A single-pass replacement over an unmodified rest variable fixes the issue: consume tokens only from the original wrapper while appending content to a separate output buffer.
The issue was found while reviewing generated Spec Kit tooling in mforce/cluckwork#635. A verified local fix is in commit c70a5ad1.
I searched open and closed issues and PRs using {CORE_TEMPLATE}, wrap strategy, infinite loop, hang, and placeholder-reintroduction terms. Related issues #3991 and #4044 concern frontmatter corruption during wrap composition; they do not cover this non-terminating Bash replacement loop.
Bug Description
The Bash template resolver's
wrapstrategy can loop indefinitely when the resolved base content contains the literal{CORE_TEMPLATE}token.The current loop replaces one token in
layer_contentwithcontent, then scans the modifiedlayer_contentagain. Ifcontentcontains{CORE_TEMPLATE}, every iteration reintroduces a token and the resolver never terminates.This is present on current
mainat commit31e6b84f701b108a848847a8bc9ea86a1e2890b7inscripts/bash/common.sh.Steps to Reproduce
Initialize a project with Bash scripts.
Add this preset registry:
.specify/presets/.registry{ "presets": { "a-wrap": { "enabled": true, "priority": 1 }, "b-base": { "enabled": true, "priority": 2 } } }Add a wrapping preset:
.specify/presets/a-wrap/preset.yml.specify/presets/a-wrap/wrapper.mdAdd a base preset whose content intentionally contains the literal token:
.specify/presets/b-base/preset.yml.specify/presets/b-base/base.mdRun:
Expected Behavior
The resolver replaces each
{CORE_TEMPLATE}occurrence that existed in the original wrapper exactly once, preserves literal token text contained in the inserted base content, and terminates. For the example above, the output should contain:Actual Behavior
The process consumes CPU until
timeoutterminates it with exit code 124. Each iteration replaces a token with base content that contains another token, so the loop condition remains true forever.Specify CLI Version
1.0.3.dev0AI Agent
Codex CLI
Operating System
Linux 7.2.0 x86_64
Python Version
Python 3.14.5 (Specify CLI runtime)
Error Logs
Additional Context
A single-pass replacement over an unmodified
restvariable fixes the issue: consume tokens only from the original wrapper while appendingcontentto a separate output buffer.The issue was found while reviewing generated Spec Kit tooling in mforce/cluckwork#635. A verified local fix is in commit c70a5ad1.
I searched open and closed issues and PRs using
{CORE_TEMPLATE}, wrap strategy, infinite loop, hang, and placeholder-reintroduction terms. Related issues #3991 and #4044 concern frontmatter corruption during wrap composition; they do not cover this non-terminating Bash replacement loop.