Skip to content
This repository was archived by the owner on Apr 5, 2026. It is now read-only.

Commit 754664d

Browse files
HerbHallclaude
andcommitted
feat: cross-machine conflict resolution strategy
Add conflict detection to sync.ps1 -Status (compares local vs remote changes, flags overlapping files, identifies append-only entries needing renumber). Add resolve-conflicts workflow to /devkit-sync skill with guided resolution for append-only and whole-file conflicts. Closes #66 Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 49504d5 commit 754664d

3 files changed

Lines changed: 184 additions & 0 deletions

File tree

claude/skills/devkit-sync/SKILL.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ What sync operation do you need?
2727
4. **Init** -- Set up symlinks and machine identity (first-time setup)
2828
5. **Diff** -- Show detailed diff of local changes vs DevKit main
2929
6. **Unlink** -- Replace symlinks with copies (portable snapshot)
30+
7. **Resolve conflicts** -- Fix merge conflicts after pull/rebase
3031

3132
Or just type your question about DevKit sync.
3233
</intake>
@@ -40,6 +41,7 @@ Or just type your question about DevKit sync.
4041
| 4, "init", "setup", "install", "link" | workflows/init.md |
4142
| 5, "diff", "changes", "what changed" | workflows/diff.md |
4243
| 6, "unlink", "copy", "snapshot", "portable" | workflows/unlink.md |
44+
| 7, "resolve", "conflicts", "conflict", "merge conflict", "rebase conflict" | workflows/resolve-conflicts.md |
4345

4446
**After reading the workflow, follow it exactly.**
4547
</routing>
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
# DevKit Sync: Resolve Conflicts
2+
3+
Guide the user through resolving conflicts after `git pull --rebase` fails on the DevKit clone.
4+
5+
## Detect Conflict Type
6+
7+
1. **Check rebase status:**
8+
9+
```bash
10+
git -C <devkit> status
11+
```
12+
13+
If output shows "rebase in progress", a pull has already failed. If not, the user may be running this proactively after seeing conflicts in `-Status` output.
14+
15+
2. **Identify conflicted files:**
16+
17+
```bash
18+
git -C <devkit> diff --name-only --diff-filter=U
19+
```
20+
21+
3. **Classify each file** by checking `.sync-manifest.json`:
22+
- If the file is in `append_only_files` (e.g., `autolearn-patterns.md`, `known-gotchas.md`): use the **Append-Only Resolution** flow
23+
- Otherwise: use the **Whole-File Resolution** flow
24+
25+
## Append-Only Resolution
26+
27+
These files are numbered entry lists. Two machines may add entries with the same number, causing merge conflicts on the entry headers.
28+
29+
1. **Accept the incoming (remote) version as the base:**
30+
31+
```bash
32+
git -C <devkit> checkout --theirs <file>
33+
```
34+
35+
2. **Find the highest entry number in the accepted version:**
36+
37+
```bash
38+
grep -oP '^## \K\d+' <devkit>/<file> | sort -n | tail -1
39+
```
40+
41+
For files using `**N.**` pattern instead of `## N`:
42+
43+
```bash
44+
grep -oP '^\*\*\K\d+' <devkit>/<file> | sort -n | tail -1
45+
```
46+
47+
3. **Retrieve the local machine's new entries from the conflict backup:**
48+
49+
```bash
50+
git -C <devkit> show REBASE_HEAD:<file> > /tmp/local-version.md
51+
```
52+
53+
Diff against the common ancestor to isolate only new entries:
54+
55+
```bash
56+
git -C <devkit> diff REBASE_HEAD~1..REBASE_HEAD -- <file>
57+
```
58+
59+
4. **Renumber the local entries** starting from `highest + 1` and append them to the file.
60+
61+
5. **Stage and continue:**
62+
63+
```bash
64+
git -C <devkit> add <file>
65+
git -C <devkit> rebase --continue
66+
```
67+
68+
## Whole-File Resolution
69+
70+
For skills, agents, config files, and other non-append content.
71+
72+
1. **Show both versions side-by-side:**
73+
74+
```bash
75+
# Remote (incoming) version
76+
git -C <devkit> show origin/main:<file>
77+
78+
# Local (current) version
79+
git -C <devkit> show REBASE_HEAD:<file>
80+
81+
# Unified diff
82+
git -C <devkit> diff
83+
```
84+
85+
2. **Ask the user to choose:**
86+
- **Keep remote:** `git -C <devkit> checkout --theirs <file>`
87+
- **Keep local:** `git -C <devkit> checkout --ours <file>`
88+
- **Manual merge:** User edits the file to combine both changes
89+
90+
3. **Stage and continue:**
91+
92+
```bash
93+
git -C <devkit> add <file>
94+
git -C <devkit> rebase --continue
95+
```
96+
97+
## Branch Strategy
98+
99+
Each machine pushes to its own sync branch to avoid direct conflicts on main.
100+
101+
- **Branch naming:** `sync/<machine-id>` (read from `~/.claude/.machine-id`)
102+
- **Workflow:**
103+
1. Machine A pushes to `sync/machine-a`, opens PR to main
104+
2. Machine B pushes to `sync/machine-b`, opens PR to main
105+
3. User reviews and merges one PR at a time on GitHub
106+
4. After merging Machine A's PR, Machine B runs `/devkit-sync pull` to rebase onto updated main
107+
5. If rebase conflicts occur, Machine B runs `/devkit-sync resolve-conflicts`
108+
6. Machine B force-pushes its updated branch: `git push --force-with-lease origin sync/machine-b`
109+
110+
## Aborting
111+
112+
If resolution gets too complex, abort and start fresh:
113+
114+
```bash
115+
git -C <devkit> rebase --abort
116+
```
117+
118+
Then manually inspect both versions and cherry-pick changes.
119+
120+
## Edge Cases
121+
122+
- **Multiple conflicted commits:** Rebase may pause at each commit. Repeat the resolution flow for each stop.
123+
- **No rebase in progress:** User ran this proactively. Show the `-Status` conflict detection output and suggest running pull first.
124+
- **Network unavailable:** Cannot fetch remote. Show local changes only and suggest trying again when online.

