Skip to content

Bug: SPECS_DIR key in setup-plan.sh JSON output contains FEATURE_DIR value #4017

Closed

Description

@pierluigilenoci

Summary

The JSON output of scripts/bash/setup-plan.sh exposes a key named SPECS_DIR but populates it with the $FEATURE_DIR value (the per-feature subdirectory, e.g. specs/001-my-feature) rather than the actual specs root directory (specs/).

Affected file

scripts/bash/setup-plan.sh

Bug locations

Line ~71 (jq path):

# Current (wrong):
--arg specs_dir "$FEATURE_DIR"

# Should be:
--arg feature_dir "$FEATURE_DIR"

Line ~73 (jq JSON key):

# Current (wrong):
'{FEATURE_SPEC:$feature_spec,IMPL_PLAN:$impl_plan,SPECS_DIR:$specs_dir,BRANCH:$branch}'

# Should be:
'{FEATURE_SPEC:$feature_spec,IMPL_PLAN:$impl_plan,FEATURE_DIR:$feature_dir,BRANCH:$branch}'

Line ~75 (printf fallback):

# Current (wrong):
printf '{"FEATURE_SPEC":"%s","IMPL_PLAN":"%s","SPECS_DIR":"%s","BRANCH":"%s"}\n'

# Should be:
printf '{"FEATURE_SPEC":"%s","IMPL_PLAN":"%s","FEATURE_DIR":"%s","BRANCH":"%s"}\n'

Line ~81 (text-mode output):

# Current (wrong):
echo "SPECS_DIR: $FEATURE_DIR"

# Should be:
echo "FEATURE_DIR: $FEATURE_DIR"

Impact

Consumers of the JSON output who treat SPECS_DIR as the specs root directory will receive the wrong path instead of the feature subdirectory.

Fix

Rename the key from SPECS_DIR to FEATURE_DIR in all 4 locations listed above.

Activity

  1. Yash-Chindam commented on Sep 1, 2026

    @Yash-Chindam
    Contributor

    Confirmed on main. Adding some scope that the report misses — the same defect exists in the PowerShell and Python ports, and there is a consumer that has to move with it.

    All three script implementations emit the mislabelled key:

    scripts/bash/setup-plan.sh:75        '{...,SPECS_DIR:$specs_dir,BRANCH:$branch}'
    scripts/bash/setup-plan.sh:77        printf '{...,"SPECS_DIR":"%s",...}'
    scripts/bash/setup-plan.sh:83        echo "SPECS_DIR: $FEATURE_DIR"
    scripts/powershell/setup-plan.ps1:79 SPECS_DIR = $paths.FEATURE_DIR
    scripts/powershell/setup-plan.ps1:86 Write-Output "SPECS_DIR: $($paths.FEATURE_DIR)"
    scripts/python/setup_plan.py:85      "SPECS_DIR": str(paths.feature_dir),
    scripts/python/setup_plan.py:93      print(f"SPECS_DIR: {paths.feature_dir}")
    

    Fixing only the four bash locations listed in the description would leave the three ports disagreeing with each other, which is worse than the current state.

    setup-plan is the only script in the suite that uses this name. Every sibling emits FEATURE_DIR for exactly the same value:

    scripts/bash/setup-tasks.sh:75           FEATURE_DIR:$feature_dir
    scripts/bash/check-prerequisites.sh:122  FEATURE_DIR:$feature_dir
    scripts/bash/check-prerequisites.sh:208  FEATURE_DIR:$feature_dir
    scripts/bash/setup-plan.sh:75            SPECS_DIR:$specs_dir      <-- odd one out
    

    The name is also already taken, with a different meaning. In create-new-feature.sh:

    SPECS_DIR="$REPO_ROOT/specs"           # the specs root
    FEATURE_DIR="$SPECS_DIR/$BRANCH_NAME"  # one feature inside it

    So SPECS_DIR means specs/ in one script and specs/001-my-feature/ in another.

    There is one consumer, templates/commands/plan.md:62:

    Run {SCRIPT} from repo root and parse JSON for FEATURE_SPEC, IMPL_PLAN, SPECS_DIR, BRANCH.

    It has to be updated in the same change or /speckit.plan starts looking for a key that no longer exists. That makes the full surface 7 script locations across 3 languages, plus the template.

    One decision worth settling before anyone writes code: the JSON output is a public-ish contract, so a straight rename is technically breaking for anyone parsing it outside this repo. Two options:

    1. Rename outright — plan.md is the only in-repo consumer and ships alongside the scripts, so they stay in sync automatically. Clean, no lingering wart.
    2. Emit FEATURE_DIR and keep SPECS_DIR as a deprecated alias for a release or two.

    I lean towards (1), since the value was never the specs root and anything relying on it was already reading the wrong path. Happy to go with (2) if you would rather not break external parsers.

    I am glad to put up a PR covering all three ports plus the template — just let me know which option you prefer.

    Disclosure: I used an AI assistant (Claude) to help trace the call sites and draft this comment. The findings above were verified against main.

  2. mnriem commented on Sep 1, 2026

    @mnriem
    Collaborator

    @Yash-Chindam Feel free to create a PR with a fix as I consider it a bug

  3. Yash-Chindam commented on Sep 1, 2026

    @Yash-Chindam
    Contributor

    PR up: #4397

    Covers the full surface rather than just the four bash lines — the same key is emitted by all three ports, plus templates/commands/plan.md, which parses it by name and would break if the scripts moved without it:

    scripts/bash/setup-plan.sh         73, 75, 77, 83
    scripts/powershell/setup-plan.ps1  79, 86
    scripts/python/setup_plan.py       85, 93
    templates/commands/plan.md         62
    

    Verified all three variants now emit ['BRANCH', 'FEATURE_DIR', 'FEATURE_SPEC', 'IMPL_PLAN']. create-new-feature.sh is untouched, since SPECS_DIR correctly means the specs root there.

    I went with the straight rename rather than a deprecated alias, for the reasons in the PR — happy to switch if you would rather not break external parsers.

    Could you assign this issue to me so ownership is clear? Happy to revise on feedback.

    Disclosure: I used an AI assistant (Claude) to help investigate this, implement the change, and draft this comment. The verification described above was produced by running the code, and I reviewed the change before submitting.

  4. added a commit that references this issue on Sep 1, 2026
    d6cb6ee
  5. added a commit that references this issue on Sep 3, 2026
    6552be3
  6. added a commit that references this issue on Sep 3, 2026
    af8f5a4
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions