'use strict' const healthMixin = { _digestRelayHandoffGossipKeys: null, _scheduleDashboardHealWatermark: null, _archivePeerHealWatermark: null, _initHealthMixinState () { if (!this._digestRelayHandoffGossipKeys) { this._digestRelayHandoffGossipKeys = new Set() } }, _shouldGossipDigestRelayHandoff (slice) { this._initHealthMixinState() if (!slice?.guildId || !slice?.exportedAt) return true const hash = `${slice.guildId}:${slice.exportedAt}:${slice.snapshotId || ''}:${slice.transport || ''}:${slice.signature || ''}` if (this._digestRelayHandoffGossipKeys.has(hash)) return false this._digestRelayHandoffGossipKeys.add(hash) if (this._digestRelayHandoffGossipKeys.size > 8192) { const first = this._digestRelayHandoffGossipKeys.values().next().value if (first) this._digestRelayHandoffGossipKeys.delete(first) } return true }, async _healScheduleDashboardOnPartition (guildId) { const gid = guildId || this.guild?.guild?.id || null const span = this.log.time('automation.health', { spanKind: 'automation.health', guildId: gid, context: 'heal.schedule' }) try { if (!gid || !this.getAutomationHealthDashboard) { span.end({ relisted: 0, skipped: true, guildHealthRowCount: 0 }) return { relisted: 0, skipped: true } } const dash = await this.getAutomationHealthDashboard() const watermark = Date.now() this._scheduleDashboardHealWatermark = watermark const rowCount = (dash.overdueActions || []).length + (dash.auditExport ? 1 : 0) + (dash.digestSync ? 1 : 0) span.end({ relisted: 1, watermark, guildHealthRowCount: rowCount, anyOverdue: !!dash.anyOverdue, bridgeKind: 'automation.health' }) return { relisted: 1, watermark, guildHealthRowCount: rowCount } } catch (err) { this.log.error('automation.health error', { guildId: gid, context: 'heal.schedule', error: err?.message || String(err) }) span.fail(err) return { relisted: 0, error: err?.message || String(err) } } }, async _healArchivePeerCursorOnPartition (guildId) { const gid = guildId || this.guild?.guild?.id || null const span = this.log.time('automation.health', { spanKind: 'automation.health', guildId: gid, context: 'heal.archive' }) try { if (!gid) { span.end({ relisted: 0, skipped: true, guildHealthRowCount: 0 }) return { relisted: 0, skipped: true } } const watermark = Date.now() this._archivePeerHealWatermark = watermark const dash = await this.getAutomationHealthDashboard().catch(() => ({})) span.end({ relisted: 0, watermark, guildHealthRowCount: (dash.health?.archivePeers || []).length, avgArchivePeerScore: dash.health?.avgArchivePeerScore || 0, guildCount: (this.guilds || []).length, activeChannelId: this.activeChannelId || null, bridgeKind: 'automation.health' }) return { relisted: 0, watermark, guildHealthRowCount: (dash.health?.archivePeers || []).length } } catch (err) { this.log.error('automation.health error', { guildId: gid, context: 'heal.archive', error: err?.message || String(err) }) span.fail(err) return { relisted: 0, error: err?.message || String(err) } } } } module.exports = { healthMixin }