chore: remove dead code from branchless refactor (#510)

Remove methods, fields, and comments left over after the branchless
worktree architecture landed:

- git-service.ts: delete isOnSliceBranch, getActiveSliceBranch,
  branchExists (private), discardUntrackedRuntimeFiles (private);
  update writeIntegrationBranch comment and remove --force flag
- worktree.ts: delete isOnSliceBranch, getActiveSliceBranch exports;
  update module doc from "branch-per-slice" to "worktree utilities"
- state.ts: remove activeBranch computation (always null)
- types.ts: remove GSDState.activeBranch field
- templates/state.md: remove "Slice Branch" line
- auto.ts, guided-flow.ts: update "branch-per-slice" comments
- Tests updated to match

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
TÂCHES 2026-03-15 13:42:41 -06:00 committed by GitHub
parent e6d55f8aaf
commit dda9d713f2
10 changed files with 20 additions and 109 deletions

View file

@ -553,7 +553,7 @@ export async function startAuto(
return;
}
// Ensure git repo exists — GSD needs it for branch-per-slice
// Ensure git repo exists — GSD needs it for worktree isolation
try {
execSync("git rev-parse --git-dir", { cwd: base, stdio: "pipe" });
} catch {

View file

@ -132,13 +132,11 @@ export function readIntegrationBranch(basePath: string, milestoneId: string): st
* Persist the integration branch for a milestone.
*
* Called when auto-mode starts on a milestone. Records the branch the user
* was on at that point, so that slice branches merge back to it instead of
* the repo's default branch. Idempotent when the branch matches; updates
* the record when the user starts from a different branch.
* was on at that point, so the milestone worktree merges back to the correct
* branch. Idempotent when the branch matches; updates the record when the
* user starts from a different branch.
*
* The file is committed immediately so it survives branch switches the
* pre-switch auto-commit excludes `.gsd/` to avoid merge conflicts, and
* uncommitted `.gsd/` files are discarded during checkout.
* The file is committed immediately so the metadata is persisted in git.
*/
export function writeIntegrationBranch(basePath: string, milestoneId: string, branch: string): void {
// Don't record slice branches as the integration target
@ -167,7 +165,7 @@ export function writeIntegrationBranch(basePath: string, milestoneId: string, br
// Commit immediately so the metadata is persisted in git.
try {
runGit(basePath, ["add", "--force", metaFile]);
runGit(basePath, ["add", metaFile]);
runGit(basePath, ["commit", "--no-verify", "-F", "-"], {
input: `chore(${milestoneId}): record integration branch`,
});
@ -404,48 +402,8 @@ export class GitServiceImpl {
}
/** True if currently on a GSD slice branch. */
isOnSliceBranch(): boolean {
const current = this.getCurrentBranch();
return SLICE_BRANCH_RE.test(current);
}
/** Returns the slice branch name if on one, null otherwise. */
getActiveSliceBranch(): string | null {
try {
const current = this.getCurrentBranch();
return SLICE_BRANCH_RE.test(current) ? current : null;
} catch {
return null;
}
}
// ─── Branch Lifecycle ──────────────────────────────────────────────────
/**
* Check if a local branch exists. Native libgit2 when available, execSync fallback.
*/
private branchExists(branch: string): boolean {
return nativeBranchExists(this.basePath, branch);
}
/**
* Remove untracked runtime files from the working tree.
*
* Complements `git checkout -- .gsd/` (which only handles tracked files).
* Runtime files can end up untracked after a cleanup commit removes them
* from the current branch's HEAD but the target branch may still have
* them committed. Without this step, `git checkout` fails with:
* "The following untracked working tree files would be overwritten by checkout"
*
* `git clean -fdx` is safe here because:
* - Only removes *untracked* files (tracked files are untouched)
* - Targets only the specific runtime paths listed in RUNTIME_EXCLUSION_PATHS
* - These files are always regenerated by GSD on the next run
*/
private discardUntrackedRuntimeFiles(): void {
this.git(["clean", "-fdx", "--", ...RUNTIME_EXCLUSION_PATHS], { allowFailure: true });
}
// ─── S05 Features ─────────────────────────────────────────────────────
/**

View file

@ -700,7 +700,7 @@ export async function showSmartEntry(
): Promise<void> {
const stepMode = options?.step;
// ── Ensure git repo exists — GSD needs it for branch-per-slice ──────
// ── Ensure git repo exists — GSD needs it for worktree isolation ──────
try {
execSync("git rev-parse --git-dir", { cwd: basePath, stdio: "pipe" });
} catch {

View file

@ -29,7 +29,7 @@ import {
resolveGsdRootFile,
gsdRoot,
} from './paths.js';
import { getActiveSliceBranch } from './worktree.js';
import { milestoneIdSort, findMilestoneIds } from './guided-flow.js';
import { nativeBatchParseGsdFiles, type BatchParsedFile } from './native-parser-bridge.js';
@ -438,8 +438,6 @@ async function _deriveStateImpl(basePath: string): Promise<GSDState> {
};
}
const activeBranch = getActiveSliceBranch(basePath);
// Check if the slice has a plan
const planFile = resolveSliceFile(basePath, activeMilestone.id, activeSlice.id, "PLAN");
const slicePlanContent = planFile ? await cachedLoadFile(planFile) : null;
@ -453,7 +451,7 @@ async function _deriveStateImpl(basePath: string): Promise<GSDState> {
recentDecisions: [],
blockers: [],
nextAction: `Plan slice ${activeSlice.id} (${activeSlice.title}).`,
activeBranch: activeBranch ?? undefined,
registry,
requirements,
progress: {
@ -480,7 +478,7 @@ async function _deriveStateImpl(basePath: string): Promise<GSDState> {
recentDecisions: [],
blockers: [],
nextAction: `All tasks done in ${activeSlice.id}. Write slice summary and complete slice.`,
activeBranch: activeBranch ?? undefined,
registry,
requirements,
progress: {
@ -501,7 +499,7 @@ async function _deriveStateImpl(basePath: string): Promise<GSDState> {
recentDecisions: [],
blockers: [],
nextAction: `Slice ${activeSlice.id} has a plan file but no tasks. Add tasks to the plan.`,
activeBranch: activeBranch ?? undefined,
registry,
requirements,
progress: {
@ -547,7 +545,7 @@ async function _deriveStateImpl(basePath: string): Promise<GSDState> {
recentDecisions: [],
blockers: [`Task ${blockerTaskId} discovered a blocker requiring slice replan`],
nextAction: `Task ${blockerTaskId} reported blocker_discovered. Replan slice ${activeSlice.id} before continuing.`,
activeBranch: activeBranch ?? undefined,
activeWorkspace: undefined,
registry,
requirements,
@ -578,7 +576,6 @@ async function _deriveStateImpl(basePath: string): Promise<GSDState> {
nextAction: hasInterrupted
? `Resume interrupted work on ${activeTask.id}: ${activeTask.title} in slice ${activeSlice.id}. Read continue.md first.`
: `Execute ${activeTask.id}: ${activeTask.title} in slice ${activeSlice.id}.`,
activeBranch: activeBranch ?? undefined,
registry,
requirements,
progress: {

View file

@ -4,7 +4,6 @@
**Active Slice:** {{sliceId}}: {{sliceTitle}}
**Active Task:** {{taskId}}: {{taskTitle}}
**Phase:** {{phase}}
**Slice Branch:** {{activeBranch}}
**Active Workspace:** {{activeWorkspace}}
**Next Action:** {{nextAction}}
**Last Updated:** {{date}}

View file

@ -533,7 +533,7 @@ async function main(): Promise<void> {
return dir;
}
// ─── getCurrentBranch / isOnSliceBranch / getActiveSliceBranch ─────────
// ─── getCurrentBranch ────────────────────────────────────────────────
console.log("\n=== Branch queries ===");
@ -541,21 +541,13 @@ async function main(): Promise<void> {
const repo = initBranchTestRepo();
const svc = new GitServiceImpl(repo);
// On main
assertEq(svc.getCurrentBranch(), "main", "getCurrentBranch returns main on main branch");
assertEq(svc.isOnSliceBranch(), false, "isOnSliceBranch returns false on main");
assertEq(svc.getActiveSliceBranch(), null, "getActiveSliceBranch returns null on main");
// Create and checkout a slice branch manually
run("git checkout -b gsd/M001/S01", repo);
assertEq(svc.getCurrentBranch(), "gsd/M001/S01", "getCurrentBranch returns slice branch name");
assertEq(svc.isOnSliceBranch(), true, "isOnSliceBranch returns true on slice branch");
assertEq(svc.getActiveSliceBranch(), "gsd/M001/S01", "getActiveSliceBranch returns branch name on slice branch");
// Non-slice feature branch
run("git checkout -b feature/foo", repo);
assertEq(svc.isOnSliceBranch(), false, "isOnSliceBranch returns false on non-slice branch");
assertEq(svc.getActiveSliceBranch(), null, "getActiveSliceBranch returns null on non-slice branch");
assertEq(svc.getCurrentBranch(), "feature/foo", "getCurrentBranch returns feature branch name");
rmSync(repo, { recursive: true, force: true });
}

View file

@ -24,9 +24,8 @@ import {
getCurrentBranch,
getMainBranch,
getSliceBranchName,
isOnSliceBranch,
getActiveSliceBranch,
autoCommitCurrentBranch,
SLICE_BRANCH_RE,
} from "../worktree.ts";
import { deriveState } from "../state.ts";
@ -109,8 +108,7 @@ async function main(): Promise<void> {
const sliceBranch = getSliceBranchName("M001", "S01", "alpha");
run(`git checkout -b ${sliceBranch}`, wt.path);
assertEq(getCurrentBranch(wt.path), "gsd/alpha/M001/S01", "worktree-namespaced slice branch");
assertTrue(isOnSliceBranch(wt.path), "isOnSliceBranch returns true");
assertEq(getActiveSliceBranch(wt.path), "gsd/alpha/M001/S01", "getActiveSliceBranch returns namespaced branch");
assertTrue(SLICE_BRANCH_RE.test(getCurrentBranch(wt.path)), "slice branch regex matches namespaced branch");
// ── Do work on slice branch, then merge to worktree branch ─────────────────

View file

@ -7,11 +7,9 @@ import {
autoCommitCurrentBranch,
captureIntegrationBranch,
detectWorktreeName,
getActiveSliceBranch,
getCurrentBranch,
getMainBranch,
getSliceBranchName,
isOnSliceBranch,
parseSliceBranch,
setActiveMilestoneId,
SLICE_BRANCH_RE,
@ -37,12 +35,6 @@ run('git commit -m "chore: init"', base);
async function main(): Promise<void> {
console.log("\n=== getActiveSliceBranch on main ===");
assertEq(getActiveSliceBranch(base), null, "getActiveSliceBranch returns null on main");
console.log("\n=== isOnSliceBranch on main ===");
assertEq(isOnSliceBranch(base), false, "isOnSliceBranch returns false on main");
console.log("\n=== autoCommitCurrentBranch ===");
// Clean — should return null
const cleanResult = autoCommitCurrentBranch(base, "execute-task", "M001/S01/T01");

View file

@ -175,7 +175,6 @@ export interface GSDState {
recentDecisions: string[];
blockers: string[];
nextAction: string;
activeBranch?: string;
activeWorkspace?: string;
registry: MilestoneRegistryEntry[];
requirements?: RequirementCounts;

View file

@ -1,13 +1,11 @@
/**
* GSD Slice Branch Management Thin Facade
* GSD Worktree Utilities
*
* Simple branch-per-slice workflow. No worktrees, no registry.
* Runtime state (metrics, activity, lock, STATE.md) is gitignored
* so branch switches are clean.
* Pure utility functions for worktree name detection, legacy branch name
* parsing, and integration branch capture.
*
* All git-mutation functions delegate to GitServiceImpl from git-service.ts.
* Pure utility functions (detectWorktreeName, getSliceBranchName, parseSliceBranch,
* SLICE_BRANCH_RE) remain standalone.
* SLICE_BRANCH_RE) remain standalone for backwards compatibility.
*
* Branchless architecture: all work commits sequentially on the milestone branch.
* Pure utility functions (detectWorktreeName, getSliceBranchName, parseSliceBranch,
@ -147,26 +145,4 @@ export function autoCommitCurrentBranch(
return getService(basePath).autoCommit(unitType, unitId);
}
// ─── Query Functions (delegate to GitServiceImpl) ──────────────────────────
/**
* Check if we're currently on a slice branch (not main).
* Handles both plain (gsd/M001/S01) and worktree-namespaced (gsd/wt/M001/S01) branches.
*/
export function isOnSliceBranch(basePath: string): boolean {
const current = getCurrentBranch(basePath);
return SLICE_BRANCH_RE.test(current);
}
/**
* Get the active slice branch name, or null if on main.
* Handles both plain and worktree-namespaced branch patterns.
*/
export function getActiveSliceBranch(basePath: string): string | null {
try {
const current = getCurrentBranch(basePath);
return SLICE_BRANCH_RE.test(current) ? current : null;
} catch {
return null;
}
}