// PART D — adopt-from-CAR fork reconciliation (the RECOVERY for the wj62 fork).
//
// An autobase fork of a shared DID (two divergent authored lines, same base.key) can't be merged in
// place. The recovery: re-adopt the NETWORK-AUTHORITATIVE signed repo (its CAR) into a FRESH line, then
// UNION any record that only landed on the LOSING line so NOTHING is lost, then sign FORWARD (rev > CAR
// rev, prev = CAR commit) as a monotonic continuation the relay accepts. Proves: the winner's records
// are all preserved, a loser-only record is kept (no data loss), the head advances forward, the
// anti-re-author guard holds, and a loser that is a strict subset produces no spurious commit.
import '../lib/bare-globals.js'
import test from 'brittle'
import b4a from 'b4a'
import { SwarmDevice } from '../lib/swarm-device.js'
import { readAdoptRepo } from '../lib/device-node.js'
import { createRepo } from 'atproto-repo/lib/repo.js'
import { tidNow } from 'atproto-store/lib/tid.js'
const DID = 'did:plc:reconcile'
const KEY = (() => { const k = b4a.alloc(32); k[31] = 11; return k })()
const post = (text) => ({ $type: 'app.bsky.feed.post', text, createdAt: '2026-07-20T00:00:00.000Z' })
const follow = (subj) => ({ $type: 'app.bsky.graph.follow', subject: subj, createdAt: '2026-07-20T00:00:00.000Z' })
class MemKV {
constructor () { this.m = new Map() }
async set (k, v) { this.m.set(k, v) }
async get (k) { return this.m.has(k) ? this.m.get(k) : null }
async getNow (k) { return this.get(k) }
async del (k) { this.m.delete(k) }
async list (prefix = '') { const o = {}; for (const [k, v] of this.m) if (k.startsWith(prefix)) o[k] = v; return o }
async listNow (prefix = '') { return this.list(prefix) }
}
test('reconcile: adopt winner CAR + union loser-only record ⇒ UNION, no loss, forward continuation', async (t) => {
// WINNER (authoritative, e.g. tabertron's 3mr436): 3 records. Build its signed CAR.
const carRev = tidNow()
const winner = [
{ collection: 'app.bsky.feed.post', rkey: 'w1', record: post('winner-one') },
{ collection: 'app.bsky.feed.post', rkey: 'shared', record: post('shared-post') },
{ collection: 'app.bsky.feed.post', rkey: 'w2', record: post('winner-two') }
]
const { carBytes } = await createRepo({ records: winner, did: DID, rev: carRev, privateKey: KEY })
// LOSER (e.g. stortron's 85): shares one post, but has a follow that ONLY landed here (must NOT be lost).
const loser = [
{ collection: 'app.bsky.feed.post', rkey: 'shared', record: post('shared-post') },
{ collection: 'app.bsky.graph.follow', rkey: 'onlyL', record: follow('did:plc:someone') }
]
// FRESH reconcile device (own MemKV = clean line). Adopt the CAR + union the loser-only record.
const dev = new SwarmDevice(new MemKV(), { did: DID, privateKey: KEY, self: 'reconciler', firehose: true })
const parsed = await readAdoptRepo(carBytes)
t.is(parsed.records.length, 3, 'CAR parsed to the 3 winner records')
const r = await dev.reconcileFromCar(parsed, loser)
t.alike({ adopted: r.adopted, unioned: r.unioned, total: r.total }, { adopted: 3, unioned: 1, total: 4 },
'adopted 3 winner records + unioned 1 loser-only (shared post skipped) = 4')
// UNION + NO LOSS: the reconciled line has all 3 winner records AND the loser-only follow.
const finalKeys = (await dev.repo.listRecords()).map((x) => x.collection + '/' + x.rkey).sort()
t.alike(finalKeys, ['app.bsky.feed.post/shared', 'app.bsky.feed.post/w1', 'app.bsky.feed.post/w2', 'app.bsky.graph.follow/onlyL'],
'reconciled set = UNION of winner ∪ loser (nothing dropped)')
t.alike(await dev.repo.getRecord('app.bsky.graph.follow', 'onlyL'), follow('did:plc:someone'), 'the loser-only record survived byte-exact')
t.alike(await dev.repo.getRecord('app.bsky.feed.post', 'w2'), post('winner-two'), 'a winner-only record survived byte-exact')
// FORWARD continuation: the reconciled head is a new commit AFTER the adopted CAR head.
t.ok(r.head && r.head.rev > carRev, `head advanced forward (${carRev} -> ${r.head.rev}) — relay-monotonic`)
t.is((await dev.firehoseLog())[0].ops.length, 1, 'exactly ONE forward op (the unioned follow) — no re-author of the winner set')
})
test('reconcile: loser is a strict SUBSET ⇒ no spurious commit (adopted CAR head is final)', async (t) => {
const carRev = tidNow()
const winner = [
{ collection: 'app.bsky.feed.post', rkey: 'w1', record: post('one') },
{ collection: 'app.bsky.feed.post', rkey: 'w2', record: post('two') }
]
const { carBytes, dataCid } = await createRepo({ records: winner, did: DID, rev: carRev, privateKey: KEY })
const loser = [{ collection: 'app.bsky.feed.post', rkey: 'w1', record: post('one') }] // ⊂ winner
const dev = new SwarmDevice(new MemKV(), { did: DID, privateKey: KEY, self: 'reconciler', firehose: true })
const r = await dev.reconcileFromCar(await readAdoptRepo(carBytes), loser)
t.alike({ adopted: r.adopted, unioned: r.unioned }, { adopted: 2, unioned: 0 }, 'nothing to union (loser ⊆ winner)')
t.is(r.head.dataCid.toString(), dataCid.toString(), 'head is the adopted CAR head unchanged (no forward commit)')
t.is(r.head.rev, carRev, 'rev unchanged — no spurious re-sign')
t.is((await dev.firehoseLog()).length, 0, 'no #commit emitted (adopt sets head without a firehose frame)')
})
test('reconcile: anti-re-author guard — a CAR whose records do NOT reproduce its data root is REJECTED', async (t) => {
const carRev = tidNow()
const { carBytes } = await createRepo({ records: [{ collection: 'app.bsky.feed.post', rkey: 'w1', record: post('one') }], did: DID, rev: carRev, privateKey: KEY })
const parsed = await readAdoptRepo(carBytes)
parsed.records = [] // tamper: claim zero records but keep the non-empty head → would re-author/wipe
const dev = new SwarmDevice(new MemKV(), { did: DID, privateKey: KEY, self: 'reconciler', firehose: true })
await t.exception(() => dev.reconcileFromCar(parsed, []), /do not reproduce the adopted data root/,
'reconcile refuses an incomplete CAR (the anti-re-author guard fires before any write)')
})
// get-repo-car: a device exports its signed repo as a CAR; a PEER reconciles from it (the operational
// heal path — fetch the authoritative node's CAR → reconcileFromCar into a fresh line).
test('exportCar → readAdoptRepo → reconcileFromCar round-trips (the heal fetch path)', async (t) => {
// Authoritative device A: author + sign a repo (its own line).
const A = new SwarmDevice(new MemKV(), { did: DID, privateKey: KEY, self: 'A', firehose: true })
await A.putRecord('app.bsky.feed.post', 'a1', post('one'))
await A.putRecord('app.bsky.feed.post', 'a2', post('two'))
await A.putRecord('app.bsky.actor.profile', 'self', { $type: 'app.bsky.actor.profile', displayName: 'T' })
const headA = await A.sign({ force: true })
const exp = await A.exportCar()
t.is(exp.recordCount, 3, 'exportCar reports the record count')
t.is(exp.dataCid, headA.dataCid.toString(), 'exported CAR data root == the signed head data root (integrity)')
t.is(exp.commitCid, headA.commitCid.toString(), 'exported CAR commit == the signed head commit (uses existing signature)')
// A PEER parses the CAR + reconciles from it into a FRESH line.
const parsed = await readAdoptRepo(exp.carBytes)
t.is(parsed.records.length, 3, 'peer parsed 3 records from the exported CAR')
t.is(parsed.head.dataCid.toString(), headA.dataCid.toString(), 'peer parsed head matches A (round-trip)')
const C = new SwarmDevice(new MemKV(), { did: DID, privateKey: KEY, self: 'C', firehose: true })
const r = await C.reconcileFromCar(parsed, [])
t.is(r.adopted, 3, 'peer adopted all 3 records from the exported CAR')
const keys = (await C.repo.listRecords()).map((x) => x.collection + '/' + x.rkey).sort()
t.alike(keys, ['app.bsky.actor.profile/self', 'app.bsky.feed.post/a1', 'app.bsky.feed.post/a2'], 'peer reproduced A\'s exact record set')
})
test('exportCar refuses when nothing is signed yet', async (t) => {
const D = new SwarmDevice(new MemKV(), { did: DID, privateKey: KEY, self: 'D', firehose: true })
await t.exception(() => D.exportCar(), /no signed head/, 'exportCar throws on an unsigned device (nothing to export)')
})