feat(mesh): Phase 831 quad presence fanout journey
Add runGuildMeshQuadPresenceJourney so host idle presence propagates to all three guests on a 4-peer mesh, plus meshQuadPresenceReady matcher. Co-authored-by: Cursor <[email protected]>
This commit is contained in:
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
Agent control plane for Pearcord — send the same IPC messages the UI sends, read `view` snapshots, and run scripted journeys against a **live worker** or an in-process **headless** session.
|
Agent control plane for Pearcord — send the same IPC messages the UI sends, read `view` snapshots, and run scripted journeys against a **live worker** or an in-process **headless** session.
|
||||||
|
|
||||||
|
**Phase 831 (v0.8.798):** `runGuildMeshQuadPresenceJourney` (4-peer presence fanout). Bundle: `npm run test:ci-phase831`.
|
||||||
|
|
||||||
**Phase 830 (v0.8.797):** `runGuildMeshQuadRecoveryBurstJourney` (4-peer composite recovery burst). Bundle: `npm run test:ci-phase830`.
|
**Phase 830 (v0.8.797):** `runGuildMeshQuadRecoveryBurstJourney` (4-peer composite recovery burst). Bundle: `npm run test:ci-phase830`.
|
||||||
|
|
||||||
**Phase 829 (v0.8.796):** `runGuildMeshQuadChurnRejoinJourney` (4-peer single-peer churn rejoin). Bundle: `npm run test:ci-phase829`.
|
**Phase 829 (v0.8.796):** `runGuildMeshQuadChurnRejoinJourney` (4-peer single-peer churn rejoin). Bundle: `npm run test:ci-phase829`.
|
||||||
|
|||||||
@@ -143,6 +143,16 @@ function matchView (view, spec) {
|
|||||||
if (outbox > 0) return false
|
if (outbox > 0) return false
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if (key === 'meshQuadPresenceReady') {
|
||||||
|
if (!expected) return true
|
||||||
|
const peers = Number(view?.stats?.peers) || 0
|
||||||
|
if (peers < 2) return false
|
||||||
|
const gid = view?.guild?.id
|
||||||
|
const rail = gid ? view.guildSyncHealthRail?.[gid] : null
|
||||||
|
if (rail?.meshBounded) return false
|
||||||
|
if ((view?.presence?.peers || []).length < 1) return false
|
||||||
|
continue
|
||||||
|
}
|
||||||
if (key === 'presencePeersMin') {
|
if (key === 'presencePeersMin') {
|
||||||
if ((view?.presence?.peers || []).length < Number(expected)) return false
|
if ((view?.presence?.peers || []).length < Number(expected)) return false
|
||||||
continue
|
continue
|
||||||
|
|||||||
+79
@@ -7270,6 +7270,85 @@ class HeadlessSession {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Phase 831: 4-peer mesh — host idle presence visible on all three guests. */
|
||||||
|
async runGuildMeshQuadPresenceJourney () {
|
||||||
|
const { connectGuildMeshQuad } = require('./mesh-helpers')
|
||||||
|
const base = path.join(os.tmpdir(), `pearcord-agentctl-presence831-${Date.now()}`)
|
||||||
|
const host = new HeadlessSession({ storagePath: path.join(base, 'host') })
|
||||||
|
const guest1 = new HeadlessSession({ storagePath: path.join(base, 'guest1') })
|
||||||
|
const guest2 = new HeadlessSession({ storagePath: path.join(base, 'guest2') })
|
||||||
|
const guest3 = new HeadlessSession({ storagePath: path.join(base, 'guest3') })
|
||||||
|
await host.start()
|
||||||
|
await guest1.start()
|
||||||
|
await guest2.start()
|
||||||
|
await guest3.start()
|
||||||
|
try {
|
||||||
|
await host.registerUser({ username: 'meshhost831', displayName: 'Mesh Host 831' })
|
||||||
|
await host.createGuild({ name: 'Presence Quad 831', publicListing: false })
|
||||||
|
await host.wait({ mode: 'guild' }, 45000)
|
||||||
|
const invite = await host.platform.createInvite().catch(() => null)
|
||||||
|
const code = invite?.shareCode || invite?.code
|
||||||
|
if (!code) throw new Error('host invite missing')
|
||||||
|
await guest1.registerUser({ username: 'meshguest831a', displayName: 'Mesh Guest 831a' })
|
||||||
|
await guest2.registerUser({ username: 'meshguest831b', displayName: 'Mesh Guest 831b' })
|
||||||
|
await guest3.registerUser({ username: 'meshguest831c', displayName: 'Mesh Guest 831c' })
|
||||||
|
await guest1.ipc({ type: 'join-invite', code })
|
||||||
|
await guest2.ipc({ type: 'join-invite', code })
|
||||||
|
await guest3.ipc({ type: 'join-invite', code })
|
||||||
|
await guest1.wait({ mode: 'guild' }, 45000)
|
||||||
|
await guest2.wait({ mode: 'guild' }, 45000)
|
||||||
|
await guest3.wait({ mode: 'guild' }, 45000)
|
||||||
|
await connectGuildMeshQuad(
|
||||||
|
host.platform,
|
||||||
|
guest1.platform,
|
||||||
|
guest2.platform,
|
||||||
|
guest3.platform,
|
||||||
|
55000
|
||||||
|
)
|
||||||
|
const quadWait = { meshQuadReady: true, membersMin: 4 }
|
||||||
|
await host.wait(quadWait, 30000)
|
||||||
|
await guest1.wait(quadWait, 30000)
|
||||||
|
await guest2.wait(quadWait, 30000)
|
||||||
|
await guest3.wait(quadWait, 30000)
|
||||||
|
await new Promise((r) => setTimeout(r, 3000))
|
||||||
|
const hv = await host.refreshView()
|
||||||
|
const hostUserId = hv.user?.id
|
||||||
|
if (!hostUserId) throw new Error('host user id missing')
|
||||||
|
await host.ipc({ type: 'set-presence', status: 'idle' })
|
||||||
|
await new Promise((r) => setTimeout(r, 1500))
|
||||||
|
for (const p of [host.platform, guest1.platform, guest2.platform, guest3.platform]) {
|
||||||
|
if (typeof p.requestGuildSyncFanout === 'function') {
|
||||||
|
p.requestGuildSyncFanout()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const presWait = { peerPresenceStatus: { userId: hostUserId, status: 'idle' } }
|
||||||
|
await guest1.wait(presWait, 90000)
|
||||||
|
await guest2.wait(presWait, 90000)
|
||||||
|
await guest3.wait(presWait, 90000)
|
||||||
|
const g1v = await guest1.refreshView()
|
||||||
|
const g2v = await guest2.refreshView()
|
||||||
|
const g3v = await guest3.refreshView()
|
||||||
|
const hit = (v) =>
|
||||||
|
(v.presence?.peers || []).some(
|
||||||
|
(p) => p.userId === hostUserId && p.status === 'idle'
|
||||||
|
)
|
||||||
|
if (!hit(g1v) || !hit(g2v) || !hit(g3v)) {
|
||||||
|
throw new Error('guest missing host idle presence on quad mesh')
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
hostUserId,
|
||||||
|
guest1PresencePeers: (g1v.presence?.peers || []).length,
|
||||||
|
guest2PresencePeers: (g2v.presence?.peers || []).length,
|
||||||
|
guest3PresencePeers: (g3v.presence?.peers || []).length
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
await host.close()
|
||||||
|
await guest1.close()
|
||||||
|
await guest2.close()
|
||||||
|
await guest3.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Phase 829: 4-peer mesh — drop one guest, survivors stay synced, rejoin + recovery burst. */
|
/** Phase 829: 4-peer mesh — drop one guest, survivors stay synced, rejoin + recovery burst. */
|
||||||
async runGuildMeshQuadChurnRejoinJourney () {
|
async runGuildMeshQuadChurnRejoinJourney () {
|
||||||
const {
|
const {
|
||||||
|
|||||||
Reference in New Issue
Block a user