Phase 845: runGuildMeshQuadChannelTopicJourney and meshQuadChannelTopicReady
Four-peer channel topic and forum tag fanout extends Phase 798 triple slice. 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.
|
||||
|
||||
**Phase 845 (v0.8.812):** `runGuildMeshQuadChannelTopicJourney` (4-peer channel topic + forum tags). Bundle: `npm run test:ci-phase845`.
|
||||
|
||||
**Phase 844 (v0.8.811):** `runGuildMeshQuadStickerJourney` (4-peer custom sticker). Bundle: `npm run test:ci-phase844`.
|
||||
|
||||
**Phase 843 (v0.8.810):** `runGuildMeshQuadEmojiJourney` (4-peer custom emoji). Bundle: `npm run test:ci-phase843`.
|
||||
|
||||
@@ -233,6 +233,20 @@ function matchView (view, spec) {
|
||||
if (!hasSticker) return false
|
||||
continue
|
||||
}
|
||||
if (key === 'meshQuadChannelTopicReady') {
|
||||
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
|
||||
const chId = view?.activeChannelId
|
||||
const ch = (view?.channels || []).find((c) => c.id === chId)
|
||||
const topic =
|
||||
ch?.topic || view?.activeChannelSummary?.topic || ''
|
||||
if (!topic || String(topic).length < 1) return false
|
||||
continue
|
||||
}
|
||||
if (key === 'meshQuadUnreadReady') {
|
||||
if (!expected) return true
|
||||
const peers = Number(view?.stats?.peers) || 0
|
||||
|
||||
+99
@@ -8479,6 +8479,105 @@ class HeadlessSession {
|
||||
}
|
||||
}
|
||||
|
||||
/** Phase 845: 4-peer mesh — host topic + forum tags visible on all three guests. */
|
||||
async runGuildMeshQuadChannelTopicJourney () {
|
||||
const { connectGuildMeshQuad } = require('./mesh-helpers')
|
||||
const topicToken = `mesh845_${Date.now()}`
|
||||
const tagA = `tag845a_${Date.now()}`
|
||||
const tagB = `tag845b_${Date.now()}`
|
||||
const base = path.join(os.tmpdir(), `pearcord-agentctl-chtopic845-${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: 'cthost845', displayName: 'Topic Host 845' })
|
||||
await host.createGuild({ name: 'Topic Quad 845', 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: 'ctguest845a', displayName: 'Topic Guest 845a' })
|
||||
await guest2.registerUser({ username: 'ctguest845b', displayName: 'Topic Guest 845b' })
|
||||
await guest3.registerUser({ username: 'ctguest845c', displayName: 'Topic Guest 845c' })
|
||||
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)
|
||||
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 guest3.ipc({ type: 'select-channel', channelId: textId })
|
||||
await guest1.wait({ activeChannelId: textId }, 30000)
|
||||
await guest2.wait({ activeChannelId: textId }, 30000)
|
||||
await guest3.wait({ activeChannelId: textId }, 30000)
|
||||
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 host.platform.setChannelTopic(textId, `Phase 845 ${topicToken}`)
|
||||
for (const p of [host.platform, guest1.platform, guest2.platform, guest3.platform]) {
|
||||
if (typeof p.requestGuildSyncFanout === 'function') {
|
||||
p.requestGuildSyncFanout()
|
||||
}
|
||||
}
|
||||
const topicWait = {
|
||||
meshQuadChannelTopicReady: true,
|
||||
activeChannelTopicIncludes: topicToken
|
||||
}
|
||||
await guest1.wait(topicWait, 90000)
|
||||
await guest2.wait(topicWait, 90000)
|
||||
await guest3.wait(topicWait, 90000)
|
||||
const forumCh = await host.createGuildChannel('Forum Quad 845', { type: 'forum' })
|
||||
await guest1.ipc({ type: 'select-channel', channelId: forumCh.id })
|
||||
await guest2.ipc({ type: 'select-channel', channelId: forumCh.id })
|
||||
await guest3.ipc({ type: 'select-channel', channelId: forumCh.id })
|
||||
await guest1.wait({ activeChannelId: forumCh.id }, 30000)
|
||||
await guest2.wait({ activeChannelId: forumCh.id }, 30000)
|
||||
await guest3.wait({ activeChannelId: forumCh.id }, 30000)
|
||||
await host.platform.setForumChannelPalette(forumCh.id, [tagA, tagB])
|
||||
for (const p of [host.platform, guest1.platform, guest2.platform, guest3.platform]) {
|
||||
if (typeof p.requestGuildSyncFanout === 'function') {
|
||||
p.requestGuildSyncFanout()
|
||||
}
|
||||
}
|
||||
await guest1.wait({ forumTagPaletteMin: 2 }, 90000)
|
||||
await guest2.wait({ forumTagPaletteMin: 2 }, 90000)
|
||||
await guest3.wait({ forumTagPaletteMin: 2 }, 90000)
|
||||
const g3v = await guest3.refreshView()
|
||||
const palette = g3v.forumPaletteTags || []
|
||||
if (!palette.includes(tagA) || !palette.includes(tagB)) {
|
||||
throw new Error('guest3 missing forum tag palette on quad mesh')
|
||||
}
|
||||
return {
|
||||
channelId: textId,
|
||||
topicToken,
|
||||
forumChannelId: forumCh.id,
|
||||
guest3PaletteLen: palette.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. */
|
||||
async runGuildMeshQuadChurnRejoinJourney () {
|
||||
const {
|
||||
|
||||
Reference in New Issue
Block a user