Skip to content

[Bug]: GIT_BRANCH_NAME naming the current branch fails the mandatory before_specify hook #5003

Open

Description

@PascalThuet

Bug Description

speckit-git-feature documents GIT_BRANCH_NAME as "use this exact branch name". When that name is the branch already checked out, the script exits 1 with "Branch already exists". The before_specify hook that runs it is mandatory (optional: false), so /speckit-specify reports a hook failure on every run from an existing branch.

The script already handles this case behind --allow-existing-branch (it does nothing when the current branch is the target), but the command template never passes that flag.

Teams that create the branch before writing the spec (from a tracker, with names like feature/PROJ-42-login) hit this every time.

Steps to Reproduce

  1. Create a project with the git extension and check out a branch:
    git init -q p2
    cd p2
    uvx --from git+https://github.com/github/spec-kit.git@v1.2.1 specify init --here --force --integration claude --script sh --ignore-agent-tools
    uvx --from git+https://github.com/github/spec-kit.git@v1.2.1 specify extension add git
    git add -A
    git commit -qm init
    git checkout -q -b feature/PROJ-42-login
  2. Run the script the way .claude/skills/speckit-git-feature/SKILL.md invokes it, with GIT_BRANCH_NAME set to the current branch:
    GIT_BRANCH_NAME=feature/PROJ-42-login .specify/extensions/git/scripts/bash/create-new-feature-branch.sh --json --short-name login "Login page"

Expected Behavior

Exit 0 and stay on feature/PROJ-42-login. An explicit GIT_BRANCH_NAME that names the current branch is a request to use that branch, not to create a new one.

Actual Behavior

Exit 1 with "Branch already exists". The branch is unchanged, so the error is the only effect. The same call with --allow-existing-branch exits 0 and prints the expected JSON.

Either change would fix it:

  • in the script, treat GIT_BRANCH_NAME equal to the current branch as a no-op;
  • or have the command template pass --allow-existing-branch when GIT_BRANCH_NAME is set.

The first is smaller and also covers direct calls. The PowerShell and Python ports would need the same change.

Specify CLI Version

1.2.1 (git extension 1.0.1). Also present in 0.12.4 with git extension 1.0.0.

AI Agent

Claude Code

Operating System

macOS 26 (Darwin 25.6.0, arm64)

Python Version

Python 3.12.7

Error Logs

$ GIT_BRANCH_NAME=feature/PROJ-42-login .specify/extensions/git/scripts/bash/create-new-feature-branch.sh --json --short-name login "Login page"
Error: Branch 'feature/PROJ-42-login' already exists. Please use a different feature name or specify a different number with --number.
exit=1
branch=feature/PROJ-42-login

$ GIT_BRANCH_NAME=feature/PROJ-42-login .specify/extensions/git/scripts/bash/create-new-feature-branch.sh --json --allow-existing-branch --short-name login "Login page"
{"BRANCH_NAME":"feature/PROJ-42-login","FEATURE_NUM":"feature/PROJ-42-login"}
exit=0

Additional Context

#1117 proposed a SPECIFY_USE_CURRENT_BRANCH option and was closed without merge. This report does not ask for a new option. It only asks that the existing GIT_BRANCH_NAME override work when the branch already exists.

AI Disclosure

Investigated and drafted with Claude Code (Claude Opus 5.5, human-supervised). The reproduction was run as written and the log above is its unedited output. I reviewed the report before filing.

