// The lens: the loser-club bsky custom feed, served by the gateway in place of favela-web (hyper-social #80 G2).
//
// favela-web's rules, kept (feedgen.js): a community's feed is its room members' app.bsky.feed.post records,
// newest createdAt first; ONLY TID rkeys (one bad rkey — loser-club's own 'self.hello' — makes bsky reject
// the whole response); cursor a string or ABSENT, never null. What changes: posts come from the repos the
// membrane already fronts, indexed per member only when that member's head moves — never on bsky's request.
import test from 'brittle'
import { createLens } from '../lib/lens.js'
const ROOM = '91ce4dbcb6b4fe0cfa489582cb9931923c05e0d14cf0a5c939d8334edefe21ed'
const LC = 'did:plc:wqnwulql7ndcz6xmtvaovl6u'
const WJ = 'did:plc:wj62ebbusln6gxo5azo6ogzo'
const E2 = 'did:plc:e2jevhuurrhlm36ufk7fklpg'
const FEED = `at://${LC}/app.bsky.feed.generator/loser-club`
// TIDs in increasing time order.
const T = ['3mr2222222222', '3mr3333333333', '3mr4444444444', '3mr5555555555', '3mr6666666666', '3mr7777777777']
const post = (rkey, createdAt) => ({ uri: 'x/' + rkey, value: { $type: 'app.bsky.feed.post', text: rkey, createdAt } })
function fakeMembrane ({ members = [WJ, E2], records, revs }) {
const listeners = []
const labelListeners = []
const state = { members, records, revs, listCalls: 0, watchers: [] }
const membrane = {
state,
extensions: { xrpc: new Map(), ws: new Map() },
relay: { seq: { core: { on: (ev, fn) => { if (ev === 'append') listeners.push(fn) }, off: () => {} } } },
appendNow: () => { for (const fn of listeners) fn() },
// The router's onLabels: fires when a DID's front first opens a label core (atproto-did-peer).
labelsAppear: (did) => { for (const fn of labelListeners) fn(did, { labels: {} }) },
metrics: () => ({ dids: Object.keys(state.revs).map((did) => ({ did, rev: state.revs[did] })) }),
peers: {
onLabels: (fn) => { labelListeners.push(fn); return () => { const i = labelListeners.indexOf(fn); if (i >= 0) labelListeners.splice(i, 1) } },
isFronted: (did) => did in state.revs,
front: async (did) => ({
listRecords: async (col, { limit = 100, cursor = null } = {}) => {
state.listCalls++
const all = (state.records[did] || []).filter(() => col === 'app.bsky.feed.post')
const start = cursor ? all.findIndex((r) => r.uri === cursor) + 1 : 0
const page = all.slice(start, start + limit)
return { records: page, cursor: start + limit < all.length ? page[page.length - 1].uri : undefined }
}
})
},
communityRooms: async () => ({
members: async () => state.members,
communityDid: async () => LC,
curation: async () => ({ curator: 'roster', params: {} }),
watch: async (roomKey, onChange) => { state.watchers.push(onChange); await onChange(state.members); return async () => {} }
})
}
return membrane
}
const baseRecords = () => ({
[WJ]: [post(T[0], '2026-09-01T00:00:00Z'), post(T[3], '2026-09-04T00:00:00Z'), post('self.hello', '2026-09-09T00:00:00Z')],
[E2]: [post(T[1], '2026-09-02T00:00:00Z'), post(T[4], '2026-09-05T00:00:00Z')]
})
async function started (opts = {}) {
const m = fakeMembrane({ records: baseRecords(), revs: { [WJ]: 'r1', [E2]: 'r1', [LC]: 'r1' }, ...opts })
const lens = createLens({ membrane: m, feeds: [{ id: 'loser-club', roomKey: ROOM }], log: () => {}, debounceMs: 5 })
await lens.start()
return { m, lens }
}
const skeleton = (m, q) => m.extensions.xrpc.get('app.bsky.feed.getFeedSkeleton')({ query: new URLSearchParams(q) })
test('★ the skeleton: members\' posts, newest createdAt first, TID rkeys only', async (t) => {
const { m, lens } = await started()
t.teardown(() => lens.stop())
const r = await skeleton(m, { feed: FEED })
t.is(r.status, 200)
t.alike(r.body.feed.map((f) => f.post), [
`at://${E2}/app.bsky.feed.post/${T[4]}`,
`at://${WJ}/app.bsky.feed.post/${T[3]}`,
`at://${E2}/app.bsky.feed.post/${T[1]}`,
`at://${WJ}/app.bsky.feed.post/${T[0]}`
], 'self.hello is not a TID, so it is not in the feed')
t.absent('cursor' in r.body, 'exhausted: cursor ABSENT, never null')
})
test('★ pagination: pages concatenate to the whole feed, and only the last lacks a cursor', async (t) => {
const { m, lens } = await started()
t.teardown(() => lens.stop())
const p1 = (await skeleton(m, { feed: FEED, limit: '3' })).body
t.is(p1.feed.length, 3)
t.is(typeof p1.cursor, 'string', 'a string cursor while more remain')
const p2 = (await skeleton(m, { feed: FEED, limit: '3', cursor: p1.cursor })).body
t.is(p2.feed.length, 1)
t.absent('cursor' in p2)
t.ok(!/\u0000/.test(p1.cursor), 'the cursor is opaque text (favela-web\'s carried NUL bytes)')
})
test('limit: default 50, at most 100', async (t) => {
const { m, lens } = await started()
t.teardown(() => lens.stop())
t.is((await skeleton(m, { feed: FEED, limit: '1000' })).body.feed.length, 4, 'clamped, never an error')
})
test('⛔ an unknown feed is UnsupportedAlgorithm (400), as the feed-generator contract says', async (t) => {
const { m, lens } = await started()
t.teardown(() => lens.stop())
const r = await skeleton(m, { feed: `at://${LC}/app.bsky.feed.generator/nope` })
t.is(r.status, 400)
t.is(r.body.error, 'UnsupportedAlgorithm')
})
test('describeFeedGenerator names the community DID (the lexicon requires `did`)', async (t) => {
const { m, lens } = await started()
t.teardown(() => lens.stop())
const r = await m.extensions.xrpc.get('app.bsky.feed.describeFeedGenerator')({ query: new URLSearchParams() })
t.alike(r.body, { did: LC, feeds: [{ uri: FEED }] })
})
test('⛔ requests never rebuild the index; a member whose head MOVED is re-read on the next relay append', async (t) => {
const { m, lens } = await started()
t.teardown(() => lens.stop())
const before = m.state.listCalls
await skeleton(m, { feed: FEED }); await skeleton(m, { feed: FEED })
t.is(m.state.listCalls, before, 'two skeleton requests, zero repo reads')
m.state.records[WJ].push(post(T[5], '2026-09-06T00:00:00Z'))
m.appendNow() // an append whose head did not move for anyone: nothing to re-read
await new Promise((resolve) => setTimeout(resolve, 40))
t.is(m.state.listCalls, before, 'no head moved: no re-read')
m.state.revs[WJ] = 'r2'
m.appendNow()
await new Promise((resolve) => setTimeout(resolve, 60))
t.ok(m.state.listCalls > before, 'wj62\'s head moved: re-read')
t.is((await skeleton(m, { feed: FEED })).body.feed[0].post, `at://${WJ}/app.bsky.feed.post/${T[5]}`, 'and its new post leads the feed')
})
test('a member removed from the room leaves the feed', async (t) => {
const { m, lens } = await started()
t.teardown(() => lens.stop())
m.state.members = [WJ]
for (const w of m.state.watchers) await w([WJ])
const r = (await skeleton(m, { feed: FEED })).body
t.ok(r.feed.every((f) => f.post.includes(WJ)), 'only wj62 remains')
})
test('a member the membrane does not front is reported, not silently missing', async (t) => {
const { m, lens } = await started({ members: [WJ, E2, 'did:plc:stranger'] })
t.teardown(() => lens.stop())
const x = lens.metrics()
t.alike(x.feeds[0].unfronted, ['did:plc:stranger'])
t.is(x.feeds[0].members, 3)
t.is(x.feeds[0].posts, 4, 'indexed posts for the fronted members')
t.is(x.feeds[0].communityDid, LC)
t.is(x.feeds[0].curation, 'roster')
})
test('labels: queryLabels answers an empty, valid set; subscribeLabels is served', async (t) => {
const { m, lens } = await started()
t.teardown(() => lens.stop())
const r = await m.extensions.xrpc.get('com.atproto.label.queryLabels')({ query: new URLSearchParams({ uriPatterns: '*' }) })
t.alike(r.body, { labels: [] })
t.ok(m.extensions.ws.has('/xrpc/com.atproto.label.subscribeLabels'))
})
test('stop removes every registered method — a stopped lens serves nothing', async (t) => {
const { m, lens } = await started()
await lens.stop()
t.is(m.extensions.xrpc.size, 0)
t.is(m.extensions.ws.size, 0)
})
// ── labels (member labels, #80 step 4): the lens hosts the label surfaces and drives the projector ───────
function fakeLabels () {
const s = { watched: [], started: 0, stopped: 0 }
return {
s,
start: async () => { s.started++ },
stop: async () => { s.stopped++ },
watch: (did, front) => { s.watched.push({ did, front }) },
metrics: () => ({ kept: 3 }),
xrpc: { 'com.atproto.label.queryLabels': async () => ({ status: 200, body: { labels: ['real'] } }) },
ws: { '/xrpc/com.atproto.label.subscribeLabels': () => {} }
}
}
function fakeProjector () {
const applied = []
return { applied, apply: async (f) => { applied.push(f); return {} }, metrics: () => ({ feeds: { 'loser-club': { added: 2 } } }), close: async () => {} }
}
test('★ with labels injected: the real queryLabels is served, the community DID\'s front is watched', async (t) => {
const m = fakeMembrane({ records: baseRecords(), revs: { [WJ]: 'r1', [E2]: 'r1', [LC]: 'r1' } })
const labels = fakeLabels()
const lens = createLens({ membrane: m, feeds: [{ id: 'loser-club', roomKey: ROOM }], log: () => {}, debounceMs: 5, labels })
await lens.start()
t.teardown(() => lens.stop())
t.is(labels.s.started, 1)
const r = await m.extensions.xrpc.get('com.atproto.label.queryLabels')({ query: new URLSearchParams() })
t.alike(r.body.labels, ['real'], 'the labels module answers, not the empty placeholder')
await new Promise((resolve) => setTimeout(resolve, 20))
t.ok(labels.s.watched.some((w) => w.did === LC && w.front), 'the community DID\'s front is handed to the labels module')
t.alike(lens.metrics().labels, { kept: 3 })
})
test('★ with a projector: every roster change is applied, with the community DID', async (t) => {
const m = fakeMembrane({ records: baseRecords(), revs: { [WJ]: 'r1', [E2]: 'r1', [LC]: 'r1' } })
const memberLabels = fakeProjector()
const lens = createLens({ membrane: m, feeds: [{ id: 'loser-club', roomKey: ROOM }], log: () => {}, debounceMs: 5, memberLabels })
await lens.start()
t.teardown(() => lens.stop())
t.alike(memberLabels.applied[0], { id: 'loser-club', communityDid: LC, members: [WJ, E2] })
await m.state.watchers[0]([WJ])
t.alike(memberLabels.applied[memberLabels.applied.length - 1].members, [WJ], 'a member leaving is applied too')
t.alike(lens.metrics().memberLabels, { feeds: { 'loser-club': { added: 2 } } })
})
// ⛔ MEASURED 2026-10-14 on stortron: the projector's only run was at boot, while loser-club's hpp was not yet a
// labeler ("this DID is not a labeler"). hpp was restarted with `labeler: true` and began announcing a label
// core, and nothing re-ran — the roster had not changed, so the badges never appeared. A labeler coming online
// IS the event: the router's onLabels fires when the community DID's front first opens a label core.
test('⛔ the community DID gaining a label core re-runs the projector; another DID does not', async (t) => {
const m = fakeMembrane({ records: baseRecords(), revs: { [WJ]: 'r1', [E2]: 'r1', [LC]: 'r1' } })
const memberLabels = fakeProjector()
const lens = createLens({ membrane: m, feeds: [{ id: 'loser-club', roomKey: ROOM }], log: () => {}, debounceMs: 5, memberLabels })
await lens.start()
t.teardown(() => lens.stop())
const before = memberLabels.applied.length
t.is(before, 1, 'the boot run')
m.labelsAppear(WJ)
t.is(memberLabels.applied.length, before, 'a member DID\'s label core is not the labeler coming online')
m.labelsAppear(LC)
t.is(memberLabels.applied.length, before + 1, 'the community DID\'s label core re-runs the projector')
t.alike(memberLabels.applied[before], { id: 'loser-club', communityDid: LC, members: [WJ, E2] }, 'with the current roster')
})
test('stop unsubscribes from onLabels — a stopped lens projects nothing', async (t) => {
const m = fakeMembrane({ records: baseRecords(), revs: { [WJ]: 'r1', [E2]: 'r1', [LC]: 'r1' } })
const memberLabels = fakeProjector()
const lens = createLens({ membrane: m, feeds: [{ id: 'loser-club', roomKey: ROOM }], log: () => {}, debounceMs: 5, memberLabels })
await lens.start()
await lens.stop()
const n = memberLabels.applied.length
m.labelsAppear(LC)
t.is(memberLabels.applied.length, n)
})