Skip to content

[Bug]: wrap template composition can loop forever when core content contains {CORE_TEMPLATE} #4385

Description

@mforce

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

  1. Initialize a project with Bash scripts.

  2. Add this preset registry:

    .specify/presets/.registry

    {
      "presets": {
        "a-wrap": { "enabled": true, "priority": 1 },
        "b-base": { "enabled": true, "priority": 2 }
      }
    }
  3. 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
    
  4. 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

    BASE {CORE_TEMPLATE}
    
  5. 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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions