fix: apply lessons from real-world migration test run

Based on analysis of a live migration (v1.2.42 -> v1.2.47):

1. Absolute worktree paths: Bash tool resets cwd between calls,
   so relative cd .upgrade-worktree fails. Store PROJECT_ROOT and
   WORKTREE as absolute paths, use them throughout.

2. Smarter tier assessment: discount files from skill merges when
   counting — a fork with 3 skills and no other changes is Tier 2,
   not Tier 3 just because 24 files changed.

3. Inter-skill conflict analysis: new "Skill Interactions" section
   in the migration guide captures conflicts between applied skills
   (duplicate declarations, conflicting env var handling).

4. Cleaner swap recipe: use git reset --hard to the upgrade commit
   instead of git checkout -B intermediate branch. Backup tag
   preserves rollback. Copy guide to /tmp before worktree removal.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
gavrielc
2026-04-04 00:20:13 +03:00
parent f60bb3c3d5
commit 7ef1c4f5e0

View File

@@ -22,6 +22,7 @@ Two phases: **Extract** (build the migration guide) and **Upgrade** (use it). If
- Data directories (`groups/`, `store/`, `data/`, `.env`) are never touched — only code.
- Be helpful: offer to do things (stash, commit, stop services) rather than telling the user to do them.
- **Use sub-agents for exploration.** Spawn haiku sub-agents to explore the codebase, trace skill merges, diff files, and identify customizations. This keeps the main context focused on the user conversation and decision-making.
- **Always use absolute paths in worktrees.** The Bash tool resets the working directory between calls. Never use relative `cd .upgrade-worktree` — always use the full absolute path: `cd /absolute/path/.upgrade-worktree && <command>`. Store the worktree absolute path in a variable at creation time and reference it throughout.
- **Balance exploration and asking.** Don't bombard the user with questions when you can figure things out from the code. Don't burn endless tokens exploring when the user could clarify in one sentence. Use sub-agents to explore first, then ask the user targeted questions about things that are ambiguous or where intent isn't clear from the code alone.
- **Scale effort to complexity.** Not every migration needs the full process. Assess the scope early and take the lightest path that fits.
@@ -72,9 +73,10 @@ Conditions:
### Tier 3: Complex
Conditions (any of):
- Large total diff (15+ changed files) or many new files added (indicates many skills applied)
- Lots of insertions/deletions suggesting deep source changes
- Many skills applied (the presence of many new files — channel files, skill directories — is the signal, not per-file analysis)
- Many new files added (indicates many skills applied) — discount files that come purely from skill merges when assessing complexity; a fork with 3 skills and no other changes is simpler than it looks by file count alone
- Deep source changes to core files (`src/index.ts`, `src/container-runner.ts`, etc.) beyond what skills introduced
- Lots of insertions/deletions in user-authored code (not skill-merged code)
- Many skills applied (3+) AND the user confirms or sub-agents find customizations on top of them
Use the full process: multiple sub-agents in parallel, directory-based guide, migration plan.
@@ -150,6 +152,13 @@ Each sub-agent task:
> 4. Assess detail level: could a fresh Claude session reproduce this from intent alone, or does it need specific code snippets, API details, import paths?
> 5. For non-standard changes, extract the key code, imports, API calls, and configurations verbatim.
**Inter-skill conflicts:** If multiple skills are applied, spawn an additional sub-agent to check for interactions between them. Look for:
- Duplicate declarations (same variable/constant defined by two skill branches)
- Conflicting approaches (one skill throws on missing env var, another provides a fallback)
- Shared files modified by multiple skills
Document any findings in the "Skill Interactions" section of the migration guide so they can be resolved after skill branches are re-merged during upgrade.
## 1.5 Confirm with user
After sub-agents report back, compile the findings and present to the user.
@@ -205,6 +214,15 @@ List each skill with its branch name. These are reapplied by merging the upstrea
Custom skills (user-created, not from upstream): `.claude/skills/my-custom-skill/` — copy as-is from main tree.
## Skill Interactions
(Document known conflicts or interactions between applied skills.
When two or more skills modify the same file or depend on shared
config, describe the conflict and how to resolve it after merging.
Example: skill A and skill B both add a PROXY_BIND_HOST declaration —
after merging both, deduplicate. Or: skill A throws if ENV_VAR is
missing, but skill B provides a fallback — use the fallback version.)
## Modifications to Applied Skills
### <Skill name>: <what was modified>
@@ -334,9 +352,13 @@ Ask (AskUserQuestion) to proceed or abort.
## 2.3 Create upgrade worktree
```bash
PROJECT_ROOT=$(pwd)
git worktree add .upgrade-worktree upstream/$UPSTREAM_BRANCH --detach
WORKTREE="$PROJECT_ROOT/.upgrade-worktree"
```
Store `$PROJECT_ROOT` and `$WORKTREE` as absolute paths. Use `$WORKTREE` in all subsequent commands — never `cd .upgrade-worktree` with a relative path.
## 2.4 Reapply skills in worktree
For each skill listed in the migration guide's "Applied Skills" section:
@@ -344,7 +366,7 @@ For each skill listed in the migration guide's "Applied Skills" section:
1. Check if branch exists: `git branch -r --list "upstream/$branch"`
2. If yes, merge it in the worktree:
```bash
cd .upgrade-worktree && git merge upstream/skill/<name> --no-edit && cd ..
cd "$WORKTREE" && git merge upstream/skill/<name> --no-edit
```
3. If missing, warn the user (skill may have been removed or renamed upstream).
4. If any skill merge conflicts, stop and tell the user — the skill needs updating for the new upstream.
@@ -369,7 +391,7 @@ For behavior customizations (CLAUDE.md files): copy from the main tree. These ar
## 2.6 Validate in worktree
```bash
cd .upgrade-worktree && npm install && npm run build && npm test; cd ..
cd "$WORKTREE" && npm install && npm run build && npm test
```
If build fails, show the error. Fix only issues caused by the migration. If unclear, ask the user.
@@ -389,13 +411,13 @@ If testing live:
2. Symlink data into the worktree:
```bash
ln -s $(pwd)/store .upgrade-worktree/store
ln -s $(pwd)/data .upgrade-worktree/data
ln -s $(pwd)/groups .upgrade-worktree/groups
ln -s $(pwd)/.env .upgrade-worktree/.env
ln -s "$PROJECT_ROOT/store" "$WORKTREE/store"
ln -s "$PROJECT_ROOT/data" "$WORKTREE/data"
ln -s "$PROJECT_ROOT/groups" "$WORKTREE/groups"
ln -s "$PROJECT_ROOT/.env" "$WORKTREE/.env"
```
3. Start from worktree: `cd .upgrade-worktree && npm run dev`
3. Start from worktree: `cd "$WORKTREE" && npm run dev`
4. Ask the user to send a test message from their phone. Wait for them to confirm it works.
@@ -403,23 +425,40 @@ If testing live:
6. Clean up symlinks:
```bash
rm .upgrade-worktree/store .upgrade-worktree/data .upgrade-worktree/groups .upgrade-worktree/.env
rm "$WORKTREE/store" "$WORKTREE/data" "$WORKTREE/groups" "$WORKTREE/.env"
```
## 2.8 Swap into main tree
The swap must be done carefully — the worktree has the upgraded code, but main needs to point to it cleanly. Use absolute paths throughout.
```bash
UPGRADE_COMMIT=$(cd .upgrade-worktree && git rev-parse HEAD)
git checkout -B upgrade-staging $UPGRADE_COMMIT
git worktree remove .upgrade-worktree --force
# 1. Capture the worktree HEAD before removing it
WORKTREE_PATH=$(cd "$PROJECT_ROOT/.upgrade-worktree" && pwd)
UPGRADE_COMMIT=$(git -C "$WORKTREE_PATH" rev-parse HEAD)
# 2. Copy the migration guide out of the worktree before removing it
cp -r "$WORKTREE_PATH/.nanoclaw-migrations" /tmp/nanoclaw-migrations-backup 2>/dev/null || true
# 3. Remove the worktree
git worktree remove "$WORKTREE_PATH" --force
# 4. Point the current branch at the upgraded commit
git reset --hard $UPGRADE_COMMIT
# 5. Restore the migration guide and update its hashes
cp -r /tmp/nanoclaw-migrations-backup/* .nanoclaw-migrations/ 2>/dev/null || true
rm -rf /tmp/nanoclaw-migrations-backup
```
Copy the migration guide back and update its header hashes. Offer to commit:
Update the guide's header hashes to reflect the new state. Offer to commit:
```bash
git add .nanoclaw-migrations/
git commit -m "chore: upgrade to upstream $(git rev-parse --short upstream/$UPSTREAM_BRANCH)"
```
Do NOT use `git checkout -B` to create an intermediate branch — this caused issues in practice. The `git reset --hard` to the upgrade commit is the cleanest path since the backup tag already preserves the pre-upgrade state.
## 2.9 Post-upgrade
Run `npm install && npm run build` in the main tree to confirm.