feat(threads-forum): channel.delete and thread/forum span guildId (v0.8.421)

deleteChannel with gossip; thread.create, thread.archive, and forum.post
spans include guildId, span.fail, and structured error logs.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Raven Scott
2026-05-24 01:04:42 -04:00
co-authored by Cursor
parent 21cac82026
commit 98134b9abc
2 changed files with 62 additions and 6 deletions
+2
View File
@@ -35,6 +35,8 @@ Expose a single `EventEmitter` API the UI drives over IPC (`apps/pearcord/index.
| `onboarded`, `identity`, `storagePath`, `db`, `dbPath` | Session state |
| `listGuilds()`, `loadGuild(guildId)`, `createGuild({ name, publicListing? })`, `joinInvite(code)` | Guild session |
**v0.8.421 (Phase 458):** `channel.delete` span + gossip; `thread.create`, `thread.archive`, `forum.post` spans include `guildId` + `span.fail`. Bundle: `npm run test:phase458-threads-forum`. See [MESSAGE_THREADS.md](../../docs/MESSAGE_THREADS.md), [FORUM_CHANNELS.md](../../docs/FORUM_CHANNELS.md).
**v0.8.420 (Phase 457):** `channel.create`, `channel.update` (rename), and `category.create` spans + error logs with `guildId`. `create-channel` IPC selects new channel and emits composer focus. Bundle: `npm run test:phase457-channels`. See [CHANNELS.md](../../docs/CHANNELS.md).
**v0.8.419 (Phase 456):** `session.startup` span ends with `guildCount` + `guildId`; `session.startup error` log. `identity.register` span + `identity.register error`. `guild.open` span end includes `guildId`. `loadGuild` / `createGuild` bust search cache and clear `_lastSearchFromCache`. Bundle: `npm run test:phase456-onboarding-session`. See [SESSION.md](../../docs/SESSION.md).
+60 -6
View File
@@ -3077,6 +3077,9 @@ class PearcordPlatform extends EventEmitter {
this._fanoutBotEvent(BOT_EVENTS.CHANNEL_CREATE, { channel: ch })
this.emit('channel', ch)
})
guildInstance.on('channel-delete', (p) => {
this.emit('channel-delete', p)
})
guildInstance.on('pin', (p) => this.emit('pin', p))
guildInstance.on('ban', (p) => this._onBanGossip(p))
guildInstance.on('channel-settings', (p) => {
@@ -10657,7 +10660,9 @@ class PearcordPlatform extends EventEmitter {
}
async createForumPost ({ title, content, tags }) {
const guildId = this.guild?.guild?.id || null
const span = this.log.time('forum.post', {
guildId,
title: String(title || '').trim().slice(0, 64) || null
})
try {
@@ -10682,7 +10687,6 @@ class PearcordPlatform extends EventEmitter {
const message = await this.sendMessage(body)
const merged = await this.guild.setThreadRootMessage(thread.id, message.id)
this.guild.gossipChannelUpdate(merged)
const guildId = this.guild.guild.id
const palette = this.forum.parsePaletteFromTopic(forum.topic)
const postTags = this.forum.normalizePostTags(
(tags || []).filter((t) => !palette.length || palette.includes(t))
@@ -10701,19 +10705,25 @@ class PearcordPlatform extends EventEmitter {
targetId: thread.id
})
this.emit('channel', merged)
span.end({ threadId: thread.id, tagCount: postTags.length })
span.end({ guildId, threadId: thread.id, tagCount: postTags.length })
return { thread: { ...merged, tags: postTags }, message }
} catch (err) {
this.log.error('forum.post error', {
guildId,
title: String(title || '').trim().slice(0, 64) || null,
err: err?.message || String(err)
})
span.fail(err)
throw err
}
}
async createThread ({ messageId, name }) {
const span = this.log.time('thread.create', { messageId: messageId || null })
const guildId = this.guild?.guild?.id || null
const span = this.log.time('thread.create', {
guildId,
messageId: messageId || null
})
try {
if (!this.guild?.guild || !this.activeChannelId) throw new Error('no active text channel')
const parent = await this.db.get(COLLECTIONS.CHANNELS, {
@@ -10741,13 +10751,15 @@ class PearcordPlatform extends EventEmitter {
})
this.selectChannel(thread.id)
this.emit('channel', thread)
span.end({ threadId: thread.id, parentId: parent.id })
span.end({ guildId, threadId: thread.id, parentId: parent.id })
return thread
} catch (err) {
this.log.error('thread.create error', {
guildId,
messageId: messageId || null,
err: err?.message || String(err)
})
span.fail(err)
throw err
}
}
@@ -10841,7 +10853,12 @@ class PearcordPlatform extends EventEmitter {
}
async archiveThread (threadId, archived = true) {
const span = this.log.time('thread.archive', { threadId, archived: !!archived })
const guildId = this.guild?.guild?.id || null
const span = this.log.time('thread.archive', {
guildId,
threadId,
archived: !!archived
})
try {
if (!this.guild?.guild) throw new Error('no guild')
const channels = await this.guild.listChannels()
@@ -10854,14 +10871,51 @@ class PearcordPlatform extends EventEmitter {
targetId: threadId
})
this.emit('channel-update', merged)
span.end({ threadId, archived: !!archived })
span.end({ guildId, threadId, archived: !!archived })
return merged
} catch (err) {
this.log.error('thread.archive error', {
guildId,
threadId,
archived: !!archived,
err: err?.message || String(err)
})
span.fail(err)
throw err
}
}
async deleteChannel (channelId) {
const guildId = this.guild?.guild?.id || null
const span = this.log.time('channel.delete', { guildId, channelId: channelId || null })
try {
if (!this.guild) throw new Error('no guild')
if (!(await this._hasPerm(PERMISSION.MANAGE_CHANNELS))) {
throw new Error('no permission to delete channels')
}
const deletedId = String(channelId || '')
const wasActive = this.activeChannelId === deletedId
const payload = await this.guild.deleteChannel(deletedId)
this.guild.gossipChannelDelete(payload)
this._invalidateSearchCache()
if (wasActive) {
const channels = await this.guild.listChannels()
await this._selectGuildChannelWithUnread(channels)
}
await this._audit('channel.delete', {
guildId: this.guild.guild.id,
targetId: deletedId
})
this.emit('channel-delete', payload)
span.end({ guildId, channelId: deletedId })
return payload
} catch (err) {
this.log.error('channel.delete error', {
guildId,
channelId: channelId || null,
err: err?.message || String(err)
})
span.fail(err)
throw err
}
}