T-BR8T-start-task-accepts-base-ref
Status: closed/superseded · Impact: medium · Complexity: small
AUTO-DEFINED: this spec was best-effort machine-authored by /sdlc:task-auto-define on 2026-07-20 because the task is autonomy: autonomous/pr. Review the Goal, Approach, Today, Files-to-touch, and Acceptance-criteria carefully before trusting it.
/sdlc:task-work cannot pick up a task that must sit on top of another
task’s branch: start_task.ts hardcodes origin/main as the branch base,
so a stacked PR’s base is destroyed at pickup and the operator has to
repair the stack by hand. This task makes the base an explicit input.
apps/sdlc/skills/task-work/start_task.ts ends by running
git reset --hard origin/mainon the new task branch, which destroys the base of a stacked PR. During T-C9RD the task had to sit on task/T-J5DI-remove-clear-duplicate-augmented-packages, so the operator manually rebased the sibling branch, force-pushed its PR, and re-pointed the branch by hand. The fix: start_task.ts should accept a--base <ref>flag defaulting toorigin/main, reset/branch from that ref instead of hardcodingorigin/main, and /sdlc:task-work Step 3 in apps/sdlc/skills/task-work/SKILL.md should document stacking as a first-class flow. Touchpoints: apps/sdlc/skills/task-work/start_task.ts and apps/sdlc/skills/task-work/SKILL.md.
| Location | Role today |
|---|---|
apps/sdlc/skills/task-work/start_task.ts#startTask | Lands the start-commit on origin/main via an ephemeral worktree, transitions the lease, then ends with a hardcoded git fetch origin main + git reset --hard origin/main inside the task worktree. That final reset is what destroys a stacked base. |
apps/sdlc/skills/task-work/start_task.ts#parseArgs | Parses --worktree, --branch, --main-repo, --today, --lease-authority. No base flag exists. |
apps/sdlc/skills/task-work/start_task.ts#StartTaskOptions | The typed option bag main() fills and startTask() consumes; carries no base field. |
apps/sdlc/skills/task-work/SKILL.md | Step 4 creates the worktree with git worktree add .sdlc/worktrees/<basename> -b task/<basename> main; Step 5b shells out to start_task.ts and documents its exit codes. Neither step mentions stacking on a sibling task branch. |
apps/sdlc/skills/task-work/tests/start_task.test.ts | Integration tests over a seeded main repo + bare origin + worktree; asserts the start-commit lands on origin/main and the task branch is reset to that tip. |
apps/sdlc/skills/task-work/check_ancestry.ts | Step 9’s ancestry classifier, which reasons against origin/main. Untouched by this task (see Out of scope). |
Proposed
Section titled “Proposed”start_task.ts gains an optional --base <ref> argument that defaults to
origin/main. Only the trailing branch-reset block consults it; the
start-commit still lands on origin/main, because task lifecycle state
lives on main regardless of where the feature branch is rooted. SKILL.md
grows a stacked-pickup path in Step 4 (branch the worktree from the sibling
ref) and threads the same ref through Step 5b’s invocation.
Approach
Section titled “Approach”- In
apps/sdlc/skills/task-work/start_task.ts#parseArgs, add abase: string | nullfield toParsedArgs(defaulting tonull) and parse--base <ref>alongside the existing flags. - Add
base: string | nulltoStartTaskOptionsand thread the parsed value throughmain()intostartTask(). - Inside
startTask(), resolve the effective ref once near the top:const baseRef = opts.base ?? "origin/main". Leave thecommitToMainViaWorktreecall and thereadTask(..., { at: "origin/main" })source-of-truth read untouched — the start-commit and the eligibility read stay pinned toorigin/mainby design. - Refresh the base before resetting. Keep the existing
git fetch origin main --quiet(the start-commit push must be visible). Additionally, whenbaseRefhas the shapeorigin/<branch>and<branch>is notmain, rungit fetch origin <branch> --quietin the worktree; whenbaseRefis a local ref (e.g.task/<other-basename>) skip the extra fetch. - Verify the ref resolves before mutating the branch: run
git -C <worktree> rev-parse --verify <baseRef>^{commit}and, on a non-zero exit, throwStartTaskErrorwith exit code2naming the unresolvable ref. This runs after the lease transition and before the reset, so a typo’d base cannot leave the branch pointing anywhere new. - Replace the hardcoded
git reset --hard origin/mainwithgit reset --hard <baseRef>, and rewrite the failure message to interpolatebaseRefinstead of the literalorigin/main. - Update the file’s header docblock — the
Usage:line gains[--base <ref>], the prose describing the final reset names the base ref, and the exit-code table records that2now also covers an unresolvable base. - Extend
apps/sdlc/skills/task-work/tests/start_task.test.tswith three cases: (a) the base flag omitted still resets ontoorigin/main(assert against the existing seeded fixture); (b) with the flag naming a second local branch seeded off an earlier commit, the worktree HEAD equals that branch’s tip while the start-commit is still onorigin/main; (c) an unresolvable base exits2and leaves the worktree HEAD unchanged. - In
apps/sdlc/skills/task-work/SKILL.mdStep 4, add a short “Stacked pickup” subsection: when the task must sit on another task’s branch, rungit worktree add .sdlc/worktrees/<basename> -b task/<basename> <base-ref>with<base-ref>defaulting tomain, and carry that same ref forward to Step 5b. - In
SKILL.mdStep 5b, add--base <ref>to the invocation block with a one-line note that it must match the ref Step 4 branched from, state that the start-commit still lands onorigin/maineither way, and add the unresolvable-base case to the exit-2bullet. - Refresh the per-skill doc by running
/sdlc:update-skill-doc task-worksodocs/skills/task-work.mdreflects the new Step 4/5b prose.
Files to touch
Section titled “Files to touch”| Location | Kind | Change |
|---|---|---|
apps/sdlc/skills/task-work/start_task.ts | modify | Parse and thread --base; resolve baseRef; conditional base fetch; rev-parse --verify gate; reset onto baseRef; docblock refresh. |
apps/sdlc/skills/task-work/tests/start_task.test.ts | modify | Add default-base, stacked-base, and unresolvable-base cases. |
apps/sdlc/skills/task-work/SKILL.md | modify | Step 4 stacked-pickup subsection; Step 5b invocation gains the base flag; exit-2 bullet covers an unresolvable base. |
docs/skills/task-work.md | modify | Regenerate via /sdlc:update-skill-doc task-work to match the new SKILL.md steps. |
Acceptance criteria
Section titled “Acceptance criteria”- AC-1: Invoked without the base flag,
start_task.tsresets the task branch ontoorigin/mainexactly as today — the pre-existing happy-path case inapps/sdlc/skills/task-work/tests/start_task.test.tspasses with its assertions unmodified. - AC-2: Invoked with
--base <ref>naming a resolvable local branch,git -C <worktree> rev-parse HEADequalsgit rev-parse <ref>after the run, andgit log <ref>..task/<basename>is empty. - AC-3: With
--base <ref>set to a non-main ref, the start-commit still lands onorigin/main:git log origin/main -1 --format=%sischore(tasks): start <basename>. - AC-4: Invoked with a base ref that does not resolve, the process
exits
2, stderr names the offending ref, and the worktree’s HEAD SHA is byte-identical to its pre-invocation value. - AC-5:
bun testrun overapps/sdlc/skills/task-work/tests/start_task.test.tsexits0. - AC-6:
apps/sdlc/skills/task-work/SKILL.mdStep 4 documents branching the worktree from a non-mainbase, and Step 5b’s invocation block shows the base flag;command grep -c -- '--base' apps/sdlc/skills/task-work/SKILL.mdreturns at least2.
Out of scope
Section titled “Out of scope”- Teaching Step 9’s
apps/sdlc/skills/task-work/check_ancestry.tsabout a non-main base; itsorigin/mainclassification is unchanged. - Auto-deriving the base from the task’s
related:/depends_on:frontmatter — the base stays an explicit operator-supplied argument. - Relocating where the start-commit lands: task lifecycle state still
commits and pushes to
origin/mainregardless of the base. - Restacking, rebasing, or force-pushing sibling branches when the base moves after pickup.
- Passing the base automatically from
/sdlc:orchestrate’s dispatch of/sdlc:task-work.
Dependencies
Section titled “Dependencies”- none
Discovery context
Section titled “Discovery context”Spawned by /sdlc:spawn-task-pr on 2026-07-20 UTC from T-C9RD-consolidate-augmented-into-solutions in https://github.com/sksizer/dev.