feat(agentctl): guild channel settings mesh journey (Phase 794)

Add channelSlowmodeAt matcher and runGuildMeshChannelSettingsJourney for
2-peer slowmode propagation over guild mesh.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Raven Scott
2026-06-03 22:21:45 -04:00
co-authored by Cursor
parent 8c158fe914
commit 571823da51
4 changed files with 55 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 794 (v0.8.761):** `runGuildMeshChannelSettingsJourney` (2-peer slowmode gossip). Bundle: `npm run test:ci-phase794`.
**Phase 793 (v0.8.760):** `runGuildMeshSoundJourney` (2-peer custom sound gossip). Bundle: `npm run test:ci-phase793`. **Phase 793 (v0.8.760):** `runGuildMeshSoundJourney` (2-peer custom sound gossip). Bundle: `npm run test:ci-phase793`.
**Phase 792 (v0.8.759):** `runGuildMeshPinsJourney` (2-peer channel pin gossip). Bundle: `npm run test:ci-phase792`. **Phase 792 (v0.8.759):** `runGuildMeshPinsJourney` (2-peer channel pin gossip). Bundle: `npm run test:ci-phase792`.
+7
View File
@@ -78,6 +78,13 @@ function matchView (view, spec) {
if (count < Number(spec.min ?? 1)) return false if (count < Number(spec.min ?? 1)) return false
continue continue
} }
if (key === 'channelSlowmodeAt') {
const spec = expected && typeof expected === 'object' ? expected : {}
const sec =
Number(view?.channelSettings?.[spec.channelId]?.slowmodeSeconds) || 0
if (sec < Number(spec.minSeconds ?? 1)) return false
continue
}
if (key === 'typingUsersIncludes') { if (key === 'typingUsersIncludes') {
const uid = String(expected || '') const uid = String(expected || '')
if (!(view?.typingUsers || []).includes(uid)) return false if (!(view?.typingUsers || []).includes(uid)) return false
+42
View File
@@ -4847,6 +4847,48 @@ class HeadlessSession {
} }
} }
/** Phase 794: 2-peer mesh — host slowmode visible in guest channelSettings. */
async runGuildMeshChannelSettingsJourney () {
const { connectGuildMeshPeers } = require('./mesh-helpers')
const base = path.join(os.tmpdir(), `pearcord-agentctl-chset794-${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: 'cshost794', displayName: 'ChSet Host 794' })
await host.createGuild({ name: 'ChSet Mesh 794', 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: 'csguest794', displayName: 'ChSet Guest 794' })
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)
const slowSec = 45
await host.platform.setChannelSlowmode(chId, slowSec)
if (typeof host.platform.requestGuildSyncFanout === 'function') {
host.platform.requestGuildSyncFanout()
}
await guest.wait(
{ channelSlowmodeAt: { channelId: chId, minSeconds: slowSec } },
90000
)
const gv = await guest.refreshView()
const sec = Number(gv.channelSettings?.[chId]?.slowmodeSeconds) || 0
if (sec < slowSec) throw new Error('guest missing channel slowmode')
return { channelId: chId, slowmodeSeconds: sec }
} finally {
await host.close()
await guest.close()
}
}
/** Phase 793: 2-peer mesh — host custom sound visible in guest guildSounds. */ /** Phase 793: 2-peer mesh — host custom sound visible in guest guildSounds. */
async runGuildMeshSoundJourney () { async runGuildMeshSoundJourney () {
const { connectGuildMeshPeers } = require('./mesh-helpers') const { connectGuildMeshPeers } = require('./mesh-helpers')
+4
View File
@@ -46,6 +46,10 @@ function snapshotWaitDiagnostics (view, match) {
guildCustomEmojiCount: (view?.guildEmojis || []).filter((e) => !e.builtin).length, guildCustomEmojiCount: (view?.guildEmojis || []).filter((e) => !e.builtin).length,
guildStickerCount: view?.guildStickers?.length ?? 0, guildStickerCount: view?.guildStickers?.length ?? 0,
guildSoundCount: view?.guildSounds?.length ?? 0, guildSoundCount: view?.guildSounds?.length ?? 0,
channelSettingsCount: Object.keys(view?.channelSettings || {}).length,
activeChannelSlowmode:
Number(view?.channelSettings?.[view?.activeChannelId]?.slowmodeSeconds) ||
0,
channelPinCount: view?.pins?.length ?? 0, channelPinCount: view?.pins?.length ?? 0,
sessionReady: !!view?.sessionReady, sessionReady: !!view?.sessionReady,
workerPoll: view?.workerPoll || null, workerPoll: view?.workerPoll || null,