// Regression guard for the org-join content-sync fix (diagnosed 2026-07-17).
//
// SYMPTOM (before the fix): after `pgh org join`, a joiner got the repo's REFS
// (the per-repo tracker autobase replicates) but ZERO objects — git-repos/
// <name>.git was `git init --bare`'d and never populated (confirmed on two
// joiners, ashatron + celltron). The git OBJECTS ride a SEPARATE punch:// PACK
// clone (hyper-sync RepoPuller), and that only ever booted for a device-join
// MIRROR — never for an org join.
//
// FIX: mountOrgGroupedRepos now runs the #16 bidirectional loop over the ORG
// autobase — it ANNOUNCES this daemon's origin coords (writers) and boots a
// SCOPED reverse RepoPuller toward each co-member that announced coords. That
// reverse puller does the git PACK clone (`git fetch punch://<peer>/<repo>`),
// filtered to this org's repos (#35 isolation). This test pins that fan-out.
import test from 'brittle'
import { mountOrgGroupedRepos } from '../lib/services/org-grouped-repos.js'
test('org-join mounts trackers AND boots a reverse puller per announced co-member', async (t) => {
const org = {
name: 'house',
resources: ['electrical-review', 'other_accounting'],
resourceKeys: {
'electrical-review': 'a'.repeat(64),
'other_accounting': 'b'.repeat(64)
}
}
const ensured = []
const announced = []
const pullers = []
// Two co-members that announced their origin coords into the org autobase.
const peerCoords = [
{ serverKey: 'c'.repeat(64), repoEventLog: 'd'.repeat(64) },
{ serverKey: 'e'.repeat(64), repoEventLog: 'f'.repeat(64) }
]
const deps = {
getOrg: async () => org,
activateRoom: () => {},
refReconcilers: { ensureFor: (n) => { ensured.push(n); return Promise.resolve() } },
issueRegistry: {
bindRepoBootstrapKey: () => {},
getForRepo: async (n) => ({ discoveryKey: 'dk:' + n })
},
// The three deps that carry the git-OBJECT clone (wired daemon-side in
// handlers/orgs.js; injected as spies here).
announceCoords: async (name) => { announced.push(name) },
listPeerCoords: async () => peerCoords,
bootReversePuller: (serverKey, repoEventLog) => { pullers.push({ serverKey, repoEventLog }) }
}
const mounted = await mountOrgGroupedRepos('house', deps)
// Tracker/refs path unchanged.
t.alike([...mounted].sort(), ['electrical-review', 'other_accounting'], 'both resource repos mounted')
t.alike(ensured.sort(), ['electrical-review', 'other_accounting'], 'gip/RefReconciler path ran (refs)')
// The daemon announces its own coords so co-members can pull FROM it.
t.alike(announced, ['house'], 'announced our origin coords into the org autobase')
// A reverse puller boots toward EACH announced co-member — the git-OBJECT
// clone (punch:// PACK) that was missing for org joins.
t.is(pullers.length, 2, 'booted a reverse puller per co-member')
t.alike(pullers.map((p) => p.serverKey).sort(), ['c'.repeat(64), 'e'.repeat(64)], 'toward both co-members')
t.is(pullers[0].repoEventLog, 'd'.repeat(64), 'puller carries the peer repoEventLog key')
})
// A name-only org (no announced peers, no coords deps) must still work — the
// announce/puller halves are all optional and no-op when their deps are absent.
test('org-join with no peer-coord deps still mounts trackers (backward compatible)', async (t) => {
const org = { name: 'solo', resources: ['r1'], resourceKeys: { r1: 'a'.repeat(64) } }
const ensured = []
const mounted = await mountOrgGroupedRepos('solo', {
getOrg: async () => org,
activateRoom: () => {},
refReconcilers: { ensureFor: (n) => { ensured.push(n); return Promise.resolve() } },
issueRegistry: { bindRepoBootstrapKey: () => {}, getForRepo: async (n) => ({ discoveryKey: 'dk:' + n }) }
})
t.alike([...mounted], ['r1'], 'repo mounted without any coord deps')
t.alike(ensured, ['r1'], 'refs path ran')
})