diff --git a/extensions/git/scripts/powershell/create-new-feature-branch.ps1 b/extensions/git/scripts/powershell/create-new-feature-branch.ps1 index 68983db63c..3c47d17e37 100644 --- a/extensions/git/scripts/powershell/create-new-feature-branch.ps1 +++ b/extensions/git/scripts/powershell/create-new-feature-branch.ps1 @@ -446,7 +446,10 @@ if ($env:GIT_BRANCH_NAME) { $branchSuffix = Get-BranchName -Description $featureDesc } - if ($Timestamp -and $Number -ne 0) { + # Warn if -Number and -Timestamp are both specified. Use ContainsKey (not + # `-ne 0`) so an explicit `-Number 0` is also detected, matching the bash twin's + # `[ -n "$BRANCH_NUMBER" ]` check. + if ($Timestamp -and $PSBoundParameters.ContainsKey('Number')) { Write-Warning "[specify] Warning: -Number is ignored when -Timestamp is used" $Number = 0 } @@ -456,7 +459,10 @@ if ($env:GIT_BRANCH_NAME) { $branchName = New-BranchName -FeatureNum $featureNum -BranchSuffix $branchSuffix } else { $branchScopePrefix = Get-BranchScopePrefix -Template $branchTemplate -BranchSuffix $branchSuffix - if ($Number -eq 0) { + # Auto-detect the next number only when -Number was not supplied; an + # explicit value (including 0) is honored, matching the bash twin's + # `[ -z "$BRANCH_NUMBER" ]` check. + if (-not $PSBoundParameters.ContainsKey('Number')) { if ($DryRun -and $hasGit) { $Number = Get-NextBranchNumber -SpecsDir $specsDir -SkipFetch -ScopePrefix $branchScopePrefix } elseif ($DryRun) { diff --git a/tests/extensions/git/test_git_extension.py b/tests/extensions/git/test_git_extension.py index 71dfd02223..0478b90850 100644 --- a/tests/extensions/git/test_git_extension.py +++ b/tests/extensions/git/test_git_extension.py @@ -638,6 +638,21 @@ def test_specify_init_dir_with_stale_core_errors(self, tmp_path: Path): assert result.returncode != 0 assert "requires updated Spec Kit core scripts" in result.stderr + def test_explicit_number_zero_is_honored(self, tmp_path: Path): + """An explicit --number 0 is honored (yields 000), not treated as + 'auto-detect'. Pins the canonical behavior the PowerShell twin must + mirror; the empty-string check (`[ -z "$BRANCH_NUMBER" ]`) already + distinguishes an unset flag from a supplied 0.""" + project = _setup_project(tmp_path) + result = _run_bash( + "create-new-feature-branch.sh", project, + "--json", "--dry-run", "--number", "0", "--short-name", "zero", "Zero feature", + ) + assert result.returncode == 0, result.stderr + data = json.loads(result.stdout) + assert data["BRANCH_NAME"] == "000-zero" + assert data["FEATURE_NUM"] == "000" + @pytest.mark.skipif(not HAS_PWSH, reason="pwsh not available") class TestCreateFeaturePowerShell: @@ -942,6 +957,23 @@ def test_specify_init_dir_with_stale_core_errors(self, tmp_path: Path): assert result.returncode != 0 assert "requires updated Spec Kit core scripts" in result.stderr + def test_explicit_number_zero_is_honored(self, tmp_path: Path): + """An explicit -Number 0 is honored (yields 000), matching the bash twin's + --number 0. Regression guard: -Number defaults to 0, so a bare `-eq 0` + check cannot tell an unset flag from a supplied 0 and would silently + auto-detect instead. Uses PSBoundParameters.ContainsKey('Number').""" + project = _setup_project(tmp_path) + result = _run_pwsh( + "create-new-feature-branch.ps1", project, + "-Json", "-DryRun", "-Number", "0", "-ShortName", "zero", "Zero feature", + ) + assert result.returncode == 0, result.stderr + json_line = [ln for ln in result.stdout.splitlines() if ln.strip().startswith("{")] + assert json_line, f"No JSON in output: {result.stdout}" + data = json.loads(json_line[-1]) + assert data["BRANCH_NAME"] == "000-zero" + assert data["FEATURE_NUM"] == "000" + # ── auto-commit.sh Tests ─────────────────────────────────────────────────────