Phase 825: guild 3-peer composite recovery burst journey (v0.8.792)

Add runGuildMeshTripleRecoveryBurstJourney with disconnect/reconnect,
offline recovery + sync burst on all peers, and meshRecoveryBurstReady matcher.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Raven Scott
2026-06-04 03:26:38 -04:00
co-authored by Cursor
parent ff3052d26b
commit efe0151a35
3 changed files with 111 additions and 0 deletions
+2
View File
@@ -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.
**Phase 825 (v0.8.792):** `runGuildMeshTripleRecoveryBurstJourney` (3-peer composite recovery burst). Bundle: `npm run test:ci-phase825`.
**Phase 824 (v0.8.791):** `runGuildMeshTripleSyncBurstJourney` (3-peer sync burst). Bundle: `npm run test:ci-phase824`.
**Phase 823 (v0.8.790):** `runGuildMeshTriplePushHaltClearJourney` (3-peer push-halt clear). Bundle: `npm run test:ci-phase823`.
+12
View File
@@ -81,6 +81,18 @@ function matchView (view, spec) {
if (peers < 1) return false
continue
}
if (key === 'meshRecoveryBurstReady') {
if (!expected) return true
if (view?.meshStabilityStats?.pushHalted) return false
const peers = Number(view?.stats?.peers) || 0
if (peers < 1) return false
const gid = view?.guild?.id
const rail = gid ? view.guildSyncHealthRail?.[gid] : null
if (rail?.meshBounded) return false
const outbox = Number(view?.guildSyncHealth?.pendingGossipCount) || 0
if (outbox > 0) return false
continue
}
if (key === 'presencePeersMin') {
if ((view?.presence?.peers || []).length < Number(expected)) return false
continue
+97
View File
@@ -7056,6 +7056,103 @@ class HeadlessSession {
}
}
/** Phase 825: 3-peer mesh — disconnect/reconnect then offline recovery + sync burst on all peers. */
async runGuildMeshTripleRecoveryBurstJourney () {
const {
connectGuildMeshTriple,
disconnectGuildMeshTriple
} = require('./mesh-helpers')
const token = `recovery825-${Date.now()}`
const base = path.join(os.tmpdir(), `pearcord-agentctl-recovery825-${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') })
await host.start()
await guest1.start()
await guest2.start()
try {
await host.registerUser({ username: 'rechost825', displayName: 'Recovery Host 825' })
await host.createGuild({ name: 'Recovery Burst Triple 825', 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: 'recguest825a', displayName: 'Recovery Guest 825a' })
await guest2.registerUser({ username: 'recguest825b', displayName: 'Recovery Guest 825b' })
await guest1.ipc({ type: 'join-invite', code })
await guest2.ipc({ type: 'join-invite', code })
await guest1.wait({ mode: 'guild' }, 45000)
await guest2.wait({ mode: 'guild' }, 45000)
const chId = (await host.refreshView()).activeChannelId
if (!chId) throw new Error('active channel missing')
await guest1.ipc({ type: 'select-channel', channelId: chId })
await guest2.ipc({ type: 'select-channel', channelId: chId })
await connectGuildMeshTriple(
host.platform,
guest1.platform,
guest2.platform,
50000
)
await host.wait({ meshPeersMin: 1 }, 30000)
await guest1.wait({ meshPeersMin: 1 }, 30000)
await guest2.wait({ meshPeersMin: 1 }, 30000)
await host.sendGuildMessage(token)
const msgWait = { activeChannelMessageIncludes: token }
await guest1.wait(msgWait, 90000)
await guest2.wait(msgWait, 90000)
await disconnectGuildMeshTriple(host.platform, guest1.platform, guest2.platform, 20000)
await guest1.wait({ meshPeersExact: 0 }, 30000)
await guest2.wait({ meshPeersExact: 0 }, 30000)
await connectGuildMeshTriple(
host.platform,
guest1.platform,
guest2.platform,
50000
)
for (const sess of [host, guest1, guest2]) {
const gid = sess.platform.guild?.guild?.id
if (gid && typeof sess.platform._refreshGuildSyncHealthCounts === 'function') {
await sess.platform._refreshGuildSyncHealthCounts(gid).catch(() => null)
}
if (typeof sess.platform.runOfflineRecoverySync === 'function') {
await sess.platform.runOfflineRecoverySync().catch(() => null)
}
if (typeof sess.platform.requestGuildSyncBurst === 'function') {
sess.platform.requestGuildSyncBurst({ clearPushHalt: true })
} else if (typeof sess.platform.requestGuildSyncFanout === 'function') {
sess.platform.requestGuildSyncFanout()
}
}
const readyWait = {
meshRecoveryBurstReady: true,
meshPushNotHalted: true,
meshPeersMin: 1
}
const msgReady = { ...readyWait, activeChannelMessageIncludes: token }
await host.wait(msgReady, 90000)
await guest1.wait(msgReady, 90000)
await guest2.wait(msgReady, 90000)
const hv = await host.refreshView()
const g1v = await guest1.refreshView()
const g2v = await guest2.refreshView()
return {
token,
hostPeers: hv.stats?.peers,
guest1Peers: g1v.stats?.peers,
guest2Peers: g2v.stats?.peers,
hostOutbox: hv.guildSyncHealth?.pendingGossipCount,
guest1Outbox: g1v.guildSyncHealth?.pendingGossipCount,
guest2Outbox: g2v.guildSyncHealth?.pendingGossipCount,
guest1PushHalted: g1v.meshStabilityStats?.pushHalted,
guest2PushHalted: g2v.meshStabilityStats?.pushHalted
}
} finally {
await host.close()
await guest1.close()
await guest2.close()
}
}
/** Phase 813: 3-peer mesh — both guests see full roster (host + 2 joiners). */
async runGuildMeshTripleMemberRosterJourney () {
const { connectGuildMeshTriple } = require('./mesh-helpers')