// Restart continuity: after a daemon restart a device re-opens the shared state with head=null and
// _lastRecords=[]. Without restore(), the next sign diffs against an EMPTY baseline → re-emits the
// FULL record set as creates (the ops=[9,..,12] firehose bug that broke relay/AppView MST continuity).
// restore() re-seeds head + baseline from the shared __commit__ log so the firehose stays INCREMENTAL.
// Deterministic (network:false).
import '../lib/bare-globals.js'
import test from 'brittle'
import crypto from 'hypercore-crypto'
import fs from 'bare-fs'
import b4a from 'b4a'
import { StateBaseV3 } from 'atproto-room/lib/state-base.js'
import { SwarmDevice } from '../lib/swarm-device.js'
import { makeAttest, makeVerifyAttest, attestDeviceChain, deviceKeetKeyPair, rootIdentityPublicKey } from 'atproto-room/lib/membership.js'
import { deriveAtprotoKey } from 'atproto-identity'
const SIGN_SRC = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about'
const KEET_ROOT = 'legal winner thank year wave sausage worth useful legal winner thank yellow'
const DEV = 'zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong'
const DID = 'did:plc:restore22222222222222222'
const POST = 'app.bsky.feed.post'
const signKey = deriveAtprotoKey(SIGN_SRC)
const tmp = () => { const d = '/tmp/hpp-restore-' + Math.floor(Math.random() * 1e9); fs.mkdirSync(d, { recursive: true }); return d }
async function mkDevice (dir) {
const rootPub = await rootIdentityPublicKey(KEET_ROOT)
const dev = await deviceKeetKeyPair(DEV)
const proof = await attestDeviceChain({ rootMnemonic: KEET_ROOT, devicePublicKey: dev.publicKey })
const members = [{ key: b4a.toString(dev.publicKey, 'hex'), role: 1 }]
const A = new StateBaseV3(dir, crypto.hash(b4a.from(DID)), {
roomSeed: crypto.hash(b4a.from('restore-seed')), encryptionKey: crypto.hash(b4a.from('restore-enc')), network: false,
selfKey: dev.publicKey, attest: makeAttest(dev, proof), verifyAttest: makeVerifyAttest(rootPub), getMembers: async () => members
})
await A.ready(); await A.projectMembers(members)
const device = new SwarmDevice(A, { did: DID, privateKey: signKey.privateKey, self: b4a.toString(dev.publicKey, 'hex'), firehose: true })
return { A, device }
}
test('restart restore: the firehose stays INCREMENTAL (no full-set re-author)', async (t) => {
const dir = tmp()
// 1. boot, write 2 records over 2 signs → 2 incremental #commit frames
const one = await mkDevice(dir)
await one.device.claimLease()
await one.device.putRecord(POST, '3aaawu7vak2001', { $type: POST, text: 'a', createdAt: '2026-07-05T00:00:00.000Z' })
await one.device.sign()
await one.device.putRecord(POST, '3bbbwu7vak2002', { $type: POST, text: 'b', createdAt: '2026-07-05T00:00:01.000Z' })
const h2 = await one.device.sign()
const log1 = await one.device.firehoseLog()
t.is(log1.length, 2, 'two #commit frames')
t.is(log1[1].ops.length, 1, 'second frame is incremental (1 op, not the full set)')
await one.A.close()
// 2. "restart": a FRESH SwarmDevice on the SAME state — head=null, _lastRecords=[]. bootSwarmDevice
// runs restore() at boot BEFORE the control surface accepts any writes, so mirror that ordering here.
const two = await mkDevice(dir)
t.teardown(async () => { try { await two.A.close() } catch {} })
t.absent(two.device.head, 'fresh instance starts with no in-memory head')
const restored = await two.device.restore()
t.ok(restored && restored.commitCid.equals(h2.commitCid), 'restore() re-seeds head to the last signed commit')
await two.device.claimLease()
// 3. add ONE record + sign — must be INCREMENTAL (1 op), not re-author all 3
await two.device.putRecord(POST, '3cccwu7vak2003', { $type: POST, text: 'c', createdAt: '2026-07-05T00:00:02.000Z' })
const h3 = await two.device.sign()
t.ok(h3, 'restarted device signed a continuing commit')
const log2 = await two.device.firehoseLog()
t.is(log2.length, 3, 'exactly ONE new frame appended (no full-set re-author)')
t.is(log2[2].ops.length, 1, 'the post-restart frame is INCREMENTAL (1 op, not 3)')
t.is(log2[2].prev, h2.commitCid.toString(), 'the new frame chains from the RESTORED head (prev = last signed commit)')
})