feat(agentctl): 3-peer channel topic and forum tag journey (Phase 798)

runGuildMeshTripleChannelTopicJourney verifies topic and palette on both guests.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Raven Scott
2026-06-03 22:35:34 -04:00
co-authored by Cursor
parent b3728acc96
commit 2c7780eb3a
2 changed files with 87 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. 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 798 (v0.8.765):** `runGuildMeshTripleChannelTopicJourney` (3-peer topic + forum tags). Bundle: `npm run test:ci-phase798`.
**Phase 797 (v0.8.764):** `runGuildMeshChannelTopicJourney` (topic + forum tags). Bundle: `npm run test:ci-phase797`. **Phase 797 (v0.8.764):** `runGuildMeshChannelTopicJourney` (topic + forum tags). Bundle: `npm run test:ci-phase797`.
**Phase 796 (v0.8.763):** `runGuildMeshTriplePartitionHealJourney` (3-peer heal + presence). Bundle: `npm run test:ci-phase796`. **Phase 796 (v0.8.763):** `runGuildMeshTriplePartitionHealJourney` (3-peer heal + presence). Bundle: `npm run test:ci-phase796`.
+85
View File
@@ -4986,6 +4986,91 @@ class HeadlessSession {
} }
} }
/** Phase 798: 3-peer mesh — host topic + forum tags visible on both guests. */
async runGuildMeshTripleChannelTopicJourney () {
const { connectGuildMeshTriple } = require('./mesh-helpers')
const topicToken = `mesh798_${Date.now()}`
const tagA = `tag798a_${Date.now()}`
const tagB = `tag798b_${Date.now()}`
const base = path.join(os.tmpdir(), `pearcord-agentctl-chtopic798-${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: 'cthost798', displayName: 'Topic Host 798' })
await host.createGuild({ name: 'Topic Triple 798', 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: 'ctguest798a', displayName: 'Topic Guest 798a' })
await guest2.registerUser({ username: 'ctguest798b', displayName: 'Topic Guest 798b' })
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 textId = (await host.refreshView()).activeChannelId
if (!textId) throw new Error('active text channel missing')
await guest1.ipc({ type: 'select-channel', channelId: textId })
await guest2.ipc({ type: 'select-channel', channelId: textId })
await guest1.wait({ activeChannelId: textId }, 30000)
await guest2.wait({ activeChannelId: textId }, 30000)
const mesh = 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.platform.setChannelTopic(textId, `Phase 798 ${topicToken}`)
for (const p of [host.platform, guest1.platform, guest2.platform]) {
if (typeof p.requestGuildSyncFanout === 'function') {
p.requestGuildSyncFanout()
}
}
await guest1.wait({ activeChannelTopicIncludes: topicToken }, 90000)
await guest2.wait({ activeChannelTopicIncludes: topicToken }, 90000)
const forumCh = await host.createGuildChannel('Forum Triple 798', { type: 'forum' })
await guest1.ipc({ type: 'select-channel', channelId: forumCh.id })
await guest2.ipc({ type: 'select-channel', channelId: forumCh.id })
await guest1.wait({ activeChannelId: forumCh.id }, 30000)
await guest2.wait({ activeChannelId: forumCh.id }, 30000)
await host.platform.setForumChannelPalette(forumCh.id, [tagA, tagB])
for (const p of [host.platform, guest1.platform, guest2.platform]) {
if (typeof p.requestGuildSyncFanout === 'function') {
p.requestGuildSyncFanout()
}
}
await guest1.wait({ forumTagPaletteMin: 2 }, 90000)
await guest2.wait({ forumTagPaletteMin: 2 }, 90000)
const g1v = await guest1.refreshView()
const g2v = await guest2.refreshView()
for (const gv of [g1v, g2v]) {
const palette = gv.forumPaletteTags || []
if (!palette.includes(tagA) || !palette.includes(tagB)) {
throw new Error('guest missing forum tag palette on triple mesh')
}
}
return {
channelId: textId,
topicToken,
forumChannelId: forumCh.id,
hostPeers: mesh.hostPeers,
guest1Peers: mesh.guest1Peers,
guest2Peers: mesh.guest2Peers
}
} finally {
await host.close()
await guest1.close()
await guest2.close()
}
}
/** Phase 797: 2-peer mesh — host topic + forum tags visible on guest. */ /** Phase 797: 2-peer mesh — host topic + forum tags visible on guest. */
async runGuildMeshChannelTopicJourney () { async runGuildMeshChannelTopicJourney () {
const { connectGuildMeshPeers } = require('./mesh-helpers') const { connectGuildMeshPeers } = require('./mesh-helpers')