Skip to content

fix: auto-correct prefix conflicts in create-new-feature scripts#1829

Open
daniel-graham wants to merge 1 commit intogithub:mainfrom
daniel-graham:fix/auto-correct-prefix-conflicts
Open

fix: auto-correct prefix conflicts in create-new-feature scripts#1829
daniel-graham wants to merge 1 commit intogithub:mainfrom
daniel-graham:fix/auto-correct-prefix-conflicts

Conversation

@daniel-graham
Copy link

Problem

When --number is passed to create-new-feature.sh with a prefix that already exists in specs/ (e.g., an AI agent passes --number 1 but specs/001-* already exists), the script silently creates a duplicate directory. This causes downstream errors in check-prerequisites.sh:

ERROR: Multiple spec directories found with prefix '001': 001-dataset-manager 001-operator-cockpit 001-trading-engine-scaffold

Root cause

AI agents sometimes compute an incorrect feature number (e.g., matching only branches with the same short-name instead of globally), bypassing the script's own global-max logic via --number.

Fix

After FEATURE_NUM is determined, the script now checks if a specs/NNN-* directory already exists. If so, it recalculates from the true global maximum across all spec directories and git branches, then warns on stderr:

⚠️  Requested number 001 conflicts with existing spec directory. Auto-corrected to 030.

The --number flag becomes a validated suggestion rather than a hard override. The workflow never fails due to numbering — it self-heals.

Changes

  • scripts/bash/create-new-feature.sh: Added a prefix-conflict guardrail after the feature number is computed (24 lines added, 0 removed).

Testing

Tested with an existing specs/001-* directory:

$ .specify/scripts/bash/create-new-feature.sh --json --number 1 --short-name test-guardrail 'Testing guardrail'
⚠️  Requested number 001 conflicts with existing spec directory. Auto-corrected to 030.
{"BRANCH_NAME":"030-test-guardrail","SPEC_FILE":".../specs/030-test-guardrail/spec.md","FEATURE_NUM":"030"}

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates the feature creation Bash script to prevent duplicate spec-directory prefixes when --number is provided and conflicts with an existing specs/NNN-* directory, by auto-correcting to the next available global number.

Changes:

  • Added a guardrail that detects existing specs/${FEATURE_NUM}-* directories and recomputes the feature number from the global maximum across specs and git branches.
  • Emits a warning to stderr when auto-correction occurs.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Collaborator

@mnriem mnriem left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for tackling this — the problem is real and the guardrail idea is the right direction. Before we can merge, there are a few things we need to address: the --number flag's contract changes from a hard override to a suggestion, which is a silent breaking change for any automation relying on deterministic output; the recalculation path skips git fetch so the corrected number could still collide with unfetched remote branches; the check only covers spec directories but not existing branches, leaving an inconsistent experience; the help text still says "overrides auto-detection"; and the PowerShell script doesn't get the same guardrail, breaking cross-platform parity. Could you scope the guardrail to only fire when --number was explicitly passed, add a fetch before recalculating, extend the collision check to branches, update the help text, and port it to the PowerShell side as well?

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a guardrail to feature creation scripts so an explicitly requested feature number (--number / -Number) is treated as a validated suggestion and auto-corrected when its NNN- prefix already exists, preventing duplicate spec directories/branch prefixes that break downstream workflows.

Changes:

  • Add “explicit number” tracking and a conflict check that recalculates the next available number when a requested prefix already exists.
  • Update help text to clarify that --number / -Number is preferred and may be auto-corrected.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
scripts/bash/create-new-feature.sh Adds an explicit---number guardrail that detects existing NNN- prefixes and recalculates from global max.
scripts/powershell/create-new-feature.ps1 Mirrors the guardrail behavior in PowerShell and updates CLI help text accordingly.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@daniel-graham daniel-graham changed the title fix: auto-correct prefix conflicts in create-new-feature.sh fix: auto-correct prefix conflicts in create-new-feature scripts Mar 13, 2026
@daniel-graham
Copy link
Author

