* fix(worktree): detect default branch instead of hardcoding "main" on milestone merge (#1668)
Repos using `master` (or any non-`main` default branch) without a GSD
preferences file and without a milestone META.json would have
`mergeMilestoneToMain` fall back to the hardcoded string `"main"`, causing
`git checkout main` to fail. The worktree and milestone branch were left in
an indeterminate state with only a terse error message.
Two targeted fixes:
1. **auto-worktree.ts** — Replace `?? "main"` fallback with
`?? nativeDetectMainBranch(originalBasePath_)`. This function already
exists and is used in 9 other locations; it probes origin/HEAD, then
checks for `main`, `master`, and finally falls back to the current
branch. The resolution order is unchanged for the common case
(integration branch → prefs.main_branch → detected).
2. **worktree-resolver.ts** — Improve the merge-failure warning from a bare
"Milestone merge failed: <reason>" to an actionable message that
explicitly tells the user their worktree and milestone branch are
preserved, and what to do next (retry /complete-milestone or merge
manually). This prevents the panic of "is my code gone?" described in
the issue.
Tests added:
- `auto-worktree-milestone-merge.test.ts`: Test 7 creates a real git repo
with `master` as the default branch, no META.json, and no prefs, then
verifies the squash-merge succeeds and lands on `master`.
- `worktree-resolver.test.ts`: Asserts the failure message includes the
original error, the word "preserved", and a recovery suggestion.
* fix(recovery): add recover-gsd-1668 script for orphaned milestone commits
Users who hit the #1668 bug (milestone branch deleted before merge
succeeded) can use this script to recover their code from git's object
store before git gc prunes the orphaned commits (default: 14–90 days).
The script has two search strategies:
1. Git reflog — checks .git/logs/refs/heads/milestone/<ID> first.
Reflogs survive branch deletion for up to 90 days. This is the
fastest path and requires zero scanning.
2. Git fsck fallback — runs git fsck --unreachable --no-reflogs to
find all orphaned commit objects, then scores them in a single
git log --no-walk batch call (not per-commit git show, which would
be O(n) process launches). Scores by:
- Milestone ID match in subject (+100)
- GSD conventional commit pattern feat(M<id>...) (+50)
- Milestone-related keywords in subject (+20)
- Committed within last 7 days (+10)
Once a commit is selected (interactively or via --auto), the script
creates recovery/<1668>/<milestone-id> branch and prints the exact
commands to inspect, merge, and clean up.
Supports: --milestone <ID>, --dry-run, --auto
Platforms: bash (Linux/macOS) and PowerShell (Windows)