feat(platform): extend messaging span metadata for Phase 477

Add hasMention/isSlash on message.send, priorLength on message.edit,
hadAttachments on message.delete, emojiLength on reaction.toggle, and
channelId on pin.message/pin.unpin span start for richer diagnostics.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Raven Scott
2026-05-24 05:29:04 -04:00
co-authored by Cursor
parent 3f722f9b0f
commit 56d32a6bc9
2 changed files with 62 additions and 10 deletions
+6
View File
@@ -39,6 +39,10 @@ Expose a single `EventEmitter` API the UI drives over IPC (`apps/pearcord/index.
**v0.8.430 (Phase 467):** `voice.leave` span `end` includes `leavingIsStage`; `screen.start` span `end` includes `label`. Bundle: `npm run test:phase467-voice-stage`. See [VOICE.md](../../docs/VOICE.md), [STAGE_CHANNELS.md](../../docs/STAGE_CHANNELS.md).
**v0.8.440 (Phase 477):** `message.send` `hasMention`/`isSlash`; `message.edit` `priorLength`; `message.delete` `hadAttachments`; `reaction.toggle` `emojiLength`; `pin.message`/`pin.unpin` span start `channelId`. UI `syncMessagingPanelsDuringGuildLoading`. Bundle: `npm run test:phase477-messaging`. See [MESSAGING.md](../../docs/MESSAGING.md), [REACTIONS.md](../../docs/REACTIONS.md).
**v0.8.439 (Phase 476):** `attachment.stage` span.end adds `mimeType`; `attachment.read` adds `filename`/`mimeType`; `embed.resolve` adds `hasTitle`/`hasImage`. UI `syncAttachmentsPanelsDuringGuildLoading`. Bundle: `npm run test:phase476-attachments-embeds`. See [ATTACHMENTS.md](../../docs/ATTACHMENTS.md), [EMBEDS.md](../../docs/EMBEDS.md).
**v0.8.429 (Phase 466):** `attachment.read` span.end includes `byteLength`; `embed.resolve` includes `urlHost`. Bundle: `npm run test:phase466-attachments-embeds`. See [ATTACHMENTS.md](../../docs/ATTACHMENTS.md), [EMBEDS.md](../../docs/EMBEDS.md).
**v0.8.428 (Phase 465):** `message.send`/`message.edit` span.end includes `contentLength`; `reaction.toggle` includes `removed`. Bundle: `npm run test:phase465-messaging-reactions`. See [MESSAGING.md](../../docs/MESSAGING.md), [REACTIONS.md](../../docs/REACTIONS.md).
@@ -188,6 +192,8 @@ Primary consumer: `apps/pearcord/index.js` sidecar reading pear-pipe JSON.
**v0.8.436 (Phase 473):** `role.create`/`role.update` span.end adds `hasColor` and `permissionMask`; `permission.overwrite` adds `allowBits`/`denyBits`; `permission.overwrite.delete` adds `deleted`. UI `syncRolesPanelsDuringGuildLoading` + composer roles hint. Smokes: `test:platform-role-span-metadata-extend`, `test:platform-permission-span-metadata-extend`. Bundle: `test:phase473-roles-permissions`.
**v0.8.439 (Phase 476):** `attachment.stage`/`attachment.read`/`embed.resolve` span.end metadata extend (`mimeType`, `filename`, `hasTitle`, `hasImage`). UI `syncAttachmentsPanelsDuringGuildLoading`. Smokes: `test:platform-attachment-span-metadata-extend`, `test:platform-embed-span-metadata-extend`. Bundle: `test:phase476-attachments-embeds`.
**v0.8.438 (Phase 475):** `slash.invoke` span.end adds `argCount`/`ephemeral`/`builtin`; `bot.install` adds `botName`/`permissionMask`; `bot.message` adds `contentLength`/`botId`. UI `syncSlashBotsPanelsDuringGuildLoading` + `ensureComposerContextHints` + `#composer-slash-hint`. IPC `bot-installed`/`slash-invoked`. Bundle: `test:phase475-slash-bots`.
**v0.8.437 (Phase 474):** `webhook.create` span.end adds `webhookName`/`channelId`; `webhook.delete` adds `deleted`; `webhook.execute` adds `contentLength`/`hasUsername`; `slash.register` adds `firstName`. UI `syncWebhooksPanelsDuringGuildLoading` + composer webhooks hint. Smokes: `test:platform-webhook-span-metadata-extend`, `test:platform-slash-span-metadata-extend`. Bundle: `test:phase474-webhooks-integrations`.
+56 -10
View File
@@ -5006,7 +5006,13 @@ class PearcordPlatform extends EventEmitter {
channelId: this.activeChannelId,
authorId: user.id
})
span.end({ attachmentId: row?.id, size: row?.size, guildId, channelId })
span.end({
attachmentId: row?.id,
size: row?.size,
guildId,
channelId,
mimeType: row?.mimeType || mimeType || null
})
return row
} catch (err) {
this.log.error('attachment.stage error', {
@@ -7727,7 +7733,9 @@ class PearcordPlatform extends EventEmitter {
fetchSource: source,
guildId,
channelId,
byteLength: buf?.length ?? 0
byteLength: buf?.length ?? 0,
filename: row?.filename || null,
mimeType: row?.mimeType || null
})
return preview
} catch (err) {
@@ -7841,7 +7849,9 @@ class PearcordPlatform extends EventEmitter {
resolved: !!row,
guildId,
channelId,
urlHost
urlHost,
hasTitle: !!(row?.title),
hasImage: !!(row?.image || row?.thumbnail)
})
} catch (err) {
this.log.error('embed.resolve error', {
@@ -10671,11 +10681,17 @@ class PearcordPlatform extends EventEmitter {
if (this._readOnly) throw new Error('read-only companion mode cannot send messages')
if (!this.messages) throw new Error('no channel')
const msg = await this._sendPlainMessage(content, opts)
const body = String(content || '')
span.end({
messageId: msg?.id,
guildId,
channelId,
contentLength: String(content || '').length
contentLength: body.length,
hasReply: !!opts.replyToId,
hasMention: /(?:<@[^>]+>|@\w)/.test(body),
isSlash: body.trimStart().startsWith('/'),
attachmentCount: (opts.attachmentIds || []).length,
stickerCount: (opts.stickerNames || []).length
})
return msg
} catch (err) {
@@ -10813,6 +10829,11 @@ class PearcordPlatform extends EventEmitter {
const span = this.log.time('message.edit', { messageId, guildId, channelId })
try {
await this._assertCanModifyMessage(messageId, 'edit')
const prior = await this.db.get(COLLECTIONS.MESSAGES, {
channelId,
id: messageId
})
const priorLength = String(prior?.content || '').length
const msg = await this.messages.edit(messageId, content)
if (this.mode === 'dm' && this.dm) this.dm.gossipMessageUpdate(msg)
else if (this.guild) this.guild.gossipMessageUpdate(msg)
@@ -10821,7 +10842,9 @@ class PearcordPlatform extends EventEmitter {
messageId: msg?.id,
guildId,
channelId,
contentLength: String(content || '').length
contentLength: String(content || '').length,
priorLength,
edited: true
})
return msg
} catch (err) {
@@ -11285,7 +11308,11 @@ class PearcordPlatform extends EventEmitter {
async pinMessage (messageId) {
const guildId = this.guild?.guild?.id || null
const span = this.log.time('pin.message', { messageId, guildId })
const span = this.log.time('pin.message', {
messageId,
guildId,
channelId: this.activeChannelId
})
try {
const user = this.identity.user
if (!user || !this.guild?.guild) throw new Error('no guild')
@@ -11315,6 +11342,7 @@ class PearcordPlatform extends EventEmitter {
this.log.error('pin.message error', {
messageId,
guildId,
channelId: this.activeChannelId,
error: err?.message || String(err)
})
span.fail(err)
@@ -11324,7 +11352,11 @@ class PearcordPlatform extends EventEmitter {
async unpinMessage (messageId) {
const guildId = this.guild?.guild?.id || null
const span = this.log.time('pin.unpin', { messageId, guildId })
const span = this.log.time('pin.unpin', {
messageId,
guildId,
channelId: this.activeChannelId
})
try {
const roles = await this._memberRoles()
if (!roleHasPermission(roles, PERMISSION.MANAGE_MESSAGES)) {
@@ -11341,6 +11373,7 @@ class PearcordPlatform extends EventEmitter {
this.log.error('pin.unpin error', {
messageId,
guildId,
channelId: this.activeChannelId,
error: err?.message || String(err)
})
span.fail(err)
@@ -11446,12 +11479,19 @@ class PearcordPlatform extends EventEmitter {
const result = await this.messages.toggleReaction(messageId, emoji, user.id)
if (this.mode !== 'dm' && this.guild) this.guild.gossipReaction(result)
this.emit('reaction', result)
const emojiKey = String(emoji || '').slice(0, 16)
const reactionRows = await this.messages.listReactions()
const reactionCount = reactionRows.filter(
(r) => r.messageId === messageId && r.emoji === emojiKey
).length
span.end({
messageId,
emoji: String(emoji || '').slice(0, 16),
emoji: emojiKey,
emojiLength: emojiKey.length,
guildId,
channelId,
removed: result?.removed === true
removed: result?.removed === true,
reactionCount
})
return result
} catch (err) {
@@ -11489,6 +11529,12 @@ class PearcordPlatform extends EventEmitter {
const span = this.log.time('message.delete', { messageId, guildId, channelId })
try {
await this._assertCanModifyMessage(messageId, 'delete')
const existing = await this.db.get(COLLECTIONS.MESSAGES, {
channelId,
id: messageId
})
const contentLength = String(existing?.content || '').length
const hadAttachments = !!(existing?.attachmentIds?.length || existing?.attachments?.length)
await this.messages.remove(messageId)
const payload = {
id: messageId,
@@ -11498,7 +11544,7 @@ class PearcordPlatform extends EventEmitter {
if (this.mode === 'dm' && this.dm) this.dm.gossipMessageDelete(payload)
else if (this.guild) this.guild.gossipMessageDelete(payload)
this.emit('message-delete', payload)
span.end({ messageId, guildId, channelId })
span.end({ messageId, guildId, channelId, contentLength, hadAttachments })
} catch (err) {
this.log.error('message.delete error', {
messageId,