Changes made in response to review

Thanks for the thorough review @mnriem! All feedback addressed across 3 follow-up commits:

@mnriem review (requested changes)

Feedback Resolution
--number contract changes silently from override to suggestion Guardrail now only fires when --number was explicitly passed (NUMBER_EXPLICIT flag in Bash, $PSBoundParameters.ContainsKey in PowerShell)
Recalculation skips git fetch Guardrail now calls git fetch --all --prune before checking branches, and delegates recalculation to check_existing_branches / Get-NextBranchNumber which also fetch
Collision check only covers spec dirs, not branches Trigger now checks both specs/NNN-* dirs and git branch -a for matching prefixes
Help text still says "overrides auto-detection" Updated to "Preferred branch number (auto-corrected if prefix already exists in specs or branches)"
PowerShell missing parity Full guardrail ported to create-new-feature.ps1 with identical logic

@copilot review (suggestions)

Feedback Resolution
PR title only mentions .sh Updated to reference both scripts
Use $PSBoundParameters.ContainsKey instead of sentinel Adopted — idiomatic PowerShell explicit-parameter detection
git branch -a without fetch (PS) Added git fetch --all --prune before branch check
git branch -a without fetch (Bash) Same fix applied

Also fixed

  • Removed unrelated templates/commands/specify.md changes that had leaked into the PR

All changes validated on macOS with both Bash and PowerShell (pwsh):

  • Explicit --number with conflict → warns + auto-corrects
  • No --number (auto-detect) → guardrail does not fire

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds guardrails to the “create new feature” scripts so an explicitly suggested feature number won’t silently collide with existing specs/NNN-* directories or existing branch prefixes, preventing downstream prefix-ambiguity errors.

Changes:

  • Add prefix-conflict detection and auto-correction when --number / -Number is explicitly provided.
  • Update CLI help text to describe --number / -Number as a preferred (validated) number rather than a hard override.
  • Fix/adjust the specify.md command template validation step reference (proceed to step 7).

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
templates/commands/specify.md Updates the command template’s instructions/step reference and removes an unused header.
scripts/powershell/create-new-feature.ps1 Adds -Number explicit-tracking and a guardrail to auto-correct number conflicts; updates help text.
scripts/bash/create-new-feature.sh Adds --number explicit-tracking and a guardrail to auto-correct number conflicts; updates help text.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +267 to +271
# Check for conflict in spec directories
SPEC_CONFLICT=false
if compgen -G "$SPECS_DIR/${FEATURE_NUM}-*" > /dev/null 2>&1; then
SPEC_CONFLICT=true
fi
Comment on lines 131 to 133

- **If all items pass**: Mark checklist complete and proceed to step 6
- **If all items pass**: Mark checklist complete and proceed to step 7

@mnriem mnriem self-requested a review March 13, 2026 16:23
Copy link
Collaborator

@mnriem mnriem left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please pull in latest from main and address Copilot feedback. If not applicable, please explain why.

@daniel-graham daniel-graham force-pushed the fix/auto-correct-prefix-conflicts branch from ebbfc54 to f6dbbe2 Compare March 13, 2026 16:30
@daniel-graham
Copy link
Author

Follow-up: rebased on main + addressed remaining feedback

What changed

  1. Rebased on latest main — picks up PR Add specify doctor command for project health diagnostics #1828 ("Add specify doctor command")
  2. Fixed compgen to only match directories — previously compgen -G "specs/NNN-*" would also match files (e.g., specs/001-notes.md), causing false positives. Now iterates matches and only triggers on -d (directories)
  3. Confirmed templates/commands/specify.md is clean — no diff vs upstream/main. The bot's earlier comment was from a stale review before the revert was pushed

Final PR diff

Only 2 files changed (82 additions, 2 deletions):

  • scripts/bash/create-new-feature.sh
  • scripts/powershell/create-new-feature.ps1