Activity

  1. github-actions commented on Oct 17, 2026

    @github-actions
    Contributor

    Bug assessment — current-branch-hook: Valid · severity medium


    Bug Assessment: GIT_BRANCH_NAME naming the current branch fails the mandatory before_specify hook

    Report (summarized)

    The reporter (PascalThuet, contributor) reports that with Specify CLI 1.2.1 and git extension 1.0.1, setting GIT_BRANCH_NAME to the branch already checked out causes the git feature script to exit 1 with Branch already exists. The script documents GIT_BRANCH_NAME as an exact branch-name override, and the before_specify hook is mandatory, so /speckit-specify fails for projects that pre-create their tracker-named branch. The report includes a reproduction using Bash on macOS and states the same behavior was present in 0.12.4/git extension 1.0.0.

    The existing --allow-existing-branch flag makes this case succeed, but the generated command invocation does not pass that flag. No additional comments were present.

    Symptom

    When GIT_BRANCH_NAME equals the currently checked-out branch, the branch script attempts to create the already-existing branch and reports an error instead of treating the explicit exact-name request as a no-op. The expected behavior is exit 0, remain on that branch, and emit the normal JSON result so the mandatory hook can continue.

    Reproduction

    1. Initialize a project with the git extension and commit its initial files, as described in the report.
    2. Check out a branch such as feature/PROJ-42-login.
    3. Invoke create-new-feature-branch.sh --json --short-name login "Login page" with GIT_BRANCH_NAME=feature/PROJ-42-login.
    4. Observe exit 1 and the Branch 'feature/PROJ-42-login' already exists error.
    5. Repeat with --allow-existing-branch; the report states this exits 0 and emits the expected JSON.

    The report does not provide an independent reproduction for PowerShell or Python, so cross-platform parity should be verified during remediation.

    Suspected Code Paths

    • extensions/git/scripts/bash/create-new-feature-branch.sh:495-500 — assigns GIT_BRANCH_NAME directly to BRANCH_NAME, establishing the exact-name override.
    • extensions/git/scripts/bash/create-new-feature-branch.sh:566-589 — attempts git checkout -b; when the target exists, it only accepts the current-branch no-op inside the ALLOW_EXISTING branch, otherwise emits the existing-branch error.
    • extensions/git/scripts/python/create_new_feature_branch.py:492-497 — mirrors the exact-name override.
    • extensions/git/scripts/python/create_new_feature_branch.py:564-605 — mirrors the Bash create/existing-branch decision and likewise requires args.allow_existing for the current-branch no-op.
    • extensions/git/scripts/powershell/create-new-feature-branch.ps1:445-454 and :514-543 — implement the same override and existing-branch handling, including the no-op only when -AllowExistingBranch is supplied.
    • extensions/git/commands/speckit.git.feature.md:17-22,58-63 — documents that GIT_BRANCH_NAME is the exact branch name but the shown Bash and PowerShell invocations do not include the allow-existing flag.
    • extensions/git/extension.yml:43-49 — registers speckit.git.feature as the before_specify hook with optional: false, making the failure block specification.
    • tests/extensions/git/test_git_extension_python_parity.py:422-444 — covers existing branches with and without the flag, but does not cover an override equal to the current branch.

    Root Cause Hypothesis

    Confidence: high. The scripts intentionally special-case an existing target branch only when --allow-existing-branch/args.allow_existing/-AllowExistingBranch is set. GIT_BRANCH_NAME changes branch-name generation but does not imply that the explicit target may already be the current branch. The command documentation forwards the override but not the flag, and the mandatory hook therefore turns this expected pre-created-branch workflow into a failure.

    Proposed Remediation

    Preferred: In all three branch-script implementations, when the exact GIT_BRANCH_NAME override is set and the resolved current branch equals the requested branch, treat the failed git checkout -b as a successful no-op. Preserve the current error for a different existing branch unless the explicit allow-existing option is supplied; that avoids silently switching branches and keeps the existing safety behavior. Keep the normal JSON output and persistence hint unchanged.

    Alternatives:

    • Update the generated command invocation to pass --allow-existing-branch (and the PowerShell equivalent) whenever GIT_BRANCH_NAME is set. This is smaller at the command layer but does not fix direct script callers and may allow switching to a different existing branch, which is broader than the reported requirement.
    • Make GIT_BRANCH_NAME imply allow-existing behavior for any existing target. This is simpler but risks silently switching away from the current branch and is not recommended.

    Files likely to change:

    • extensions/git/scripts/bash/create-new-feature-branch.sh
    • extensions/git/scripts/python/create_new_feature_branch.py
    • extensions/git/scripts/powershell/create-new-feature-branch.ps1
    • tests/extensions/git/test_git_extension.py
    • tests/extensions/git/test_git_extension_python_parity.py

    Tests to add or update:

    • For Bash and Python, set GIT_BRANCH_NAME to the repository's current branch and assert exit 0, unchanged HEAD, and matching JSON output.
    • Add the equivalent PowerShell test where PowerShell is available.
    • Preserve a negative test proving an override naming a different existing branch still fails without the explicit allow-existing option.
    • Add a cross-implementation parity case for the current-branch override.

    Risks & Considerations

    • The change affects branch-creation semantics only for an explicit override matching the current branch; ordinary generated-name collisions should retain their current errors.
    • All three ports must remain behaviorally aligned, including JSON and stderr output expected by hook execution.
    • The mandatory-hook path should be tested end-to-end so a successful no-op still allows /speckit-specify to continue.

    Open Questions

    • [NEEDS CLARIFICATION: Should an explicit GIT_BRANCH_NAME naming a different existing branch ever switch automatically, or should only the current-branch no-op be supported?]
    • [NEEDS CLARIFICATION: Is the Python or PowerShell implementation used by any currently supported generated integrations for this hook, and should their user-facing error text be kept byte-for-byte aligned with Bash?]

    Generated by 🐛 Assess Bug from Labeled Issue for #5003 · copilot · gpt52codex · 1.91 AIC · ⌖ 0.602 AIC · ⊞ 22.3K · ◷

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions