fix(sync): connecting a folder adopts the project's files instead of forking them (#159)

A device's first cycle on a volume was treated as a concurrent edit for every
path the project already held. Whichever side's clock happened to sort higher
won -- so a joiner's seeded .bdriveignore or agent-written AGENTS.md could
replace the team's -- and the loser landed beside it as a
.bdrive-conflict-<device>-<time> file.

A first cycle is a join, not an edit. Cycle step 1b holds the scan's ops back
over the pull and demotes any whose path the project already holds to lamport
0, which sorts under every op a project can carry (scan's clock starts at 1).
The project's version then wins deterministically on every device, and
conflictCopies skips those ops the way it already skips re-asserted ones. The
local content is still journaled and pushed, so it stays in History and
`bdrive restore --list <path>` can bring it back. Reported as `adopted: N`.

Concurrent edits after the join are untouched.


Claude-Session: https://claude.ai/code/session_01GLCyEWP2XZhYjBssvdCDQm

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Snow Lee (Sungwon)
2026-08-12 16:10:10 -07:00
committed by GitHub
co-authored by Claude Opus 5
parent 7eabb3baf4
commit 33ca0caeab
8 changed files with 339 additions and 30 deletions
+3
View File
@@ -195,6 +195,9 @@ func printCycle(res *syncer.Result) {
if res.Conflicts > 0 {
fmt.Printf(" conflicts: %d (preserved as *.bdrive-conflict-* files)\n", res.Conflicts)
}
if res.Adopted > 0 {
fmt.Printf(" adopted: %d file(s) replaced by the project's version (yours: `bdrive restore --list <path>`)\n", res.Adopted)
}
if res.Pruned > 0 {
fmt.Printf(" pruned: %d path(s) removed from the hub (kept on disk)\n", res.Pruned)
}