No other files are modified.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates the “create new feature” scripts to prevent duplicate numeric prefixes (e.g., 001-*) when a caller explicitly suggests a feature number, aligning behavior with downstream tooling that expects unique 3-digit prefixes.

Changes:

  • Treat --number / -Number as a validated “preferred” value and auto-correct it when the prefix already exists in specs/ or git branches.
  • Track whether the number was explicitly provided to preserve existing auto-detection behavior.
  • Update help text to reflect the new auto-correction semantics.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
scripts/bash/create-new-feature.sh Adds an explicit-number guardrail to detect prefix conflicts in specs/ and branches and auto-correct to the next available number.
scripts/powershell/create-new-feature.ps1 Mirrors the guardrail behavior in PowerShell and updates CLI help text accordingly.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +289 to +303
if [ "$HAS_GIT" = true ]; then
# Fetch remotes so we don't miss branches that exist only on remotes
git fetch --all --prune >/dev/null 2>&1 || true
if git branch -a 2>/dev/null | grep -qE "(^|[[:space:]])(remotes/[^/]+/)?${FEATURE_NUM}-"; then
BRANCH_CONFLICT=true
fi
fi

if [ "$SPEC_CONFLICT" = true ] || [ "$BRANCH_CONFLICT" = true ]; then
REQUESTED_NUM="$FEATURE_NUM"
# Delegate to check_existing_branches, which fetches and computes
# max(all specs, all branches) + 1 — same logic used by auto-detection.
if [ "$HAS_GIT" = true ]; then
BRANCH_NUMBER=$(check_existing_branches "$SPECS_DIR")
else
Comment on lines +216 to +218
# Track whether the caller explicitly passed -Number so the guardrail below
# only fires for explicit overrides, not for auto-detected numbers.
$numberExplicit = $PSBoundParameters.ContainsKey('Number')
Comment on lines +245 to +259
if ($hasGit) {
# Fetch remotes so we don't miss branches that exist only on remotes
try { git fetch --all --prune 2>$null | Out-Null } catch { }
$allBranches = git branch -a 2>$null
if ($LASTEXITCODE -eq 0) {
$branchConflict = ($allBranches | Where-Object { $_ -match "(^|\s)(remotes/[^/]+/)?$featureNum-" }).Count -gt 0
}
}

