Skip to content

T-AHL1-sdlc-arm-commit-worktree-from-maincheckout

Status: closed/done · Impact: medium · Complexity: small

T-ML6A (PR #894) armed the ephemeral commit-worktree that commitToMainViaWorktree creates, so pre-commit hooks can resolve the workspace deps (commander, yaml) that Bun’s isolated linker places only at apps/sdlc/node_modules. The arm reads its verb list from the caller’s raw projectRoot, but ensure_ready_mutate.ts passes dirname(taskFilePath) — for the canonical nested path that is …/docs/planning/tasks, where loadConfig finds no sdlc.yaml and returns defaults (empty verbs). So the arm silently no-ops and every /sdlc:task-work Step 5a (--commit-on main readiness stamp) fails with Cannot find package 'commander'. This makes the readiness gate unusable repo-wide; only Step 5b (start_task.ts, which passes the resolved main-repo root) is arming correctly.

LocationRole today
apps/sdlc/lib/services/git/commit-to-main.tscommitToMainViaWorktree resolves mainCheckout (git-common-dir’s parent, correct from any subdir) and creates the ephemeral worktree under it, but arms with the raw projectRoot
apps/sdlc/skills/task-ensure-ready/ensure_ready_mutate.tsIts --commit-on path passes projectRoot: dirname(path) — the tasks subdir for a canonical task path — so the arm verb list resolves empty
apps/sdlc/lib/services/git/arm-worktree.tscommitWorktreeInitVerbs(projectRoot) reads loadConfig(projectRoot); loadConfig reads sdlc.yaml only at the exact root (no walk-up), so a subdir yields defaults
apps/sdlc/lib/services/git/tests/commit-to-main.test.tsThe arm test passes projectRoot: mainDir (the root), so the subdir case that breaks in production is untested

commitToMainViaWorktree arms the ephemeral worktree from the already-resolved mainCheckout rather than the caller’s raw projectRoot. Because the ephemeral worktree is created under mainCheckout/.sdlc/commit-worktrees/, its deps must come from mainCheckout’s config — so arming from mainCheckout is both the correct source and robust to any caller that passes a subdir. ensure_ready_mutate.ts then arms correctly with no caller-side change, and start_task.ts (whose projectRoot already equals its mainCheckout) is unaffected.

  1. In commitToMainViaWorktree, arm from the resolved mainCheckout instead of projectRoot (the value is already computed above the worktree add).
  2. Add a regression test: drive commitToMainViaWorktree with projectRoot pointed at a subdirectory of the main checkout and assert the declared arm verbs still run (they would resolve empty under the old code).
  3. Run the git-service suite plus tsc --noEmit; confirm the existing root-projectRoot arm test still passes.
LocationKindChange
apps/sdlc/lib/services/git/commit-to-main.tsmodifyArm from mainCheckout, not projectRoot
apps/sdlc/lib/services/git/tests/commit-to-main.test.tsmodifyAdd subdir-projectRoot arm regression test
  • AC-1: commitToMainViaWorktree invoked with a subdir projectRoot (whose main-checkout root declares commit_worktree_init:) runs the declared arm verbs; a new test asserts this and fails against the pre-fix code.
  • AC-2: The existing “arms the worktree after worktree add” test (root projectRoot) still passes unchanged.
  • AC-3: bun test apps/sdlc/lib/services/git and bunx tsc --noEmit pass in the worktree.
  • Changing loadConfig to walk ancestors — the arm-source fix is sufficient and narrower; a walk-up default is a broader behavior change with its own blast radius.
  • The backlog-capture arm call site (apps/sdlc/cli/backlog_cli/create.ts), which builds its worktree under opts.projectRoot and arms with the same opts.projectRoot (self-consistent), and is invoked from the project root.
  • none — builds on T-ML6A, already merged.
  • Found 2026-07-18 while running /sdlc:task-work on the augmented_books adoption tasks: Step 5a failed with Cannot find package 'commander' even after #894 merged. Traced to commitWorktreeInitVerbs(dirname(taskPath)) resolving [] for the nested tasks-directory projectRoot.

← Back to Tasks