Wrap the {phase} substitution in normalizePhaseName() at both fix sites:
- src/init.cts — cmdInitExecutePhase branch_name output
- src/commands.cts — cmdCommit pre-execution branch derivation
When project_code is set (e.g. "CK"), extractPhaseToken returns the
full prefixed token "CK-01" as phase_number. Without normalization the
generated branch was "gsd/phase-CK-01-foundation"; after this fix it is
"gsd/phase-01-foundation", matching the documented {phase} contract
(padded numeric only).
Adds a regression test in tests/init.test.cjs.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
5
.changeset/904-init-execute-phase-normalize-branch.md
Normal file
5
.changeset/904-init-execute-phase-normalize-branch.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
type: Fixed
|
||||
pr: 904
|
||||
---
|
||||
**`init execute-phase` and `cmdCommit` now produce correct `branch_name` when `project_code` is set** — the `{phase}` substitution in `phase_branch_template` now calls `normalizePhaseName()`, stripping the project-code prefix and zero-padding the number, so the generated branch is e.g. `gsd/phase-01-foundation` instead of `gsd/phase-CK-01-foundation`. Both the execute-phase output path (`src/init.cts`) and the pre-execution commit path (`src/commands.cts`) are fixed. (#904)
|
||||
@@ -547,7 +547,7 @@ function cmdCommit(cwd: string, message: string | undefined, files: string[] | u
|
||||
const phaseInfo = findPhaseInternal(cwd, phaseNum) as Record<string, unknown> | null;
|
||||
if (phaseInfo) {
|
||||
branchName = (config['phase_branch_template'] as string)
|
||||
.replace('{phase}', phaseInfo['phase_number'] as string)
|
||||
.replace('{phase}', normalizePhaseName(phaseInfo['phase_number']))
|
||||
.replace('{slug}', (phaseInfo['phase_slug'] as string) || 'phase');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -260,7 +260,7 @@ function cmdInitExecutePhase(
|
||||
config.branching_strategy === 'phase' && phaseInfo
|
||||
? (config.phase_branch_template as string)
|
||||
.replace('{project}', (config.project_code as string) || '')
|
||||
.replace('{phase}', phaseInfo['phase_number'] as string)
|
||||
.replace('{phase}', normalizePhaseName(phaseInfo['phase_number']))
|
||||
.replace('{slug}', (phaseInfo['phase_slug'] as string) || 'phase')
|
||||
: config.branching_strategy === 'milestone'
|
||||
? (config.milestone_branch_template as string)
|
||||
|
||||
@@ -538,6 +538,35 @@ describe('init plan-phase zero-padded phase number (bug #2391)', () => {
|
||||
assert.strictEqual(out03.phase_req_ids, out3.phase_req_ids,
|
||||
'phase_req_ids must be identical regardless of padding');
|
||||
});
|
||||
|
||||
// ── #904: branch_name must use normalized (stripped + zero-padded) phase number ──
|
||||
// When project_code is set (e.g. "CK") the phase directory is prefixed:
|
||||
// "CK-01-foundation". extractPhaseToken returns "CK-01" as phase_number.
|
||||
// branch_name must call normalizePhaseName so it strips the prefix and zero-pads,
|
||||
// producing "gsd/phase-01-foundation" rather than "gsd/phase-CK-01-foundation".
|
||||
test('branch_name uses normalized phase number when project_code prefixes phase dir (#904)', () => {
|
||||
seedPhase(tmpDir, 'CK-01-foundation', {
|
||||
'CK-01-01-PLAN.md': '# Plan',
|
||||
});
|
||||
fs.writeFileSync(
|
||||
path.join(tmpDir, '.planning', 'config.json'),
|
||||
JSON.stringify({
|
||||
project_code: 'CK',
|
||||
git: {
|
||||
branching_strategy: 'phase',
|
||||
phase_branch_template: 'gsd/phase-{phase}-{slug}',
|
||||
},
|
||||
}, null, 2)
|
||||
);
|
||||
|
||||
const result = runGsdTools('init execute-phase 1', tmpDir);
|
||||
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||
|
||||
const output = JSON.parse(result.output);
|
||||
// branch_name must use the normalized phase number, not the raw "CK-01" token
|
||||
assert.strictEqual(output.branch_name, 'gsd/phase-01-foundation',
|
||||
'branch_name must use normalized phase number (strip project_code prefix, zero-pad), not raw phase_number');
|
||||
});
|
||||
});
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user