if ($specConflict -or $branchConflict) {
# Delegate to Get-NextBranchNumber, which fetches and computes
# max(all specs, all branches) + 1 — same logic used by auto-detection.
if ($hasGit) {
$Number = Get-NextBranchNumber -SpecsDir $specsDir
} else {
@@ -39,14 +40,15 @@ while [ $i -le $# ]; do
exit 1
fi
@mnriem mnriem self-requested a review March 13, 2026 16:48
Copy link
Collaborator

@mnriem mnriem left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please address the additional Copilot comments. Thank for staying with it!

When --number (Bash) or -Number (PowerShell) is explicitly passed and
the requested prefix already exists as a spec directory or git branch,
auto-correct to the global max + 1 rather than failing or creating
duplicate numbering.

Changes:
- Guard fires only on explicit --number/-Number (Bash: NUMBER_EXPLICIT
  flag; PowerShell: $PSBoundParameters.ContainsKey('Number') -and $Number
  -ne 0), leaving the auto-detection contract unchanged
- Bash: validates --number is an integer in [1,999]; values outside this
  range are rejected with a clear error message since downstream tooling
  expects a 3-digit prefix
- Conflict detection checks both specs/NNN-* directories (directories
  only) and all local/remote git branches
- Single fetch: Bash fetches once before conflict detection and inlines
  max-finding from local branch data to avoid a second network call;
  PowerShell defers the fetch to Get-NextBranchNumber, which runs only
  when a conflict is actually found
- Emits a warning to stderr when auto-correction occurs
- Help text updated: --number/-Number is now "Preferred branch number
  (auto-corrected if prefix already exists in specs or branches)"
- Full parity between Bash and PowerShell implementations
@daniel-graham daniel-graham force-pushed the fix/auto-correct-prefix-conflicts branch from f6dbbe2 to 4fae020 Compare March 13, 2026 16:54
@daniel-graham
Copy link
Author

Latest update (rebased + squashed to 1 commit)

Addressed the remaining Copilot feedback and cleaned up from internal review:

  1. Bash: --number range validation — now rejects values outside [1,999] with a clear error, since downstream tooling expects a 3-digit prefix
  2. PowerShell: [ValidateRange(1, 999)] — added to the -Number parameter for parity with Bash
  3. Double-fetch eliminated — Bash inlines the max-finding using already-fetched local branch data; PowerShell defers the single fetch to Get-NextBranchNumber (only called when a conflict is found)
  4. Stale comments cleaned up — removed references to check_existing_branches in the inlined code path

Note for @mnriem on range validation

We added [ValidateRange(1, 999)] to the PowerShell -Number parameter (and equivalent validation in Bash) to prevent malformed prefixes from values outside the 3-digit range. This means -Number 0 now errors instead of silently triggering auto-detection — users should simply omit -Number for auto-detect behavior.

If there's an intentional reason to allow values outside [1, 999] (e.g., future support for 4-digit prefixes), let us know and we'll adjust the bounds accordingly.

Final diff stat

 scripts/bash/create-new-feature.sh    | 62 ++++++++++-
 scripts/powershell/create-new-feature.ps1 | 44 +++++++-
 2 files changed, 104 insertions(+), 2 deletions(-)

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Improves the “create new feature” scripts (bash + PowerShell) so an explicitly provided feature number is treated as a validated suggestion and auto-corrected when the numeric prefix already exists in specs/ or in git branches, preventing duplicate NNN-* spec directories.

Changes:

  • Add explicit-number tracking and a conflict guardrail that recomputes the next available global feature number on collision.
  • Update CLI help text to clarify --number/-Number is auto-corrected on conflicts.
  • Add input validation for --number / -Number (but see blocking issues in review comments).

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
scripts/powershell/create-new-feature.ps1 Adds -Number conflict guardrail + help text update; introduces a parameter validation issue with the default sentinel.
scripts/bash/create-new-feature.sh Adds --number validation and a conflict guardrail to auto-correct prefix collisions after number resolution.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

$Number = Get-NextBranchNumber -SpecsDir $specsDir
} else {
$Number = (Get-HighestNumberFromSpecs -SpecsDir $specsDir) + 1
}
Comment on lines +7 to 9
[ValidateRange(1, 999)]
[int]$Number = 0,
[switch]$Help,
>&2 echo 'Error: --number must be an integer between 1 and 999'
exit 1
fi
if (( next_arg < 1 || next_arg > 999 )); then
>&2 echo "⚠️ [specify] --number $REQUESTED_NUM conflicts with an existing spec dir or branch. Auto-corrected to $FEATURE_NUM."
fi
fi

@mnriem mnriem self-requested a review March 13, 2026 21:56
Copy link
Collaborator

@mnriem mnriem left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer the range to allow for 5 digits so it takes a longer time to run out. Now that we are using AI we probably will go through number more quickly so 3 digits seems a bit small. And please address Copilot feedback. if not applicable please explain why.

@daniel-graham
Copy link
Author

I would prefer the range to allow for 5 digits so it takes a longer time to run out. Now that we are using AI we probably will go through number more quickly so 3 digits seems a bit small. And please address Copilot feedback. if not applicable please explain why.

Agreed, I imagine we may want to either add some padding 00NNN to existing directories, or leave that up to the user, and have the script simply progress naturally to 5 digits from 999 -> 01000?

@mnriem
Copy link
Collaborator

mnriem commented Mar 13, 2026

Probably should keep the prepending for the current scheme and once it overflows no more prepending as that will keep it backwards compatible

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants