feat(agentctl): Phase 673 member/voice/stage headless journeys

Add runMemberListJourney, runVoiceRosterJourney, and runStageSpeakerJourney
for Phase 673 regression coverage alongside README notes.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Raven Scott
2026-06-02 22:05:03 -04:00
co-authored by Cursor
parent b8a427b849
commit 675eab67b8
2 changed files with 51 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 673 (v0.8.648):** `runMemberListJourney`, `runVoiceRosterJourney`, `runStageSpeakerJourney`; bundle `test:agentctl-phase673-member-voice`.
**Phase 672 (v0.8.647):** `runThreadCreateJourney`, `runForumPostSearchJourney`, `runThreadArchiveJourney`; bundle `test:agentctl-phase672-threads-forum`.
**Phase 671 (v0.8.646):** `runSearchModalJourney`, `runPinToggleJourney`, `runJumpToMessageJourney`; bundle `test:agentctl-phase671-search-pins`.
+49
View File
@@ -3113,6 +3113,55 @@ class HeadlessSession {
return { threadId, messageId: row.id }
}
async runMemberListJourney () {
if (!this.platform) throw new Error('headless not started')
await this.registerUser({ username: 'mb673', displayName: 'Member 673' })
await this.createGuild({ name: 'Member 673', publicListing: false })
await this.wait({ mode: 'guild' }, 45000)
const v = await this.refreshView()
const memberCount = (v.members || []).length
if (memberCount < 1) throw new Error('expected at least one member in roster view')
const presencePeers = (v.presence?.peers || []).length
return { memberCount, presencePeers, guildId: v.guild?.id }
}
async runVoiceRosterJourney () {
if (!this.platform) throw new Error('headless not started')
await this.registerUser({ username: 'vc673', displayName: 'Voice 673' })
await this.createGuild({ name: 'Voice 673', publicListing: false })
await this.wait({ mode: 'guild' }, 45000)
const voiceCh = await this.createGuildChannel('Voice Room', { type: 'voice', userLimit: 10 })
if (!voiceCh?.id) throw new Error('expected voice channel')
await this.joinGuildVoiceChannel(voiceCh.id)
const v = await this.refreshView()
if (v.myVoiceChannelId !== voiceCh.id) {
throw new Error('expected myVoiceChannelId after join')
}
const peers = v.voiceStatesByChannel?.[voiceCh.id] || []
if (!peers.some((p) => p.userId === v.user?.id)) {
throw new Error('expected self in voiceStatesByChannel')
}
await this.leaveGuildVoiceChannel()
return { channelId: voiceCh.id, peerCount: peers.length }
}
async runStageSpeakerJourney () {
if (!this.platform) throw new Error('headless not started')
await this.registerUser({ username: 'st673', displayName: 'Stage 673' })
await this.createGuild({ name: 'Stage 673', publicListing: false })
await this.wait({ mode: 'guild' }, 45000)
const stageCh = await this.createGuildChannel('Main Stage', { type: 'stage' })
if (!stageCh?.id) throw new Error('expected stage channel')
await this.joinGuildStageChannel(stageCh.id)
const v = await this.refreshView()
if (v.myVoiceChannelId !== stageCh.id) {
throw new Error('expected stage channel active after join')
}
const peers = v.voiceStatesByChannel?.[stageCh.id] || []
if (!peers.length) throw new Error('expected stage voice state row')
return { channelId: stageCh.id, peerCount: peers.length, myStageRole: v.myStageRole }
}
async upsertGuildSound (opts) {
if (!this.platform) throw new Error('headless not started')
const row = await this.platform.upsertGuildSound(opts)