setup/sync.ps1

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,64 @@ function Invoke-Status {
488488
if ($notSymlink -gt 0) { Write-Warn "Real files: $notSymlink (run -Link to convert)" }
489489
if ($missing -gt 0) { Write-Fail "Missing: $missing" }
490490
if ($broken -gt 0) { Write-Fail "Broken: $broken" }
491+
492+
# Potential conflicts (local changes overlapping with remote)
493+
Write-Host ''
494+
Write-Step 'Potential conflicts:'
495+
$localChanges = @()
496+
$remoteChanges = @()
497+
try {
498+
$localRaw = & git -C $devKit diff --name-only 2>&1
499+
if ($localRaw) {
500+
$localChanges = @($localRaw) | Where-Object { $_ -is [string] -and $_.Trim() }
501+
}
502+
# Include staged changes too
503+
$stagedRaw = & git -C $devKit diff --cached --name-only 2>&1
504+
if ($stagedRaw) {
505+
$staged = @($stagedRaw) | Where-Object { $_ -is [string] -and $_.Trim() }
506+
$localChanges = @($localChanges) + @($staged) | Select-Object -Unique
507+
}
508+
}
509+
catch {
510+
$localChanges = @()
511+
}
512+
try {
513+
& git -C $devKit fetch origin --quiet 2>&1 | Out-Null
514+
$remoteRaw = & git -C $devKit diff --name-only 'HEAD...origin/main' 2>&1
515+
if ($remoteRaw) {
516+
$remoteChanges = @($remoteRaw) | Where-Object { $_ -is [string] -and $_.Trim() }
517+
}
518+
}
519+
catch {
520+
$remoteChanges = @()
521+
}
522+
523+
if ($localChanges.Count -eq 0 -and $remoteChanges.Count -eq 0) {
524+
Write-OK 'No local or remote changes detected'
525+
}
526+
elseif ($localChanges.Count -eq 0) {
527+
Write-OK "No local changes ($($remoteChanges.Count) remote-only change(s))"
528+
}
529+
elseif ($remoteChanges.Count -eq 0) {
530+
Write-OK "No remote changes ($($localChanges.Count) local-only change(s))"
531+
}
532+
else {
533+
# Find overlapping files
534+
$overlapping = @($localChanges | Where-Object { $remoteChanges -contains $_ })
535+
if ($overlapping.Count -gt 0) {
536+
Write-Warn "$($overlapping.Count) file(s) modified both locally and on remote:"
537+
foreach ($file in $overlapping) {
538+
# Check if this is an append-only file
539+
$isAppendOnly = $manifest.append_only_files -contains $file
540+
$tag = if ($isAppendOnly) { ' (append-only -- renumber entries)' } else { '' }
541+
Write-Warn " $file$tag"
542+
}
543+
Write-Step 'Run /devkit-sync resolve-conflicts for resolution guidance.'
544+
}
545+
else {
546+
Write-OK "No overlapping changes ($($localChanges.Count) local, $($remoteChanges.Count) remote -- no conflicts expected)"
547+
}
548+
}
491549
}
492550

493551
# ---------------------------------------------------------------------------

0 commit comments

Comments
 (0)