OverviewFilesCommitsIssuesDocsReleasesPeers

atproto-did-peer commits / c090366

0.2.12: fix the four tests 0.2.10 left pinned to the old listRecords shape
0.2.10 changed listRecords from a bare array to the lexicon's { records, cursor } and updated three tests that pinned the old shape. It missed five more call sites, and the suite has been red since 2026-07-31. NOT a behavioural regression, which is what I assumed when I filed this. Every failure is `.length` read off the wrapper — `undefined === 0` — on code paths whose behaviour never changed. The noRepo contract in particular is exactly as it was: zero records, because they live in the consumer's shared OverlayRepoStore and not in a repo core the peer never opened. Fixed: no-repo, fork, coowner-set, evict, co-owned, and the read-live probe. no-repo now asserts the SHAPE explicitly (records is an array, cursor absent) so the next contract change fails on the contract rather than on arithmetic against undefined. Whole suite green: 9/9 including get-repo-since. The same shape change also left three LATENT callers wrong in favela-web (roster.js, p2p-handler.js, appview.js's non-store baseline) — fixed there in 0.3.49. None was live-triggered: FAVELA_LIST/MODLISTS are unset on the fleet, p2p-handler is spike-only, and the appview branch is unreachable while every DID is noRepo. Real bugs, no outage. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
c09036670b71f4736d9d5dc2f81a6900b57c9102
by plankatron <fleeky@gmail.com> · 2026-08-07T19:25:34.000Z

Changed files (7) +18 -7

package.json +1 -1
……@@ -1,6 +1,6 @@
11{
22 "name": "atproto-did-peer",
3 "version": "0.2.11",
3 "version": "0.2.12",
44 "description": "Keyless, direction-free P2P reader/seeder for an atproto DID's repo+firehose Hypercores: announce-push discovery + event-driven head reconciliation + a keyless read surface. The shared primitive beneath atproto-bridge, atproto-relay and favela (extracted from atproto-bridge did-peer.js).",
55 "type": "module",
66 "main": "index.js",
test/co-owned.test.mjs +2 -1
……@@ -69,7 +69,8 @@ await swarm.join(discoveryTopic(DID), { server: false, client: true }).flushed()
6969const counts = []
7070for (let i = 0; i < 40; i++) {
7171 const f = await router.front(DID).catch(() => null)
72 const recs = f ? await f.listRecords('app.bsky.feed.post').catch(() => []) : []
72 // listRecords returns { records, cursor } per the lexicon (0.2.10) — NOT a bare array.
73 const recs = f ? ((await f.listRecords('app.bsky.feed.post').catch(() => null)) || {}).records || [] : []
7374 counts.push(recs.length)
7475 await new Promise((r) => setTimeout(r, 300))
7576}
test/coowner-set.test.mjs +2 -1
……@@ -80,7 +80,8 @@ await swarm.join(discoveryTopic(DID), { server: false, client: true }).flushed()
8080const counts = []
8181for (let i = 0; i < 40; i++) {
8282 const f = await router.front(DID).catch(() => null)
83 const recs = f ? await f.listRecords('app.bsky.feed.post').catch(() => []) : []
83 // listRecords returns { records, cursor } per the lexicon (0.2.10) — NOT a bare array.
84 const recs = f ? ((await f.listRecords('app.bsky.feed.post').catch(() => null)) || {}).records || [] : []
8485 counts.push(recs.length)
8586 await new Promise((r) => setTimeout(r, 300))
8687}
test/evict.test.mjs +2 -1
……@@ -79,7 +79,8 @@ function reader (opts = {}) {
7979async function served (router) {
8080 const f = await router.front(DID).catch(() => null)
8181 if (!f) return -1
82 return (await f.listRecords('app.bsky.feed.post').catch(() => [])).length
82 // listRecords returns { records, cursor } per the lexicon (0.2.10) — NOT a bare array.
83 return (((await f.listRecords('app.bsky.feed.post').catch(() => null)) || {}).records || []).length
8384}
8485async function waitServed (router, want, ms = 15000) {
8586 const end = Date.now() + ms
test/fork.test.mjs +1 -1
……@@ -109,7 +109,7 @@ const router = new DidPeerRouter(store, {
109109toClose.push(async () => { try { await swarm.destroy() } catch {}; try { await router.close() } catch {}; try { await store.close() } catch {} })
110110
111111const servedRev = async () => { const f = await router.front(DID).catch(() => null); const lc = f && f.getLatestCommit(); return lc ? lc.rev : null }
112const servedRecs = async () => { const f = await router.front(DID).catch(() => null); return f ? (await f.listRecords('app.bsky.feed.post').catch(() => [])).length : -1 }
112const servedRecs = async () => { const f = await router.front(DID).catch(() => null); return f ? (((await f.listRecords('app.bsky.feed.post').catch(() => null)) || {}).records || []).length : -1 }
113113async function waitRev (want, ms = 20000) { const end = Date.now() + ms; let last = null; while (Date.now() < end) { last = await servedRev(); if (last === want) return last; await sleep(250) } return last }
114114
115115// 1. Same line: the fresher co-owner wins, continuity permits it (its frames CONTAIN the lagging head).
test/no-repo.test.mjs +9 -1
……@@ -46,7 +46,15 @@ const head = await reader.ready({ timeout: 20000 })
4646assert(!!head, 'noRepo reader reached a VERIFIED head via the firehose (no repo core)')
4747assert(reader.repo === null, 'repo core was NEVER opened (this.repo === null) — no repo-core replica')
4848assert(reader.seq && reader.seq.core.length >= 1, 'firehose core IS opened + replicated')
49assert((await reader.listRecords('app.bsky.feed.post')).length === 0, 'listRecords returns [] (records come from the shared store, not the repo core)')
49// ⛔ THE SHAPE, NOT THE EMPTINESS, IS WHAT BROKE HERE. 0.2.10 made listRecords return { records, cursor }
50// per the lexicon instead of a bare array; this assertion still read `.length` off the wrapper, so it was
51// comparing `undefined === 0` and failing on a code path that had never changed behaviour. The noRepo
52// contract below is exactly as it always was — zero records, because they live in the consumer's shared
53// OverlayRepoStore and not in a repo core this peer never opened.
54const listed = await reader.listRecords('app.bsky.feed.post')
55assert(Array.isArray(listed.records), 'listRecords returns the lexicon shape { records, cursor }')
56assert(listed.records.length === 0, 'and noRepo yields NO records (they come from the shared store, not a repo core)')
57assert(listed.cursor === undefined, 'with no cursor — its ABSENCE is how a pager knows to stop')
5058assert((await reader.getRecord('app.bsky.feed.post', '1')) === null, 'getRecord returns null in noRepo mode')
5159
5260console.log('\nNO-REPO OK: firehose-only consumer verifies the head + follows the tail with NO repo-core replica')
test/read-live.mjs +1 -1
……@@ -42,7 +42,7 @@ console.log('VERIFIED HEAD:', { rev: head.rev, cid: head.cid.slice(0, 24) + '…
4242console.log('collections:', await peer.collections())
4343const rec = await peer.getRecord('app.bsky.feed.post', KNOWN_RKEY)
4444console.log('getRecord ->', JSON.stringify(rec?.value?.text || rec).slice(0, 66))
45const list = await peer.listRecords('app.bsky.feed.post')
45const list = (await peer.listRecords('app.bsky.feed.post')).records
4646console.log('listRecords ->', list.length, 'posts')
4747const { carBytes } = await peer.getRepo()
4848console.log('getRepo ->', carBytes.length, 'CAR bytes (keyless, DID-key-verified)')