Phase 789: agentctl guild mesh reactions journey (v0.8.756)
Add messageReactionIncludes matcher and runGuildMeshReactionsJourney for 2-peer message + reaction gossip verification. 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 789 (v0.8.756):** `runGuildMeshReactionsJourney` (2-peer reaction gossip). Bundle: `npm run test:ci-phase789`.
|
||||||
|
|
||||||
**Phase 788 (v0.8.755):** `runGuildMeshReconnectJourney` (2-peer disconnect/reconnect). Bundle: `npm run test:ci-phase788`.
|
**Phase 788 (v0.8.755):** `runGuildMeshReconnectJourney` (2-peer disconnect/reconnect). Bundle: `npm run test:ci-phase788`.
|
||||||
|
|
||||||
**Phase 787 (v0.8.754):** `runGuildMeshMemberRolesJourney` (2-peer custom role assign). Bundle: `npm run test:ci-phase787`.
|
**Phase 787 (v0.8.754):** `runGuildMeshMemberRolesJourney` (2-peer custom role assign). Bundle: `npm run test:ci-phase787`.
|
||||||
|
|||||||
@@ -108,6 +108,14 @@ function matchView (view, spec) {
|
|||||||
if (n !== Number(expected)) return false
|
if (n !== Number(expected)) return false
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if (key === 'messageReactionIncludes') {
|
||||||
|
const spec = expected && typeof expected === 'object' ? expected : {}
|
||||||
|
const row = view?.reactions?.[spec.messageId] || {}
|
||||||
|
const hit = row[spec.emoji]
|
||||||
|
const min = Number(spec.minCount ?? 1)
|
||||||
|
if (!hit || Number(hit.count) < min) return false
|
||||||
|
continue
|
||||||
|
}
|
||||||
if (key === 'voiceChannelPeersMin') {
|
if (key === 'voiceChannelPeersMin') {
|
||||||
const spec = expected && typeof expected === 'object' ? expected : {}
|
const spec = expected && typeof expected === 'object' ? expected : {}
|
||||||
const n = (view?.voiceStatesByChannel?.[spec.channelId] || []).length
|
const n = (view?.voiceStatesByChannel?.[spec.channelId] || []).length
|
||||||
|
|||||||
+48
@@ -4803,6 +4803,54 @@ class HeadlessSession {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Phase 789: 2-peer mesh — host reaction on message visible on guest. */
|
||||||
|
async runGuildMeshReactionsJourney () {
|
||||||
|
const { connectGuildMeshPeers } = require('./mesh-helpers')
|
||||||
|
const token = `mesh789-${Date.now()}`
|
||||||
|
const base = path.join(os.tmpdir(), `pearcord-agentctl-rx789-${Date.now()}`)
|
||||||
|
const host = new HeadlessSession({ storagePath: path.join(base, 'host') })
|
||||||
|
const guest = new HeadlessSession({ storagePath: path.join(base, 'guest') })
|
||||||
|
await host.start()
|
||||||
|
await guest.start()
|
||||||
|
try {
|
||||||
|
await host.registerUser({ username: 'rxhost789', displayName: 'Rx Host 789' })
|
||||||
|
await host.createGuild({ name: 'Reactions Mesh 789', 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 guest.registerUser({ username: 'rxguest789', displayName: 'Rx Guest 789' })
|
||||||
|
await guest.ipc({ type: 'join-invite', code })
|
||||||
|
await guest.wait({ mode: 'guild' }, 45000)
|
||||||
|
const chId = (await host.refreshView()).activeChannelId
|
||||||
|
if (!chId) throw new Error('active channel missing')
|
||||||
|
await guest.ipc({ type: 'select-channel', channelId: chId })
|
||||||
|
await guest.wait({ activeChannelId: chId }, 30000)
|
||||||
|
await connectGuildMeshPeers(host.platform, guest.platform, 45000)
|
||||||
|
await host.sendGuildMessage(token)
|
||||||
|
await guest.wait({ activeChannelMessagesMin: 1 }, 90000)
|
||||||
|
const hv = await host.refreshView()
|
||||||
|
const msg = (hv.messages || []).find((m) => String(m.content || '').includes(token))
|
||||||
|
if (!msg?.id) throw new Error('host message missing')
|
||||||
|
await host.platform.toggleReaction(msg.id, '👍')
|
||||||
|
if (typeof host.platform.requestGuildSyncFanout === 'function') {
|
||||||
|
host.platform.requestGuildSyncFanout()
|
||||||
|
}
|
||||||
|
await guest.wait(
|
||||||
|
{ messageReactionIncludes: { messageId: msg.id, emoji: '👍', minCount: 1 } },
|
||||||
|
90000
|
||||||
|
)
|
||||||
|
const gv = await guest.refreshView()
|
||||||
|
return {
|
||||||
|
messageId: msg.id,
|
||||||
|
guestReaction: gv.reactions?.[msg.id]?.['👍']
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
await host.close()
|
||||||
|
await guest.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Phase 788: 2-peer mesh disconnect + reconnect; guest recovers sync rail. */
|
/** Phase 788: 2-peer mesh disconnect + reconnect; guest recovers sync rail. */
|
||||||
async runGuildMeshReconnectJourney () {
|
async runGuildMeshReconnectJourney () {
|
||||||
const {
|
const {
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ function snapshotWaitDiagnostics (view, match) {
|
|||||||
meshBounded: !!(view?.guild?.id && view.guildSyncHealthRail?.[view.guild.id]?.meshBounded),
|
meshBounded: !!(view?.guild?.id && view.guildSyncHealthRail?.[view.guild.id]?.meshBounded),
|
||||||
meshRetryCountdownMs:
|
meshRetryCountdownMs:
|
||||||
Number(view?.guildSyncHealthRail?.[view?.guild?.id]?.retryCountdownMs) || 0,
|
Number(view?.guildSyncHealthRail?.[view?.guild?.id]?.retryCountdownMs) || 0,
|
||||||
|
reactionMessageCount: Object.keys(view?.reactions || {}).length,
|
||||||
sessionReady: !!view?.sessionReady,
|
sessionReady: !!view?.sessionReady,
|
||||||
workerPoll: view?.workerPoll || null,
|
workerPoll: view?.workerPoll || null,
|
||||||
viewBuildStats: view?.viewBuildStats || null
|
viewBuildStats: view?.viewBuildStats || null
|
||||||
|
|||||||
Reference in New Issue
Block a user