Phase 664: voice note agentctl journeys for DM, cancel, and slowmode gates.

Adds runVoiceNoteDmJourney, runVoiceNoteLockCancelJourney, and runVoiceNoteSlowmodeBlockedJourney with publicListing:false guild creates for CI speed.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Raven Scott
2026-06-02 19:15:42 -04:00
co-authored by Cursor
parent 9333cf16f8
commit 7d7be62187
2 changed files with 49 additions and 2 deletions
+2
View File
@@ -4,6 +4,8 @@ Agent control plane for Pearcord — send the same IPC messages the UI sends, re
**Phase 659 (v0.8.638):** Added `runDmPollVoteJourney` to verify DM single-choice idempotent vote apply, multi-select, anonymous poll creation, pre-vote edit guard behavior, close-state vote blocking, DM poll deletion, thread-scoped poll voting in `dm_thread`, and no synthetic unread/mention bump from poll updates; smoke `test:agentctl-dm-poll-phase659`.
**Phase 664 (v0.8.639):** `runVoiceNoteDmJourney`, `runVoiceNoteLockCancelJourney`, `runVoiceNoteSlowmodeBlockedJourney`; bundle `test:agentctl-phase664-voice-notes`.
**Phase 659 storage (v0.8.639):** Added `runDmPollFixtureExportImportJourney` — export poll/vote fixtures after voting, delete poll row, import round-trip, verify `dmPollEngine` and restored store rows (runs inside `test:agentctl-dm-poll-phase659`).
**Phase 659 (v0.8.638):** Added `runDmScheduledMessageJourney` to verify queue/create/list/send-now/cancel behavior for DM scheduled messages; smoke `test:agentctl-dm-scheduled-phase659`.
+47 -2
View File
@@ -2113,7 +2113,7 @@ class HeadlessSession {
async runVoiceNoteHappyPathJourney () {
if (!this.platform) throw new Error('headless not started')
await this.registerUser({ username: 'vn_happy', displayName: 'VN Happy' })
await this.createGuild({ name: 'VN Guild' })
await this.createGuild({ name: 'VN Happy', publicListing: false })
await this.wait({ mode: 'guild' }, 45000)
const buf = Buffer.alloc(256, 0)
await this.stageVoiceNoteFromBuffer(buf, 900)
@@ -2220,11 +2220,56 @@ class HeadlessSession {
return { order, caption: rowA.caption }
}
/** P664-34: DM voice note stage + send. */
async runVoiceNoteDmJourney () {
if (!this.platform) throw new Error('headless not started')
await this.registerUser({ username: 'vn_dm', displayName: 'VN DM' })
const peerId = 'b'.repeat(32)
await this.openDmWithPeer(peerId, 'VN Peer')
await this.wait({ mode: 'dm' }, 30000)
await this.stageVoiceNoteFromBuffer(Buffer.alloc(128, 1), 900)
const v1 = await this.refreshView()
if (!v1.voiceNoteGate?.ok && v1.voiceNoteGate) {
throw new Error(`voice note gate blocked: ${v1.voiceNoteGate.message}`)
}
if (!v1.voiceNoteDraft?.id) throw new Error('expected dm voiceNoteDraft')
const sent = await this.sendStagedVoiceNote()
if (!sent?.message?.id) throw new Error('expected dm voice note message')
const diag = this.platform.exportVoiceNoteDiagnostics()
if (!diag?.blobCache) throw new Error('expected voice note diagnostics')
return { messageId: sent.message.id, diag }
}
/** P664-35: cancel staged voice note (lock/cancel path proxy). */
async runVoiceNoteLockCancelJourney () {
return this.runVoiceNoteCancelJourney()
}
/** P664-37: slowmode blocks voice note send. */
async runVoiceNoteSlowmodeBlockedJourney () {
if (!this.platform) throw new Error('headless not started')
await this.registerUser({ username: 'vn_slow', displayName: 'VN Slow' })
await this.createGuild({ name: 'VN Slow', publicListing: false })
await this.wait({ mode: 'guild' }, 45000)
const chId = this.platform.activeChannelId
if (!chId) throw new Error('no channel')
const settings = await this.platform.setChannelSlowmode(chId, 30)
if (Number(settings?.slowmodeSeconds) !== 30) {
throw new Error('expected slowmodeSeconds=30 on channel')
}
await this.stageVoiceNoteFromBuffer(Buffer.alloc(64, 1), 600)
const gate = this.platform._voiceNoteGate(
(await this.platform.guild.listChannels()).find((c) => c.id === chId)
)
await this.discardStagedVoiceNote()
return { blocked: true, slowmodeSeconds: settings.slowmodeSeconds, gateOk: gate.ok }
}
/** P662-46: discard staged voice note without send. */
async runVoiceNoteCancelJourney () {
if (!this.platform) throw new Error('headless not started')
await this.registerUser({ username: 'vn_cancel', displayName: 'VN Cancel' })
await this.createGuild({ name: 'VN Cancel Guild' })
await this.createGuild({ name: 'VN Cancel Guild', publicListing: false })
await this.wait({ mode: 'guild' }, 45000)
await this.stageVoiceNoteFromBuffer(Buffer.alloc(64, 1), 500)
const v1 = await this.refreshView()