Call ensurePrefsRecoveredOnStartup after settings ready, log userPrefs.startup-recovery spans, and expose userPrefsRecovery in view(). Co-authored-by: Cursor <[email protected]>
pearcord-platform
Application facade: one PearcordPlatform class that wires identity, database, guild mesh, DMs, invites, voice, discovery, bots, and dozens of feature modules for the Pearcord desktop sidecar and companion.
Phase 659 (v0.8.638): DM polls slice — sendMessage parses /poll, /pollm, /polla, /pollma in DM mode; createDmPoll supports expiry metadata, voteDmPoll enforces close/expiry guardrails with idempotent desired-state apply, editDmPoll allows edits only before first vote, closeDmPoll force-closes active polls, and deleteDmPoll enforces DM/group DM poll delete permissions. See DM_POLLS.md.
Phase 661 (v0.8.640): Added explicit pearcord-dm-voice dependency declaration to keep attached/headless agentctl runtimes stable when resolving local file: modules in Bare environments.
Phase 661 (v0.8.640): Added dependency guard scripts: test:deps compares require('pearcord-*') usage vs package.json declarations, test:imports smoke-resolves imported pearcord-* modules, and ci:deps fails when undeclared imports are introduced.
Phase 410 hardening (v0.8.641): Added cached pin snapshots in view() (_pinListCacheKey/_pinListCacheGen + _invalidatePinListCache) and broadened reaction snapshot cache reuse beyond light polls to reduce duplicate startup list scans.
Phase 410 hardening (v0.8.643): view() now emits cache-path span metadata for pin.list, reaction.list, and discovery.list (cacheHit, source), coalesces concurrent discovery listing fetches behind _discoveryListPending, and reports aggregate cache counters in view.listCacheMetrics.
Phase 410 hardening (v0.8.643): view() now supports explicit detail levels (minimal/light/full/diagnostic) through opts.detail, keeps legacy opts.light compatibility, and surfaces the resolved mode as viewDetailLevel in payload diagnostics.
Phase 410 hardening (v0.8.643): guildSyncHealthRail now carries bounded-retry metadata (retryAttempt, retryDelayMs, retryMaxAttempts, nextRetryAt, retryCountdownMs) so UI can show live “mesh bounded mode” retry countdown/tooltip state during background join retries.
Phase 410 hardening (v0.8.643): Guild mesh peer retry scheduling now uses _computeGuildMeshPeerRetryDelayMs (last-peer-contact staleness + RTT bounds) instead of fixed attempt-index spacing; health rows track lastPeerSeenAt on peer join.
Phase 410 hardening (v0.8.643): _requestGuildMeshJoin coalesces duplicate guild mesh joins during guild.open, peer retry, and startup/switch races; stale loadGen joins are skipped; pearcord-guild#joinMesh deduplicates in-flight swarm.join. Smoke: npm run test:platform-guild-mesh-join-coalesce.
Phase 410 hardening (v0.8.643): _evictStaleGuildMeshPeers drops idle hyperswarm sockets (env PEARCORD_GUILD_MESH_PEER_STALE_MS, periodic sweep) and records peerRetryAttempt, lastPeerSeenAt, and eviction totals on guildSyncHealthRail. Smoke: npm run test:platform-guild-mesh-stale-peer-evict.
Phase 410 hardening (v0.8.643): _auditChannelViewConsistency runs during view() to detect pin/reaction/message mismatches, repair stale pin/reaction list caches, and expose channelConsistencyAudit for diagnostics. Smoke: npm run test:platform-channel-consistency-audit.
Phase 410 hardening (v0.8.643): _resolveActiveChannelForView / _finalizeViewActiveChannel recover when activeChannelId disappears mid-view-build; view exposes activeChannel + activeChannelRecovery. Smoke: npm run test:platform-active-channel-recover.
Phase 659 (v0.8.638): DM scheduled messages slice — sendMessage parses /schedule and /sendlater in DM mode; queue APIs createDmScheduledMessage, listDmScheduledMessages, cancelDmScheduledMessage, sendNowDmScheduledMessage, plus due runner runDueDmScheduledMessages() and local timer flush. See DM_SCHEDULED_MESSAGES.md.
Phase 658 (v0.8.638): DM thread parity closure — archiveThread supports dm_thread channels in DM mode; exportDmThreadP2PAudit() reports DM thread topic safety (pearcord:dm-thread:*, no central URL). See DM_THREADS.md.
Phase 656 (v0.8.631): Live DM voice via pearcord-dm-voice, group add/remove member, dmCall view, mutual guild counts. See DM_VOICE.md.
Phase 656 (v0.8.630): DmChannelMetaStore via _ensureDmMetaStore, globalPresence + dmMetaEngine in view, _applyGlobalPresenceMeshFromPrefs. See GLOBAL_PRESENCE.md and DM_INBOX.md.
Phase 655 (v0.8.629): DM _pinDmMessage / _unpinDmMessage, DM reaction gossip, pins in view for mode=dm, dm.open heal metadata.
Phase 655 (v0.8.628): renameGroupDM, startDmVoiceCall / startDmVideoCall (P2P stubs + spans), dm.open sidebarFilterActive metadata. See DM_INBOX.md.
Phase 654 (50 swarm/repl/sync items): Global mesh resilience — adaptive peer retry + discovery refresh aid (P654-1), improved backoff using swarm adaptive (P654-26 area), replicator joiner attach work, auto-heal triggers, full CRDT slices, diagnostics.
Mission
Expose a single EventEmitter API the UI drives over IPC (apps/pearcord/index.js), while owning session lifecycle (onboarding, guild/DM mode, active channel), permission checks, gossip bridging, and lazy initialization of feature registries (emoji, stickers, automation, search, …).
When to use / not
Use when:
- Building the Pearcord app, companion read-only client, or headless smoke that needs full session semantics.
- You want one import instead of composing
pearcord-identity+pearcord-guild+pearcord-messageyourself.
Do not use when:
- Writing a minimal library that only needs constants (
pearcord-shared) or raw DB access (pearcord-db). - Implementing a single feature in isolation — prefer the focused module (
pearcord-voice,pearcord-discovery, …) and optional thin adapter.
Public API
| Export | Role |
|---|---|
PearcordPlatform |
Main facade (~9k lines) |
USER_STATUS |
Re-exported from pearcord-shared for UI |
Session & identity
| Method / property | Role |
|---|---|
ready() |
identity.ready(), channel descriptions, invite/attachments bootstrap if onboarded |
register({ username, displayName }) |
_bootstrapAfterUser |
importIdentityBundle(bundle) |
Device pair restore |
onboarded, identity, storagePath, db, dbPath |
Session state |
listGuilds(), loadGuild(guildId), createGuild({ name, publicListing? }), deleteGuild({ confirmName? }), joinInvite(code) |
Guild session; owner-only deleteGuild gossips GUILD_DELETE, revokes discovery, purges local data (GUILD_DELETE.md) |
v0.8.610 (Phase 638): Explore joinPublicDiscovery awaits guildSyncHealth settle (PEARCORD_DISCOVERY_JOIN_SYNC_WAIT_MS, span discovery.join.sync-wait), refreshes discovery mesh listings, mesh peer-drop recovery (guild.mesh.recover), gossip flush before sync request. View: discoveryJoinSyncPending, discoveryJoinSyncPct, discoveryPostJoinRefreshAt. Bundle: npm run test:phase638-discovery-sync.
v0.8.611 (Phase 639): Settings mesh health gossip (RPC.GUILD_SYNC_HEALTH, SETTINGS_READ_RECEIPT_SYNC), syncSettingsMesh, getSettingsMeshStats, view.settingsMeshStats, debounced health push on _patchGuildSyncHealth, cross-device read receipt broadcast on markChannelRead. Span settings.mesh.health-gossip. Bundle: npm run test:phase639-settings-mesh.
v0.8.612 (Phase 640): pearcord-guild-sidecar audit cursors on GUILD_SYNC.auditSidecar, exportAuditSidecar, migrateGuildMsgIndex, PRESENCE_VECTOR_SYNC (96) settings mesh, view.auditSidecar + read receipt sync rows. Span guild.audit.sidecar. Bundle: npm run test:phase640-audit-sidecar.
v0.8.613 (Phase 641): Mesh stability — requestGuildSyncBurst, runOfflineRecoverySync, clearGuildSyncPushHalt, bandwidth fair-share (PEARCORD_GUILD_SYNC_FAIR_SHARE), detectMsgIndexMigrationNeeded, opt-in PEARCORD_GUILD_AUTO_MIGRATE_MSG_INDEX, diagnostics bandwidthFairShareCap + sparseAckCursors, span guild.mesh.stability. Bundle: npm run test:phase641-mesh-stability. See MESH_STABILITY.md.
v0.8.614 (Phase 642): Sparse ACK QoS — RTT-adaptive bandwidth cap, defer retry jitter, getMeshStabilityStats, span guild.sync.sparse-qos, gossip outbox drop metrics, push halt clear on peer leave, sidecar lastPartialDeltaAt, UI sparse ACK rows + bandwidth sparkline, IPC run-offline-recovery-sync, ⌘⇧B shortcut. Bundle: npm run test:phase642-sparse-qos. See SPARSE_ACK.md.
v0.8.609 (Phase 637): Sparse ACK batch pulls (PEARCORD_GUILD_SYNC_SPARSE_BATCH), per-channel sparse cursors, eager Hypercore open, gossip outbox priority lanes, stageRoles[] sync slice, member churn delta, visible-only reaction export, search index version bump on delta ingest. Bundle: npm run test:phase637-guild-sync.
v0.8.624 (Phase 652): guild.mesh.churn.scale span, topic pool cap logging, federation aggregate heal refresh, hub-ingest composer focus, exportMeshReplicationDiagnostics IPC path. See MESH_CHURN_SCALE.md. Bundle: npm run test:phase652-live-mesh-churn.
v0.8.623 (Phase 651): GuildTopicPool peer cap per guild topic, exportMeshReplicationDiagnostics, scoped discovery on partition heal/offline recovery (_refreshDiscoveryAfterGuildMeshAction), gossip lane counts in mesh stats. See SWARM_REPLICATION.md. Bundle: npm run test:phase651-swarm-replication.
v0.8.622 (Phase 650): Discovery listing LWW (mergeDiscoveryListingLWW), scoped Explore refresh (refreshDiscoveryExploreScoped), hub ingest span discovery.mesh.federation.hub-ingest, discoveryMeshFlushListings, listing TTL chip in server settings, async exportFederationSyncDiagnostics with hub subs. Bundle: npm run test:phase650-live-federation-mesh.
v0.8.621 (Phase 649): Federation/discovery depth — refreshDiscoveryListingForGuild, listing TTL refresh on loadGuild focus (debounced, no global mesh stall), exportFederationSyncDiagnostics, view.federationSyncAggregate, span discovery.mesh.federation, dev-log federation-sync. See FEDERATION_MESH.md. Bundle: npm run test:phase649-federation-discovery.
v0.8.620 (Phase 648): Per-guild mesh bounded + partition heal isolation (_guildMeshBoundedByGuild, _partitionHealCooldownUntilByGuild), exportMultiGuildSyncDiagnostics, guildSyncHealthRail in view, span guild.mesh.multi-scale. See MULTI_GUILD_MESH.md. Bundle: npm run test:phase648-multi-guild-mesh.
v0.8.619 (Phase 647): Partition heal cooldown (PEARCORD_PARTITION_HEAL_COOLDOWN_MS), exportPartitionHealDiagnostics, heal durationMs on span/log, throttled heal return. See PRODUCTION_MESH.md. Bundle: npm run test:phase647-production-mesh.
v0.8.618 (Phase 646): runPartitionHealSync — CRDT partition heal (presence at tie-break, read receipts, sparse cursors, sidecar, member page resume). Span guild.mesh.partition-heal. See PARTITION_HEAL.md. Bundle: npm run test:phase646-partition-heal.
v0.8.617 (Phase 645): Large guild partial roster — getMemberRosterSyncProgress, member page burst limiter, roster-scaled bandwidth, ingest roster health patch, seedGuildMembersForDiagnostics, diagnostics memberSync. See LARGE_GUILD_MESH.md. Bundle: npm run test:phase645-large-guild-mesh.
v0.8.608 (Phase 636): readReceiptVector in GUILD_SYNC (LWW vs message tail), threadArchiveSlice, forumTagPalettes, voice/forum ingest merge, member page debounce (PEARCORD_GUILD_SYNC_MEMBER_PAGE_DEBOUNCE_MS), RTT-scaled zlib threshold, push peer round-robin slot on health, span guild.sync.member-page. Bundle: npm run test:phase636-guild-sync.
v0.8.607 (Phase 635): Partial member roster + MEMBER_PAGE_REQUEST/MEMBER_PAGE (RPC 92/93), attachment priority queue, RTT-adaptive sync bursts, forumIndex/voiceOccupancy slices, thread parent idempotency, span guild.sync.attach, ingest timing on guildSyncHealth. Env: PEARCORD_GUILD_SYNC_MEMBER_CAP, PEARCORD_GUILD_SYNC_MEMBER_PAGE_SIZE. Bundle: npm run test:phase635-guild-sync.
v0.8.606 (Phase 634): GUILD_SYNC attachment pull scheduler, bandwidth bytes/sec cap, HyperDB uint sanitization on messages/members, delta reaction tombstones, pins[] bundle slice, contacts presence reconcile, span guild.sync.presence. Env: PEARCORD_GUILD_SYNC_BYTES_PER_SEC, PEARCORD_GUILD_ATTACHMENT_PULL_CAP. Bundle: npm run test:phase634-guild-sync. See GUILD_REPLICATION.md.
v0.8.431 (Phase 468): dm.open span.end includes group, peerUserId, participantCount; discovery.list span.end includes filterTag; contacts.accept span.end includes peerUserId; dm.open/contacts.accept/discovery.list error logs use error: key. Bundle: npm run test:phase468-discovery-dm. See DISCOVERY.md, CONTACTS.md.
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, STAGE_CHANNELS.md.
v0.8.454 (Phase 491): automation.hook / automation.manifest / automation.hook.dryRun spans + errors. UI automation compositor + hints. Bundle: npm run test:phase491-automation. See AUTOMATION_EXPORT.md.
v0.8.453 (Phase 490): bot.token/bot.verify/bot.install/bot.message span metadata extend. UI bot compositor + hints. Bundle: npm run test:phase490-bots. See BOTS.md.
v0.8.452 (Phase 489): slash.register customCount; slash.delete deleted; slash.invoke replyLen/commandFound. UI slash compositor + hints. Bundle: npm run test:phase489-slash. See SLASH_COMMANDS.md.
v0.8.510 (Phase 547): DM spans extend — activeChannelId on dm.send and dm.group.create ends; split DMs panel compositor; clipboard dmNewDmModalBusy. Bundle: npm run test:phase547-dms. See DM.md.
v0.8.509 (Phase 546): Contacts spans extend — contacts.block/contacts.unblock spans; activeChannelId on request/accept/decline/block/unblock ends; split contacts panel compositor; contacts-errors includes block/unblock. Bundle: npm run test:phase546-contacts. See CONTACTS.md.
v0.8.508 (Phase 545): Discovery spans extend — discovery.prune span; activeChannelId on discovery.list/publish/mesh/prune ends; split discovery panel compositor; discovery-errors includes prune. Bundle: npm run test:phase545-discovery. See DISCOVERY.md.
v0.8.507 (Phase 544): Presence spans extend — presence.image span for activity artwork; activeChannelId on presence.set/presence.activity/presence.image ends; split presence panel compositor; presence-errors includes image. Bundle: npm run test:phase544-presence. See PRESENCE.md.
v0.8.528 (Phase 565): DM UI live region #dm-panel-live; dm-errors excludes voice/stage/screen/soundboard/presence/discovery/contacts bleed; clipboard dmPanelLiveBusy/channelDmConversationCount. Bundle: npm run test:phase565-dms. See DM.md.
v0.8.560 (Phase 597): Session hub live region #session-hub-panel-live; session-errors excludes audit/compliance/slash/webhook/bot/automation/attachment/embed/message/reaction/pin/search/voice/stage/screen/soundboard/presence/discovery/contacts/dm/settings/invite bleed; clipboard sessionHubPanelLiveBusy; session.startup/guild.open/identity.register span cross-check. Bundle: npm run test:phase597-session. See SESSION.md. Settings hub depth: see SETTINGS.md (Phase 598 — complete).
v0.8.590 (Phase 627): Search hub depth — #search-hub-panel-live / #search-panel-live active #channelName + mesh notes; search-errors excludes reaction bleed; syncComposerSearchHint dual hub/panel refresh; guild.search.cache / guild.search.mesh span activeChannelId re-verify. Bundle: npm run test:phase627-search. See SEARCH.md. Pins hub depth: Phase 628 — planned.
v0.8.589 (Phase 626): Reaction hub depth — #reaction-hub-panel-live hub mesh note + #channelName context; reaction-errors excludes thread bleed; syncComposerReactionHint dual hub/panel refresh; reaction.list/reaction.gossip span activeChannelId re-verify. Bundle: npm run test:phase626-reactions. See REACTIONS.md.
v0.8.588 (Phase 625): Threads hub depth — #threads-hub-panel-live hub mesh note + #channelName context; thread-errors excludes expression bleed; syncComposerThreadHint dual hub/panel refresh; thread.read span activeChannelId/guildCount. Bundle: npm run test:phase625-threads. See THREADS_PANEL.md.
v0.8.587 (Phase 624): Expression hub depth — #expression-hub-panel-live hub mesh note + #channelName context; expression-errors excludes thread bleed; syncComposerExpressionHint dual hub/panel refresh. Bundle: npm run test:phase624-expression. See EMOJI.md.
v0.8.586 (Phase 623): Stickers hub depth — #sticker-hub-panel-live hub mesh note + #channelName context; sticker-errors excludes thread bleed; syncComposerStickerHint dual hub/panel refresh. Bundle: npm run test:phase623-stickers. See STICKERS.md. Expression hub depth: see EMOJI.md (Phase 624 — complete).
v0.8.583 (Phase 620): Bots hub depth — #bots-hub-panel-live hub mesh note + active channel context; bot-errors excludes thread bleed; syncComposerBotHint dual hub/panel refresh. Bundle: npm run test:phase620-bots. See BOTS.md. Automation hub depth: see AUTOMATION_EXPORT.md (Phase 621 — complete).
v0.8.582 (Phase 619): Slash hub depth — #slash-hub-panel-live hub mesh note + active channel context; slash-errors excludes thread bleed; syncComposerSlashHint dual hub/panel refresh. Bundle: npm run test:phase619-slash. See SLASH_COMMANDS.md. Bots hub depth: see BOTS.md (Phase 620 — complete).
v0.8.581 (Phase 618): Webhooks hub depth — #webhooks-hub-panel-live hub mesh note + active channel hook count; webhook-errors excludes thread bleed; syncComposerWebhooksHint dual hub/panel refresh. Bundle: npm run test:phase618-webhooks. See WEBHOOKS.md. Slash hub depth: see SLASH_COMMANDS.md (Phase 619 — complete).
v0.8.580 (Phase 617): Compliance hub depth — #compliance-hub-panel-live hub mesh note + channel context; compliance-errors excludes thread bleed; syncComposerComplianceHint dual hub/panel refresh. Bundle: npm run test:phase617-compliance. See COMPLIANCE.md. Webhooks hub depth: see WEBHOOKS.md (Phase 618 — complete).
v0.8.579 (Phase 616): Audit hub depth — #audit-hub-panel-live hub mesh note + channel context; audit-errors excludes thread bleed; syncComposerAuditHint dual hub/panel refresh. Bundle: npm run test:phase616-audit. See AUDIT.md. Compliance hub depth: see COMPLIANCE.md (Phase 617 — complete).
v0.8.578 (Phase 615): Moderation hub depth — #moderation-hub-panel-live hub mesh note + channel context; moderation-errors excludes thread bleed; syncComposerModerationHint dual hub/panel refresh. Bundle: npm run test:phase615-moderation. See MODERATION.md. Audit hub depth: see AUDIT.md (Phase 616 — complete).
v0.8.577 (Phase 614): Pin hub depth — syncPinLivePanelDuringGuildLoading; #pin-hub-panel-live hub depth copy + mesh note; pin-errors excludes thread/automod/ban/kick/timeout bleed; clipboard pinPanelLiveBusy; pin.message/pin.unpin activeChannelId re-verify. Bundle: npm run test:phase614-pins. See PINS.md. Moderation hub depth: see MODERATION.md (Phase 615 — complete).
v0.8.576 (Phase 613): Search hub depth — syncSearchLivePanelDuringGuildLoading; #search-hub-panel-live hub depth copy + mesh note; search-errors excludes pin/thread/automod/ban/kick/timeout bleed; clipboard searchPanelLiveBusy; guild.search activeChannelId re-verify. Bundle: npm run test:phase613-search. See SEARCH.md. Pins hub depth: see PINS.md (Phase 614 — complete).
v0.8.575 (Phase 612): Reaction hub depth — syncReactionLivePanelDuringGuildLoading; #reaction-hub-panel-live hub depth copy + aria-busy; reaction-errors excludes pin/automod/ban/kick/timeout bleed; clipboard reactionPanelLiveBusy; reaction.toggle/reaction.clear activeChannelId re-verify. Bundle: npm run test:phase612-reactions. See REACTIONS.md.
v0.8.574 (Phase 611): Threads hub live region depth — #threads-panel-live + syncThreadsLivePanelDuringGuildLoading; #threads-hub-panel-live aria-busy; thread-errors excludes attachment/embed/message/reaction/pin/automod/ban/kick/timeout bleed; clipboard threadsPanelLiveBusy; thread.create/thread.archive span guildCount/activeChannelId extend. Bundle: npm run test:phase611-threads. See THREADS_PANEL.md.
v0.8.573 (Phase 610): Expression hub live region depth — #expression-panel-live + syncExpressionLivePanelDuringGuildLoading; #expression-hub-panel-live aria-busy; unified expression-errors excludes attachment/embed/message/pin/automod/ban/kick/timeout bleed; clipboard expressionPanelLiveBusy; emoji.create/sticker.send/reaction.toggle span cross-check. Bundle: npm run test:phase610-expression. See EMOJI.md.
v0.8.572 (Phase 609): Stickers hub live region depth — #sticker-panel-live + syncStickerLivePanelDuringGuildLoading; #sticker-hub-panel-live aria-busy; sticker-errors excludes attachment/embed/message/reaction/pin/automod/ban/kick/timeout bleed; clipboard stickerPanelLiveBusy; sticker.create/sticker.delete/sticker.stage/sticker.send span cross-check. Bundle: npm run test:phase609-stickers. See STICKERS.md.
v0.8.571 (Phase 608): Emoji hub live region depth — #emoji-panel-live + syncEmojiLivePanelDuringGuildLoading; #emoji-hub-panel-live aria-busy; emoji-errors excludes attachment/embed/message/reaction/pin/automod/ban/kick/timeout bleed; clipboard emojiPanelLiveBusy; emoji.create/emoji.delete/emoji.stage span cross-check. Bundle: npm run test:phase608-emoji. See EMOJI.md.
v0.8.570 (Phase 607): Automation hub live region depth — #automation-panel-live + syncAutomationLivePanelDuringGuildLoading; #automation-hub-panel-live aria-busy; automation-errors excludes attachment/embed/message/reaction/pin/automod/ban/kick/timeout bleed; clipboard automationPanelLiveBusy; automation.hook/automation.manifest/automation.dispatch span cross-check. Bundle: npm run test:phase607-automation. See AUTOMATION_EXPORT.md.
v0.8.569 (Phase 606): Bots hub live region depth — #bots-panel-live + syncBotsLivePanelDuringGuildLoading; #bots-hub-panel-live aria-busy; bot-errors excludes automation/attachment/embed/message/reaction/pin/automod/ban/kick/timeout bleed; clipboard botsPanelLiveBusy; bot.install/bot.verify/bot.message span cross-check. Bundle: npm run test:phase606-bots. See BOTS.md.
v0.8.568 (Phase 605): Slash hub live region depth — #slash-panel-live + syncSlashLivePanelDuringGuildLoading; #slash-hub-panel-live aria-busy; slash-errors excludes automation/attachment/embed/message/reaction/pin/automod/ban/kick/timeout bleed; clipboard slashPanelLiveBusy; slash.register/slash.delete/slash.invoke span cross-check. Bundle: npm run test:phase605-slash. See SLASH_COMMANDS.md. Bots hub depth: see BOTS.md (Phase 606 — planned).
v0.8.567 (Phase 604): Webhooks hub live region depth — #webhooks-panel-live + syncWebhooksLivePanelDuringGuildLoading; #webhooks-hub-panel-live aria-busy; webhook-errors excludes automation/attachment/embed/message/reaction/pin/automod/ban/kick/timeout bleed; clipboard webhooksPanelLiveBusy; webhook.create/webhook.delete/webhook.execute span cross-check. Bundle: npm run test:phase604-webhooks. See WEBHOOKS.md.
v0.8.566 (Phase 603): Compliance hub live region depth — #compliance-panel-live + syncComplianceLivePanelDuringGuildLoading; #compliance-hub-panel-live aria-busy; compliance-errors excludes slash/webhook/bot/automation/attachment/embed/message/reaction/pin/search/audit bleed; clipboard compliancePanelLiveBusy; compliance.snapshot/compliance.archive/compliance.export/digest.schedule span cross-check. Bundle: npm run test:phase603-compliance. See COMPLIANCE.md.
v0.8.565 (Phase 602): Audit hub live region depth — #audit-panel-live + syncAuditLivePanelDuringGuildLoading; #audit-hub-panel-live aria-busy; audit-errors excludes slash/webhook/bot/automation/attachment/embed/message/reaction/pin/search bleed; clipboard auditPanelLiveBusy; audit.append/audit.export/audit.schedule span cross-check. Bundle: npm run test:phase602-audit. See AUDIT.md.
v0.8.564 (Phase 601): Moderation hub live region depth — #moderation-panel-live + syncModerationLivePanelDuringGuildLoading; #moderation-hub-panel-live aria-busy; moderation-errors excludes compliance/slash/webhook/bot/automation/attachment/embed/message/reaction/pin/search bleed; clipboard moderationPanelLiveBusy; ban.member/kick.member/timeout.member/automod.setConfig span cross-check. Bundle: npm run test:phase601-moderation. See MODERATION.md.
v0.8.563 (Phase 600): Roles hub live region depth — #roles-panel-live + syncRolesLivePanelDuringGuildLoading; #roles-hub-panel-live aria-busy; role-errors excludes audit/compliance/slash/webhook/bot/automation/attachment/embed/message/reaction/pin/search/permission/moderation/session/settings bleed; clipboard rolesPanelLiveBusy; role.create/role.update/role.delete/role.reorder/role.member span cross-check. Bundle: npm run test:phase600-roles. See ROLES.md.
v0.8.562 (Phase 599): Permissions hub live region #permissions-hub-panel-live; permission-errors excludes audit/compliance/slash/webhook/bot/automation/attachment/embed/message/reaction/pin/search/role/moderation/session/settings bleed; clipboard permissionsHubPanelLiveBusy; role.create/role.update/permission.overwrite/permission.eval span cross-check. Bundle: npm run test:phase599-permissions. See PERMISSIONS.md. Roles hub depth: see ROLES.md (Phase 600 — complete).
v0.8.561 (Phase 598): Settings hub live region #settings-hub-panel-live; settings-errors excludes audit/compliance/slash/webhook/bot/automation/attachment/embed/message/reaction/pin/search/voice/stage/screen/soundboard/presence/discovery/contacts/dm/session/invite bleed; clipboard settingsHubPanelLiveBusy; guildCount on settings.guild span end; settings.prefs appearance metadata (themeChanged/densityChanged). Bundle: npm run test:phase598-settings. See SETTINGS.md.
v0.8.559 (Phase 596): Onboarding hub live region #onboarding-hub-panel-live; onboarding-errors excludes audit/compliance/slash/webhook/bot/automation/attachment/embed/message/reaction/pin/search/voice/stage/screen/soundboard/presence/discovery/contacts/dm/settings/invite bleed; clipboard onboardingHubPanelLiveBusy; guildCount on guild.create span end. Bundle: npm run test:phase596-onboarding. See ONBOARDING.md.
v0.8.558 (Phase 595): DM hub live region #dm-hub-panel-live; dm-errors excludes audit/compliance/slash/webhook/bot/automation/attachment/embed/message/reaction/pin/search/voice/stage/screen/soundboard/presence/discovery/contacts/settings/invite bleed; clipboard dmHubPanelLiveBusy; guildCount on dm.open/dm.group.create span ends. Bundle: npm run test:phase595-dms. See DM.md.
v0.8.557 (Phase 594): Contacts hub live region #contacts-hub-panel-live; contacts-errors excludes audit/compliance/slash/webhook/bot/automation/attachment/embed/message/reaction/pin/search/voice/stage/screen/soundboard/presence/discovery bleed; clipboard contactsHubPanelLiveBusy; guildCount on contacts.request/accept/decline/block/unblock span ends. Bundle: npm run test:phase594-contacts. See CONTACTS.md.
v0.8.527 (Phase 564): Contacts UI live region #contacts-panel-live; contacts-errors excludes voice/stage/screen/soundboard/presence/discovery bleed; clipboard contactsPanelLiveBusy/channelContactsAcceptedCount. Bundle: npm run test:phase564-contacts. See CONTACTS.md.
v0.8.556 (Phase 593): Discovery hub live region #discovery-hub-panel-live; discovery-errors excludes audit/compliance/slash/webhook/bot/automation/attachment/embed/message/reaction/pin/search/voice/stage/screen/soundboard/presence bleed; clipboard discoveryHubPanelLiveBusy; guildCount on discovery.list/join/filter/mesh/publish/prune span ends; new discovery.filter span. Bundle: npm run test:phase593-discovery. See DISCOVERY.md.
v0.8.526 (Phase 563): Discovery UI live region #discovery-panel-live; discovery-errors excludes voice/stage/screen/soundboard/presence bleed; clipboard discoveryPanelLiveBusy/channelDiscoveryListingCount. Bundle: npm run test:phase563-discovery. See DISCOVERY.md.
v0.8.555 (Phase 592): Presence hub live region #presence-hub-panel-live; presence-errors excludes audit/compliance/slash/webhook/bot/automation/attachment/embed/message/reaction/pin/search/voice/stage/screen/soundboard bleed; clipboard presenceHubPanelLiveBusy; guildCount on presence.set/activity/custom/image span ends. Bundle: npm run test:phase592-presence. See PRESENCE.md.
v0.8.525 (Phase 562): Presence UI live region #presence-panel-live; presence-errors excludes voice/stage/screen/soundboard bleed; clipboard presencePanelLiveBusy/channelPresencePeerCount. Bundle: npm run test:phase562-presence. See PRESENCE.md.
v0.8.554 (Phase 591): Soundboard hub live region #soundboard-hub-panel-live; soundboard-errors excludes audit/compliance/slash/webhook/bot/automation/attachment/embed/message/reaction/pin/search/voice/stage/screen bleed; clipboard soundboardHubPanelLiveBusy; guildCount on soundboard.play/upload/delete/stage span ends. Bundle: npm run test:phase591-soundboard. See SOUNDBOARD.md.
v0.8.524 (Phase 561): Soundboard UI live region #soundboard-panel-live; soundboard-errors excludes voice/stage/screen bleed; clipboard soundboard live meta. Bundle: npm run test:phase561-soundboard. See SOUNDBOARD.md.
v0.8.506 (Phase 543): Soundboard spans extend — soundboard.stage span; activeChannelId on play/upload/delete/stage ends; upload panel guild-loading guard; soundboard-errors includes stage. Bundle: npm run test:phase543-soundboard. See SOUNDBOARD.md.
v0.8.544 (Phase 581): Reaction spans extend — guildCount on reaction.toggle/reaction.gossip/reaction.list/reaction.clear. UI #expression-hub-panel-live + unified expression-errors filter; clipboard expressionHubPanelLiveBusy/channelExpressionEmojiCount/channelExpressionStickerCount. Bundle: npm run test:phase581-expression. See REACTIONS.md.
v0.8.543 (Phase 580): Sticker spans extend — guildCount on sticker.create/sticker.delete/sticker.stage/sticker.send. UI #sticker-hub-panel-live + syncStickerHubLivePanelDuringGuildLoading; sticker-errors excludes audit/compliance/slash/webhook/bot/automation/emoji bleed; clipboard stickerHubPanelLiveBusy/channelStickerCount. Bundle: npm run test:phase580-stickers. See STICKERS.md.
v0.8.505 (Phase 542): Sticker spans extend — sticker.create/sticker.delete/sticker.stage/sticker.send with spanKind + activeChannelId. UI split sticker compositor panels, lastStickerTileName, dedicated sticker-errors dev-log filter. Bundle: npm run test:phase542-stickers. See STICKERS.md.
v0.8.542 (Phase 579): Emoji spans extend — guildCount on emoji.create/emoji.delete/emoji.stage. UI #emoji-hub-panel-live + syncEmojiHubLivePanelDuringGuildLoading; emoji-errors excludes audit/compliance/slash/webhook/bot/automation bleed; clipboard emojiHubPanelLiveBusy/channelEmojiCount. Bundle: npm run test:phase579-emoji. See EMOJI.md.
v0.8.504 (Phase 541): Emoji spans extend — emoji.upsert ends with spanKind: 'emoji.create'; new emoji.delete + emoji.stage spans with activeChannelId. UI split emoji compositor panels, picker tile lastEmojiTileName, clipboard emoji busy flags. Bundle: npm run test:phase541-emoji. See EMOJI.md.
v0.8.541 (Phase 578): Automation spans extend — guildCount on automation.hook/automation.dispatch/automation.manifest/automation.hook.dryRun. UI #automation-hub-panel-live + syncAutomationHubLivePanelDuringGuildLoading; automation-errors excludes audit/compliance/slash/webhook/bot bleed; clipboard automationHubPanelLiveBusy/channelAutomationHookCount. Bundle: npm run test:phase578-automation. See AUTOMATION_EXPORT.md.
v0.8.503 (Phase 540): Automation spans extend — automation.hook/automation.manifest/automation.hook.dryRun/automation.dispatch end metadata (spanKind, activeChannelId). UI automation panel compositor wrappers, hook row lastAutomationHookRowId + roving tabindex, clipboard automation busy flags. Bundle: npm run test:phase540-automation. See AUTOMATION_EXPORT.md.
v0.8.502 (Phase 539): Bot spans extend — bot.install/bot.verify/bot.message/bot.token end metadata (spanKind, activeChannelId). UI bot panel compositor wrappers, bot row lastBotRowId + roving tabindex, clipboard bot busy flags. Bundle: npm run test:phase539-bots. See BOTS.md.
v0.8.501 (Phase 538): Slash spans extend — slash.register/slash.delete/slash.invoke end metadata (spanKind, activeChannelId). UI slash panel compositor wrappers, slash row lastSlashRowId + roving tabindex, clipboard slash busy flags. Bundle: npm run test:phase538-slash. See SLASH_COMMANDS.md.
v0.8.500 (Phase 537): Webhook spans extend — webhook.create/webhook.delete/webhook.execute end metadata (spanKind, activeChannelId). UI webhook panel compositor wrappers, webhook row lastWebhookRowId + roving tabindex, clipboard webhook busy flags. Bundle: npm run test:phase537-webhooks. See WEBHOOKS.md.
v0.8.451 (Phase 488): webhook.create channelType/hasAvatar; webhook.execute messageSent. UI webhook compositor split + hints. Bundle: npm run test:phase488-webhooks. See WEBHOOKS.md.
v0.8.450 (Phase 487): audit.append hasTarget/hasDetail; audit.export archived; compliance.snapshot signed/format. UI compliance compositor. Bundle: npm run test:phase487-compliance. See COMPLIANCE.md.
v0.8.449 (Phase 486): ban.member/kick.member reasonLen/removedMember; timeout.member durationMs; automod.evaluate blocked. UI moderation compositor split + hints. Bundle: npm run test:phase486-moderation. See MODERATION.md.
v0.8.448 (Phase 485): role.create nameLen; role.update renamed/permissionsChanged; role.delete existed; permission.overwrite allowNonZero/denyNonZero. UI roles compositor split + hints. Bundle: npm run test:phase485-roles. See ROLES.md.
v0.8.447 (Phase 484): thread.create fromMessage/hasRootMessage; thread.archive wasArchived/threadName; thread.read parentId/clearedMentions; forum.post titleLen/hasTags. UI threads compositor split + hints. Bundle: npm run test:phase484-threads. See MESSAGE_THREADS.md.
v0.8.482 (Phase 519): guild.search, guild.search.cache, and guild.search.mesh spans include spanKind on start/end. UI extends search compositor (recent chips, mesh chip, panel live region). Bundle: npm run test:phase519-search. See SEARCH.md.
v0.8.462 (Phase 499): guild.search.cache span on cache hits (scope/guildId); guild.search.mesh span after mesh merge (meshHitCount/localHitCount/mergedCount). UI split search guild-loading compositor. Bundle: npm run test:phase499-search. See SEARCH.md.
v0.8.483 (Phase 520): message.edit and message.delete spans include spanKind on start/end. UI extends messaging compositor (reply bar, slowmode pill, message list live region). Bundle: npm run test:phase520-messaging. See MESSAGING.md.
v0.8.463 (Phase 500): message.send replyToId; message.edit deltaLength; message.delete hadReactions/reactionCount. UI split messaging composer guild-loading compositor. Bundle: npm run test:phase500-messaging. See MESSAGING.md.
v0.8.484 (Phase 521): reaction.toggle and reaction.gossip spans include spanKind on start/end. UI extends reaction guild-loading compositor. Bundle: npm run test:phase521-reactions. See REACTIONS.md.
v0.8.464 (Phase 501): reaction.toggle added/hadMeBefore/gossipSent; reaction.gossip span on mesh fanout. UI syncReactionPanelsDuringGuildLoading. Bundle: npm run test:phase501-reactions. See REACTIONS.md.
v0.8.485 (Phase 522): attachment.stage, attachment.read, and embed.gossip spans include spanKind on start/end. UI extends attachment guild-loading compositor. Bundle: npm run test:phase522-attachments. See ATTACHMENTS.md.
v0.8.465 (Phase 502): attachment.stage filenameLen; attachment.read attachmentId/isImage; embed.gossip span; embed.resolve gossipSent. UI split attachment guild-loading compositor. Bundle: npm run test:phase502-attachments. See ATTACHMENTS.md, EMBEDS.md.
v0.8.490 (Phase 527): soundboard.play, soundboard.upload, and soundboard.delete spans include spanKind. UI split soundboard guild-loading compositor. Bundle: npm run test:phase527-soundboard. See SOUNDBOARD.md.
v0.8.489 (Phase 526): screen.start, screen.stop, and screen.capture spans include spanKind. UI split screen guild-loading compositor. Bundle: npm run test:phase526-screen. See SCREEN_SHARE.md.
v0.8.488 (Phase 525): stage.join and stage.speaker.* spans include spanKind. UI split stage guild-loading compositor. Bundle: npm run test:phase525-stage. See STAGE.md.
v0.8.487 (Phase 524): voice.join, voice.leave, and voice.mute spans include spanKind. UI split voice guild-loading compositor (syncVoiceBarPanelDuringGuildLoading, syncVoiceRosterPanelDuringGuildLoading, syncVoiceControlsPanelDuringGuildLoading). Bundle: npm run test:phase524-voice. See VOICE.md.
v0.8.486 (Phase 523): embed.resolve and embed.gossip spans include spanKind; resolve end adds fromCache. UI extends embed guild-loading compositor. Bundle: npm run test:phase523-embeds. See EMBEDS.md.
v0.8.466 (Phase 503): embed.resolve hasDescription; embed.gossip hasTitle/hasImage/urlHost. UI syncEmbedPanelsDuringGuildLoading. Bundle: npm run test:phase503-embeds. See EMBEDS.md.
v0.8.446 (Phase 483): guild.search includeMesh/queryLen; pin.list pinCount. UI search compositor split + hints. Bundle: npm run test:phase483-search. See SEARCH.md.
v0.8.445 (Phase 482): message.send hasAttachment/hasSticker; message.edit contentChanged; message.delete wasOwn. UI messaging compositor + hints. Bundle: npm run test:phase482-messaging. See MESSAGING.md.
v0.8.444 (Phase 481): attachment.stage byteLength/isImage; attachment.read fromLocal; embed.resolve urlLen. UI attachment compositor + clipboard staging meta. Bundle: npm run test:phase481-attachments. See ATTACHMENTS.md, EMBEDS.md.
v0.8.461 (Phase 498): inbox.mark-read unreadBefore/unreadAfter; inbox.mark-all remainingUnread; notification.open jumped/mode. UI syncInboxPanelsDuringGuildLoading + inbox-errors filter. Bundle: npm run test:phase498-inbox. See INBOX.md.
v0.8.471 (Phase 508): presence.set/presence.custom/presence.activity spans extended (previousStatus, statusChanged, customTextLength, spanKind, activityName, hasAssets). Split presence guild-loading compositor + clipboard presence meta. Bundle: npm run test:phase508-presence.
v0.8.473 (Phase 510): role.update adds previousPermissionMask/permissionMaskDelta; permission.overwrite adds replacedExisting/allowChanged/denyChanged; permission.eval after overwrite upsert; permission.overwrite.delete adds hadAllow/hadDeny. UI syncPermissionsPanelsDuringGuildLoading. Bundle: npm run test:phase510-permissions.
v0.8.472 (Phase 509): discovery.list adds queryLen/sort/minMembers; discovery.publish adds beforeListed/changed; discovery.mesh adds hadCachedStats. UI split discovery guild-loading compositor + clipboard discovery meta. Bundle: npm run test:phase509-discovery.
v0.8.460 (Phase 497): presence.set/presence.custom/presence.activity spans (peerCount, activityType, guildGossip). UI presence compositor + tightened presence-errors filter. Bundle: npm run test:phase497-presence. See PRESENCE.md.
v0.8.470 (Phase 507): soundboard.play pcmBytes/peerCountInChannel/wasGossip; soundboard.upload replacedExisting; soundboard.delete hadAttachment. Split soundboard guild-loading compositor in UI. Bundle: npm run test:phase507-soundboard. See SOUNDBOARD.md.
v0.8.459 (Phase 496): soundboard.play framesQueued/remote/listenOnly; soundboard.upload soundCount/durationMs; soundboard.delete remainingSounds. UI soundboard compositor + soundboard-errors filter. Bundle: npm run test:phase496-soundboard. See SOUNDBOARD.md.
v0.8.469 (Phase 506): screen.start peerCountInChannel/displayActive; screen.capture frameBytes/captureSource; screen.stop captureSourceWas/hadCaptureLogged. Split screen guild-loading compositor in UI. Bundle: npm run test:phase506-screen. See SCREEN_SHARE.md.
v0.8.458 (Phase 495): screen.start captureSource/activeShareCount; screen.stop remainingShares; screen.capture on first display frame. UI screen compositor + screen-errors filter. Bundle: npm run test:phase495-screen. See SCREEN_SHARE.md.
v0.8.457 (Phase 494): stage.join channelType/peerCount; stage.speaker.request pendingCount; stage.speaker.approve approvedFromPending/speakerCount; stage.speaker.demote demotedFromSpeaker. UI stage compositor + stage-errors filter. Bundle: npm run test:phase494-stage. See STAGE.md.
v0.8.468 (Phase 505): stage.join joinedFresh/switchedStage/wasInVoice; stage.speaker.request wasAudience; stage.speaker.demote speakerCountAfter/pendingCountAfter. UI split stage guild-loading compositor. Bundle: npm run test:phase505-stage. See STAGE.md.
v0.8.467 (Phase 504): voice.join listenOnlyRequested/joinedFresh; voice.leave peerCountAtLeave; voice.mute span + error log (wasMuted/wasDeafened). UI voice-errors filter: voice.join|leave|mute. Bundle: npm run test:phase504-voice. See VOICE.md.
v0.8.456 (Phase 493): voice.join channelType/peerCount; voice.leave listenOnlyWas; stage.join isModerator; screen.start wasSharing/listenOnly; screen.stop hadActive. Split voice guild-loading compositor. Bundle: npm run test:phase493-voice. See VOICE.md.
v0.8.443 (Phase 480): voice.join wasInVoice/switchedChannel; voice.leave hadMedia; screen.start hasLabel; screen.stop stopped. UI voice compositor. Bundle: npm run test:phase480-voice. See VOICE.md.
v0.8.455 (Phase 492): discovery.publish on setGuildDiscoveryListing (hasInvite, memberCount); discovery.mesh on stats refresh; discovery.list logs cache hits (fromCache: true); discovery.join span.fail. UI split discovery compositor + discovery-errors dev-log filter. Bundle: npm run test:phase492-discovery. See DISCOVERY.md.
v0.8.442 (Phase 479): discovery.list meshLive/filteredCount; discovery.join on joinPublicDiscovery; dm.open reusedChannel; contacts.accept accepted. UI discovery compositor. Bundle: npm run test:phase479-discovery. See DISCOVERY.md.
v0.8.441 (Phase 478): notification.markRead wasUnread/hadMessageId; notification.markAll unreadBefore; notification.open hasMessageId; inbox.mark-read clearedInboxCount. UI syncNotificationPanelsDuringGuildLoading compositor. Bundle: npm run test:phase478-notifications. See NOTIFICATIONS.md, INBOX.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, 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, 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, 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, REACTIONS.md.
v0.8.427 (Phase 464): role.update span.end includes name; permission.overwrite* span.end includes targetType. Bundle: npm run test:phase464-roles-permissions. See ROLES.md, PERMISSIONS.md.
v0.8.426 (Phase 463): audit.append, audit.export (end metadata), compliance.snapshot spans with guildId. Bundle: npm run test:phase463-audit-compliance. See AUDIT.md, COMPLIANCE.md.
v0.8.425 (Phase 462): automod.evaluate on send; ban.member/kick.member/timeout.member re-verified with guildId. Bundle: npm run test:phase462-moderation-automod. See MODERATION.md, AUTOMOD.md.
v0.8.424 (Phase 461): slash.invoke span includes guildId/channelId; bot.message + bot.install spans with guildId. Bundle: npm run test:phase461-slash-bots. See SLASH_COMMANDS.md, BOTS.md.
v0.8.423 (Phase 460): embed.resolve span uses active guildId/channelId fallback; attachment.stage/attachment.read re-verified. Bundle: npm run test:phase460-attachments-embeds. See ATTACHMENTS.md, EMBEDS.md.
v0.8.422 (Phase 459): emoji.upsert + sticker.create spans include guildId + span.fail; reaction.toggle error log includes guildId/channelId (re-verified). Bundle: npm run test:phase459-reactions-emojis. See REACTIONS.md, EMOJI.md.
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, 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.
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.
v0.8.540 (Phase 577): Bot spans extend — guildCount on bot.install/bot.verify/bot.message. UI #bots-hub-panel-live + syncBotsHubLivePanelDuringGuildLoading; bot-errors excludes audit/compliance/slash/webhook bleed; clipboard botsHubPanelLiveBusy/channelBotCount. Bundle: npm run test:phase577-bots. See BOTS.md.
v0.8.539 (Phase 576): Slash spans extend — guildCount on slash.register/slash.delete/slash.invoke. UI #slash-hub-panel-live + syncSlashHubLivePanelDuringGuildLoading; slash-errors excludes audit/compliance/webhook/bot bleed; clipboard slashHubPanelLiveBusy/channelCustomSlashCount. Bundle: npm run test:phase576-slash. See SLASH_COMMANDS.md.
v0.8.538 (Phase 575): Webhook spans extend — guildCount on webhook.create/webhook.delete/webhook.execute. UI #webhooks-hub-panel-live + syncWebhooksHubLivePanelDuringGuildLoading; webhook-errors excludes audit/compliance/slash bleed; clipboard webhooksHubPanelLiveBusy/channelWebhookCount. Bundle: npm run test:phase575-webhooks. See WEBHOOKS.md.
v0.8.537 (Phase 574): Compliance spans extend — guildCount on compliance.snapshot; new compliance.archive span on export archive append; new digest.schedule span on setDigestExportSchedule. UI #compliance-hub-panel-live + syncComplianceHubLivePanelDuringGuildLoading; compliance-errors excludes audit/moderation bleed; clipboard complianceHubPanelLiveBusy/channelComplianceArchiveCount. Bundle: npm run test:phase574-compliance. See COMPLIANCE.md.
v0.8.536 (Phase 573): Audit spans extend — guildCount on audit.append/audit.export/audit.schedule; new audit.schedule span + error logging on setAuditExportSchedule. UI #audit-hub-panel-live + syncAuditHubLivePanelDuringGuildLoading; audit-errors excludes moderation bleed; clipboard auditHubPanelLiveBusy/auditExportChromeBusy/channelAuditEntryCount. Bundle: npm run test:phase573-audit. See AUDIT.md.
v0.8.535 (Phase 572): Moderation spans extend — guildCount on ban.member/kick.member/timeout.member/automod.setConfig. UI #moderation-hub-panel-live + syncModerationHubLivePanelDuringGuildLoading; moderation-errors excludes role/permission/session/automod/audit bleed; clipboard moderationHubPanelLiveBusy/moderationKickChromeBusy/channelModerationBanCount. Bundle: npm run test:phase572-moderation. See MODERATION.md.
v0.8.534 (Phase 571): Role spans extend — guildCount on role.create/role.delete/role.reorder; role.member spanKind/activeChannelId/assignedRoleCount/customRoleCount. UI #roles-hub-panel-live + syncRolesHubLivePanelDuringGuildLoading; role-errors excludes permission bleed; clipboard rolesHubPanelLiveBusy/rolesReorderChromeBusy/channelCustomRoleCount. Bundle: npm run test:phase571-roles. See ROLES.md.
v0.8.533 (Phase 570): Permission spans extend — role.update/permission.overwrite/permission.eval guildCount; permission.eval spanKind/activeChannelId. UI #permissions-panel-live + syncPermissionsLivePanelDuringGuildLoading; permission-errors/role-errors exclude session/settings/invite bleed; clipboard permissionsPanelLiveBusy/permissionsRoleChromeBusy/channelPermissionOverwriteCount. Bundle: npm run test:phase570-permissions. See PERMISSIONS.md.
v0.8.532 (Phase 569): Session spans extend — session.startup spanKind/activeChannelId; guild.open memberCount/guildCount/channelCount/spanKind. UI #session-panel-live + syncSessionLivePanelDuringGuildLoading; session-errors excludes settings/invite bleed; clipboard sessionPanelLiveBusy/sessionBootChromeBusy/channelSessionGuildCount. Bundle: npm run test:phase569-session. See SESSION.md.
v0.8.530 (Phase 567): Onboarding spans extend — identity.register/guild.create/invite.join activeChannelId; guild.create/invite.join textChannelCount. UI #onboarding-panel-live + syncOnboardingLivePanelDuringGuildLoading; onboarding-errors excludes dm/contacts/discovery/invites bleed; clipboard onboardingPanelLiveBusy/onboardingRailChromeBusy/channelOnboardingGuildCount. Bundle: npm run test:phase567-onboarding. See ONBOARDING.md.
v0.8.474 (Phase 511): Onboarding spans — identity.register (usernameLen, displayNameLen, hadGuilds), guild.create (channelCount, isFirstGuild, listOnDiscovery), invite.join (inviteLen, guildId) + matching error logs. UI syncOnboardingPanelsDuringGuildLoading, #composer-onboarding-hint, dev-log onboarding-errors + clipboard meta. Bundle: npm run test:phase511-onboarding. See ONBOARDING.md.
v0.8.531 (Phase 568): Settings spans extend — settings.prefs/notifications.prefs/guild.update activeChannelId + guildCount; UI #settings-panel-live + syncSettingsLivePanelDuringGuildLoading; settings-errors excludes onboarding/invite bleed; clipboard settingsPanelLiveBusy/settingsNavChromeBusy/channelSettingsGuildCount. Bundle: npm run test:phase568-settings. See SETTINGS.md.
v0.8.475 (Phase 512): Settings spans — settings.prefs (patchKeyCount, themeChanged), notifications.prefs, guild.update / settings.guild (nameChanged, iconChanged, nsfwGateChanged). UI syncSettingsPanelsDuringGuildLoading, #composer-settings-hint, dev-log settings-errors + clipboard meta. Bundle: npm run test:phase512-settings. See SETTINGS.md.
v0.8.476 (Phase 513): Guild-admin spans — channel.create (nameLen, hadParent), channel.delete (wasActive, channelType), channel.reorder (updatedCount, channelCount). UI syncGuildAdminPanelsDuringGuildLoading, #composer-guild-admin-hint, dev-log guild-admin-errors + clipboard meta. Bundle: npm run test:phase513-guild-admin. See CHANNELS.md.
v0.8.479 (Phase 516): Contact spans — contacts.request (peerDisplayNameLen), contacts.accept (accepted), contacts.decline (declined). UI syncContactsPanelsDuringGuildLoading, contacts-errors filter, clipboard contacts meta. Bundle: npm run test:phase516-contacts. See CONTACTS.md.
v0.8.478 (Phase 515): DM spans — dm.open (spanKind, reusedChannel, peerDisplayNameLen), dm.send (when guildId === dm), dm.group.create (participantCount, nameLen). UI syncDmsPanelsDuringGuildLoading, tightened dm-errors, clipboard DM meta. Bundle: npm run test:phase515-dms. See DM.md.
v0.8.499 (Phase 536): Compliance/audit spans extend — audit.append/audit.export/compliance.snapshot end metadata (spanKind, activeChannelId). UI compliance panel compositor wrappers, audit row lastAuditRowId + roving tabindex, clipboard compliance busy flags. Bundle: npm run test:phase536-compliance. See COMPLIANCE.md, AUDIT_LOG.md.
v0.8.523 (Phase 560): Screen spans extend — screen.start/screen.stop/screen.capture activeChannelId. UI #screen-panel-live, syncScreenPopoutLivePanelDuringGuildLoading. Bundle: npm run test:phase560-screen. See SCREEN_SHARE.md.
v0.8.522 (Phase 559): Stage spans extend — stage.join/stage.speaker.request/stage.speaker.approve/stage.speaker.demote activeChannelId. UI #stage-panel-live, syncStageHandRaiseLivePanelDuringGuildLoading. Bundle: npm run test:phase559-stage. See STAGE.md.
v0.8.521 (Phase 558): Voice spans extend — voice.join/voice.leave/voice.mute activeChannelId. UI #voice-panel-live, syncVoiceOccupancyLivePanelDuringGuildLoading. Bundle: npm run test:phase558-voice. See VOICE.md.
v0.8.553 (Phase 590): Screen spans extend — screen.start/screen.stop/screen.capture end adds guildCount. UI #screen-hub-panel-live, syncScreenHubLivePanelDuringGuildLoading; tightened screen-errors bleed exclusions. Bundle: npm run test:phase590-screen. See SCREEN_SHARE.md.
v0.8.552 (Phase 589): Stage spans extend — stage.join/stage.speaker.request/stage.speaker.approve end adds guildCount. UI #stage-hub-panel-live, syncStageHubLivePanelDuringGuildLoading; tightened stage-errors bleed exclusions. Bundle: npm run test:phase589-stage. See STAGE.md.
v0.8.551 (Phase 588): Voice spans extend — voice.join/voice.leave/voice.mute end adds guildCount. UI #voice-hub-panel-live, syncVoiceHubLivePanelDuringGuildLoading; tightened voice-errors bleed exclusions. Bundle: npm run test:phase588-voice. See VOICE.md.
v0.8.550 (Phase 587): Pin spans extend — pin.message/pin.unpin/pin.list end adds guildCount. UI #pin-hub-panel-live, syncPinHubLivePanelDuringGuildLoading; tightened pin-errors bleed exclusions. Bundle: npm run test:phase587-pins. See PINS.md.
v0.8.549 (Phase 586): Search spans extend — guild.search/search.pins end adds guildCount. UI #search-hub-panel-live, syncSearchHubLivePanelDuringGuildLoading; tightened search-errors bleed exclusions. Bundle: npm run test:phase586-search. See SEARCH.md.
v0.8.548 (Phase 585): Reaction spans extend — reaction.clear end adds guildCount (toggle/list re-verified). UI #reaction-hub-panel-live, syncReactionHubLivePanelDuringGuildLoading; tightened reaction-errors bleed exclusions. Bundle: npm run test:phase585-reactions. See REACTIONS.md.
v0.8.547 (Phase 584): Message spans extend — message.send/message.edit/message.delete end adds guildCount. UI #messaging-hub-panel-live, syncMessagingHubLivePanelDuringGuildLoading; tightened message-errors bleed exclusions. Bundle: npm run test:phase584-messaging. See MESSAGING.md.
v0.8.546 (Phase 583): Attachment spans extend — attachment.stage/attachment.read/attachment.unlink end adds guildCount. UI #attachment-hub-panel-live, syncAttachmentHubLivePanelDuringGuildLoading; tightened attachment-errors bleed exclusions. Bundle: npm run test:phase583-attachments. See ATTACHMENTS.md.
v0.8.545 (Phase 582): Embed spans extend — embed.resolve/embed.gossip/embed.clear end adds guildCount. UI #embed-hub-panel-live, syncEmbedHubLivePanelDuringGuildLoading; tightened embed-errors bleed exclusions. Bundle: npm run test:phase582-embeds. See EMBEDS.md.
v0.8.519 (Phase 556): Attachment spans extend — attachment.stage stagedCount/remainingStagingCapacity; attachment.read activeChannelId; attachment.unlink on delete. UI split attachment panels; #attachment-panel-live. Bundle: npm run test:phase556-attachments. See ATTACHMENTS.md.
v0.8.518 (Phase 555): Reaction spans extend — reaction.toggle messageReactionCount/activeChannelId; reaction.list rowCount/messageCount; reaction.clear on delete via clearReactionsForMessage. UI split reaction pills/quick-react/picker chrome/live panels; #reaction-panel-live. Bundle: npm run test:phase555-reactions. See REACTIONS.md.
v0.8.517 (Phase 554): Messaging spans extend — message.send channelMessageCount/remainingCharCapacity; message.edit editAgeMs; message.delete meshFanout. UI split messaging panels; #messaging-panel-live. Bundle: npm run test:phase554-messaging. See MESSAGING.md.
v0.8.516 (Phase 553): Search spans extend — guild.search searchSpanKind (search.messages/search.dms/search.pins), remainingResultCapacity; pin.list spanKind: search.pins. UI split search panels; #search-panel-live. Bundle: npm run test:phase553-search. See SEARCH.md.
v0.8.515 (Phase 552): Compliance spans extend — audit.append auditEntryCount, audit.export exportLimit/remainingExportCapacity, compliance.snapshot meshPushed/archiveAppended. UI split compliance panels; .compliance-panel-live. Bundle: npm run test:phase552-compliance. See COMPLIANCE.md.
v0.8.514 (Phase 551): Moderation spans extend — kick.member memberCount, timeout.member activeTimeoutCount, automod.setConfig keywordCount/blockInvites. UI split automod/ban/member panels; .moderation-panel-live/.moderation-modal-live; clipboard moderationAutomodTogglesBusy/moderationTimeoutModalBusy. Bundle: npm run test:phase551-moderation. See MODERATION.md.
v0.8.498 (Phase 535): Moderation/automod spans extend — ban.member/kick.member/timeout.member/automod.* end metadata (spanKind, activeChannelId). UI moderation panel compositor wrappers, ban/audit aria-current=location, clipboard moderation busy flags. Bundle: npm run test:phase535-moderation. See MODERATION.md.
v0.8.513 (Phase 550): Role/permission spans extend — customRoleCount on update/delete, channelOverwriteCount/remainingOverwriteCount. UI split role list + channel perm panels; .roles-panel-live/.roles-modal-live; clipboard rolesListChromeBusy/rolesChannelPermControlsBusy. ui-flow composer focus after delete/reorder role. Bundle: npm run test:phase550-roles. See ROLES.md.
v0.8.497 (Phase 534): Role/permission spans extend — role.create/update/delete/reorder and permission.overwrite* end metadata (spanKind, activeChannelId, customRoleCount). UI roles panel compositor wrappers, role/overwrite aria-current=location, clipboard roles busy flags. Bundle: npm run test:phase534-roles. See ROLES.md.
v0.8.512 (Phase 549): Guild-admin spans extend — channel.create channelCount, channel.delete remainingChannelCount, channel.reorder requestedCount/reorderedAll. UI split channel list chrome/rows + category panels; .guild-admin-modal-live; clipboard guildAdminChannelModalBusy. ui-flow composer focus after rename-channel/create-category. Bundle: npm run test:phase549-guild-admin. See CHANNELS.md.
v0.8.496 (Phase 533): Guild-admin spans extend — channel.create/delete/reorder end metadata (spanKind, activeChannelId, createdChannelId). UI panel compositor wrappers, channel row aria-current=location, clipboard guild-admin busy flags. Bundle: npm run test:phase533-guild-admin. See CHANNELS.md.
v0.8.529 (Phase 566): Invite spans extend — guild.invite (textChannelCount, guildCount), invite.revoke (guildCount). UI #invites-panel-live + syncInvitesLivePanelDuringGuildLoading; invite-errors excludes dm/contacts/discovery/presence bleed; clipboard invitesPanelLiveBusy/invitesHubChromeBusy/channelGuildTextChannelCount. Bundle: npm run test:phase566-invites. See INVITES.md.
v0.8.511 (Phase 548): Invite spans extend — guild.invite (maxUses, activeChannelId), invite.revoke (activeChannelId). UI split hub buttons/banner + join/invite modal panels, .invite-modal-live, clipboard invitesJoinModalBusy/invitesInviteModalBusy. ui-flow composer focus after create-invite. Bundle: npm run test:phase548-invites. See INVITES.md.
v0.8.495 (Phase 532): Invite spans extend — guild.invite (spanKind, hasShareCode), invite.join (activeChannelId, guildCount, spanKind), invite.revoke (spanKind). UI split compositor (syncInvitesHubPanelDuringGuildLoading, syncInvitesJoinPanelDuringGuildLoading, syncInvitesInvitePanelDuringGuildLoading), invite code aria-current=location, clipboard invitesHubBusy/invitesHubButtonsBusy/inviteCopyBusy. Bundle: npm run test:phase532-invites. See INVITES.md.
v0.8.477 (Phase 514): Invite spans — guild.invite (shareCodeLen, channelCount), invite.join (inviteGuildId, portable), invite.revoke (revoked). UI syncInvitesPanelsDuringGuildLoading, #composer-invite-hint, dev-log invite-errors + clipboard meta. Bundle: npm run test:phase514-invites. See INVITES.md.
Messaging & channels
| Method | Role |
|---|---|
selectChannel(channelId) |
Sets messages.setChannel, read state, voice context |
sendMessage, editMessage, deleteMessage |
Delegates to PearcordMessage + guild/DM gossip |
signalTyping, toggleReaction |
Gossip + local state |
createCategory, createForumPost, thread helpers |
PearcordGuild + permissions |
Invites & discovery
| Method | Role |
|---|---|
createInvite() |
PearcordInvite#create with channel snapshot |
joinInvite(code) |
Resolve + guild.joinByInvite + mesh; pearcord://explore?… → navigateDeepLink (v0.8.108) |
markOnboardingExploreShown() |
Clear first-guild Explore prompt; onboardingExploreShown pref (v0.8.108) |
setThreadsPanelFilter(filter) |
Filter chips; persists threadsPanelFilter in USER_PREFS (v0.8.108) |
formatDiscoveryExploreBaselineDeepLink() |
pearcord://explore?baseline&at share URL (v0.8.107) |
Voice & media
| Method | Role |
|---|---|
joinVoice, leaveVoice, setVoiceMute |
pearcord-voice + guild gossip |
setVoiceRosterChannelExpanded(channelId, expanded) |
USER_PREFS voiceRosterExpandedChannelIds (v0.8.110) |
setDiscoveryFilter({ query, sort, minMembers, tag }) |
Persists discoveryExploreFilter in USER_PREFS (v0.8.111) |
| Voice/s screen share ingest | VoiceMediaHub, ScreenShareHub wired in _openGuild |
Views & companion
| Property | Role |
|---|---|
mode |
'home', 'guild', or DM mode |
guildBans |
Ban rows for active guild (@pearcord/bans, limit 200) (v0.8.409) |
getViewState() / snapshot builders |
IPC state payloads for UI |
_readOnly, _companionMode |
Companion restrictions |
Hundreds of additional methods cover moderation, bots, search, audit export, forums, stage, automod, boosts, settings mesh, contacts, notifications — each delegating to the matching pearcord-* package.
P2P surface
Platform does not implement wire protocols; it orchestrates:
| Subsystem | Module | Topic / protocol |
|---|---|---|
| Guild gossip | pearcord-guild |
pearcord-gossip-v1 on guild.topic |
| DM mesh | pearcord-dm |
dmTopic / group DM topics |
| Contacts | pearcord-contacts |
contactsTopic |
| Settings / prefs | pearcord-settings |
userPrefsTopic |
| Discovery | pearcord-discovery |
Public listings mesh |
| Device pair | pearcord-device-sync |
deviceSyncTopic + DEVICE_PAIR_* RPC |
_wireGuild(guild) registers setMessageHandler and forwards guild events to platform.emit (message, member, guild-sync, voice, …).
Storage
| Path | Owner |
|---|---|
{storagePath}/db/ |
LocalDatabase — all @pearcord/* collections |
{storagePath}/identity/ |
pearcord-identity keypairs |
{storagePath}/dm-meta/ |
JsonStore for DM metadata |
| Feature dirs | attachments, discovery JSON, moderation, voice state, etc. |
storagePath from resolveStoragePath() (PEARCORD_STORAGE or ~/.config/pearcord).
Platform integration (core batch A wiring)
// Constructor (simplified)
this.db = new LocalDatabase(this.dbPath)
this.identity = new PearcordIdentity({ db, storagePath })
// ready() → invite, attachments, discovery when snap.user
// Guild open (_openGuild)
this.guild = new PearcordGuild({ ownerId, userId, db })
this._wireGuild(this.guild)
await this.guild.joinMesh()
this.messages = new PearcordMessage({ authorId: user.id, db })
| Dependency | Role in platform |
|---|---|
pearcord-db |
Shared this.db |
pearcord-identity |
this.identity, storage paths |
pearcord-guild |
Active server + mesh |
pearcord-message |
this.messages per channel |
pearcord-invite |
this.invite |
pearcord-shared |
Permissions, COLLECTIONS, deep links, id, now |
pearcord-channel and pearcord-sync are not dependencies.
UI / IPC
Primary consumer: apps/pearcord/index.js sidecar reading pear-pipe JSON.
| Direction | Examples |
|---|---|
| UI → platform | register, create-guild, join-invite, select-guild, select-channel, send-message, join-voice |
v0.8.115: loadGuild / _openGuild clears stale activeChannelId, then _selectGuildChannelWithUnread picks mention-weighted unread channels before falling back to the first text channel. setSearch records searchRecentByGuild in user prefs. View: searchRecentQueries.
v0.8.397: ingestInboundGuildMessage(msg) processes guild mesh gossip while on home (mention alerts + inbox via DB member lookup). markAllNotificationsRead / openNotificationTarget / markChannelRead log structured errors. Smoke helper: simulateGuildMessage(platform, msg) in smoke-runner.cjs.
v0.8.398: parseSearchQuery + _applySearchQueryFilters for guild/channel search; pinMessage/unpinMessage log pin.message/pin.unpin spans and errors; guild.search error on search failure.
v0.8.399: banMember/kickMember/timeoutMember log ban.member/kick.member/timeout.member spans and errors; exportAuditLog logs audit.export span and audit.export error.
v0.8.400: createChannelWebhook/deleteChannelWebhook/executeChannelWebhook log webhook.* spans and errors; registerSlashCommands/deleteSlashCommand/executeSlashCommand log slash.* spans and errors.
v0.8.411 (Phase 448): Webhook and slash spans/errors include guildId metadata; webhook.execute span end includes channelId. Dev-log webhook-errors filter: webhook.create|delete|execute; slash-errors: slash.register|delete|invoke. Integrations UI permission toasts; webhook token modal autofocus. Bundle: npm run test:phase448-webhooks-integrations.
v0.8.412 (Phase 449): sendMessage/editMessage/deleteMessage/toggleReaction spans/errors include guildId + channelId; _assertCanParticipate('send'|'reaction') for distinct read-only errors. Dev-log message-errors filter: message.send|edit|delete; reaction-errors: reaction.toggle. UI: message toolbar + reaction pill keyboard roving, composer autofocus after reaction, permission toasts. Bundle: npm run test:phase449-messaging-reactions.
v0.8.413 (Phase 450): stageAttachment/readAttachmentPreview/embed.resolve spans/errors include guildId + channelId; _assertCanParticipate('attach') for read-only attach gate. Dev-log attachment-errors filter: attachment.stage|read; embed-errors: embed.resolve. UI: staging bar + btn-attach aria-busy, attach-files permission toast, staging/embed keyboard roving, empty-state hints. Bundle: npm run test:phase450-attachments-media.
v0.8.431 (Phase 468): openDM span.end adds group: false + peerUserId; openGroupDM span.end adds group: true + participantCount; discovery.list span.end adds filterTag; acceptFriendRequest span.end re-verifies peerUserId. Error logs use error: key (not err:). Dev-log clipboard: discoveryFilterTag, discoveryMeshLive, dmChannelCount, contactsPendingCount. UI: syncDiscoveryPanelsDuringGuildLoading, #composer-dm-hint, 🔒 E2E badge. Bundle: npm run test:phase468-discovery-dm.
v0.8.415 (Phase 452): openDM/openGroupDM log dm.open spans + dm.open error; sendFriendRequest/acceptFriendRequest log contacts.request/contacts.accept spans; _buildView logs discovery.list span + discovery.list error. Dev-log dm-errors filter: dm.open|group, contacts.request|accept|decline, discovery.list. UI-flow emits composer-focus-channel after DM open. Bundle: npm run test:phase452-discovery-dm.
v0.8.414 (Phase 451): joinVoiceChannel/leaveVoiceChannel/joinStageChannel/requestStageSpeak/approveStageSpeaker/demoteStageSpeaker/startScreenShare/stopScreenShare spans/errors include guildId + channelId. Dev-log voice-errors filter: voice.join|leave, stage.join|speaker.*, screen.start|stop. UI: voice/stage bars + channel list aria-busy, stage connect/speak permission toasts, stage approve keyboard, stream popout autofocus. Bundle: npm run test:phase451-voice-stage.
v0.8.402: sendMessage/editMessage/deleteMessage log message.* spans and errors; toggleReaction logs reaction.toggle span and reaction.toggle error on failure.
v0.8.403: stageAttachment/readAttachmentPreview log attachment.* spans and errors; _queueLinkEmbed logs embed.resolve span and embed.resolve error on failure.
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.
v0.8.435 (Phase 472): ban.member/kick.member span end adds hasReason; ban.member adds banCount; timeout.member adds until; audit.export adds signed. UI extended syncModerationPanelsDuringGuildLoading + composer moderation hint. Smokes: test:platform-moderation-span-metadata-extend, test:dev-log-moderation-errors-tighten, test:dev-log-audit-errors-tighten. Bundle: test:phase472-moderation-audit.
v0.8.434 (Phase 471): thread.create span end adds parentType/threadName; thread.archive adds parentId; forum.post adds parentChannelId/messageId. Error logs use error: key. UI syncThreadsPanelsDuringGuildLoading + composer thread hint + thread-read live extend. Smokes: test:platform-thread-span-metadata-extend, test:dev-log-thread-errors-tighten. Bundle: test:phase471-threads-forum.
v0.8.433 (Phase 470): guild.search span end adds meshHitCount/localHitCount/mesh per scope; pin.message/pin.unpin span end adds messageId + pinCount. UI syncSearchPanelsDuringGuildLoading + composer search hint. Smokes: test:platform-search-span-metadata-extend, test:dev-log-search-errors-tighten, test:dev-log-pin-errors-tighten. Bundle: test:phase470-search-pins.
v0.8.432: notification.markRead span end adds kind + read; notification.markAll adds inboxCount; notification.open adds messageId; inbox.mark-read/thread.read span end adds threadRead + isDm. Error logs use error: key. UI syncNotificationPanelsDuringGuildLoading + composer notif hint + thread-read live region. Smokes: test:platform-notification-span-metadata-extend, test:dev-log-notification-errors-tighten, test:dev-log-inbox-errors-tighten. Bundle: test:phase469-notifications-inbox.
v0.8.416: openNotificationTarget span end includes guildId; markAllNotificationsRead / markNotificationRead / markChannelRead (inbox.mark-read or thread.read) span end includes guildId + channelId. Smoke: test:platform-notification-span-guild-id. Bundle: test:phase453-notifications-inbox.
v0.8.407: markNotificationRead(notificationId) wraps inbox markRead with notification.markRead span + notification.markRead error. markAllNotificationsRead span ends with count + guildId. markChannelRead logs inbox.mark-read span + error for non-thread channels (thread channels use thread.read). Smokes: test:notification-platform-errors, test:inbox-platform-errors. Bundle: test:phase444-notifications-inbox.
v0.8.409 (Phase 446): banMember/kickMember/timeoutMember spans include guildId metadata on start and error lines; exportAuditLog span includes guildId, format, filter; setAutomodConfig logs automod.setConfig span + automod.setConfig error. View snapshot adds guildBans (db.find(COLLECTIONS.BANS, { guildId }, { limit: 200 })). Bundle: test:phase446-moderation-audit.
v0.8.410 (Phase 447): createGuildCustomRole/updateGuildCustomRole/deleteGuildCustomRole/reorderGuildCustomRoles/setMemberCustomRoles spans and role.* error lines include guildId; upsertChannelPermissionOverwrite/deleteChannelPermissionOverwrite spans and permission.overwrite*.error include guildId, channelId, targetType, targetId. View snapshot adds channelPermsByChannel (effective viewChannel/sendMessages per channel) and channelOverwriteCount (guild overwrite row count). Bundle: test:phase447-roles-permissions.
v0.8.418: updateGuildSettings logs guild.update span + guild.update error; createInvite logs guild.invite span + guild.invite error (both include guildId). Smoke: test:platform-guild-settings-span-guild-id. Bundle: test:phase455-guild-settings.
v0.8.417: guild.search spans include guildId on start/end (endSearchSpan helper); pin.message/pin.unpin spans include guildId. Smoke: test:platform-search-span-guild-id. Bundle: test:phase454-search-pins.
v0.8.408: markChannelRead calls _invalidateSearchCache() when the channel is a thread (thread.read path) so search results refresh after thread read. Smoke: test:search-cache-invalidate-thread-read. Bundle: test:phase445-search-inbox.
v0.8.406: createThread/createForumPost/markChannelRead (thread channels)/archiveThread log thread.create/forum.post/thread.read/thread.archive spans and errors; audit entries for thread.create, forum.post.create, thread.archive, and thread.unarchive. Smoke: test:thread-platform-errors.
v0.8.401: createGuildCustomRole/updateGuildCustomRole/deleteGuildCustomRole/reorderGuildCustomRoles/setMemberCustomRoles log role.* spans and errors; upsertChannelPermissionOverwrite/deleteChannelPermissionOverwrite log permission.overwrite* spans and errors.
v0.8.252: Guild search mesh skipped when guildOpenNoViewableChannels; emoji batch prefetch skips negative miss TTL; sticker gossip prefetch gated on attachments; session.startup logs INFO when initial guild mesh flush was bounded. Smoke: test:guild-search-no-viewable-skip, test:sticker-prefetch-attachments-ready.
v0.8.248: Guild open filters unread/last picks with _canViewChannel; sets guildOpenNoViewableChannels when none accessible; defers _ensureGuildModules via setTimeout(0) after channel pick; emoji mesh prefetch clears miss cache on success. Smokes: test:guild-viewable-channel-pick, test:guild-open-no-viewable, test:guild-plain-beats-dual-combo.
v0.8.158: Between two mention+plain parent buckets, the category with the higher combined score wins on guild open. Smoke: test:guild-dual-combo-buckets-highest.
v0.8.157: A heavy plain-only parent bucket whose total exceeds three lower mention+plain combo categories wins on guild open. Smoke: test:guild-plain-beats-triple-combo.
v0.8.156: Among three mention+plain parent buckets, the category with the highest combined score wins on guild open. Smoke: test:guild-triple-combo-buckets-highest.
v0.8.155: Among three plain-only parent buckets, the category with the highest combined unread total wins on guild open. Smoke: test:guild-triple-plain-bucket-pick.
v0.8.154: A mention+plain parent bucket whose combined score exceeds a higher plain-only bucket wins on guild open. Smoke: test:guild-combo-beats-plain-bucket.
v0.8.153: When plain-only and mention+plain parent buckets tie on total score, the bucket with the lower minimum channel position wins. Smoke: test:guild-plain-combo-minpos-tie.
v0.8.152: Between two plain-only parent buckets, the category with the higher plain-unread total wins on guild open. Smoke: test:guild-dual-plain-bucket-pick.
v0.8.151: A plain-only parent bucket whose unread total exceeds a lighter mention+plain combo bucket wins on guild open (e.g. 1004 plain vs 1003 combo). Smoke: test:guild-plain-beats-combo-bucket.
v0.8.150: Among three mention+plain parent buckets, the category with the highest combined score wins on guild open. Smoke: test:guild-triple-combo-bucket-pick.
v0.8.149: Equal mention+plain parent-bucket totals tie-break to the bucket whose unread channels have the lower minimum position. Smoke: test:guild-combo-bucket-minpos-tie.
v0.8.148: A mention+plain category bucket beats a plain-only bucket when both have the same plain-unread count (mention adds 1000 to bucket total). Smoke: test:guild-combo-beats-plain-equal.
v0.8.147: Between two categories each with mention+plain unread, the parent bucket with the higher combined score wins. Smoke: test:guild-dual-combo-bucket-pick.
v0.8.146: A category bucket with one mention plus plain unread (score 1001 + n) beats a higher plain-only bucket in another category. Smoke: test:guild-mention-plain-bucket-pick.
v0.8.145: A root (uncategorized) bucket whose plain-unread total exceeds 1000 beats two mention-weighted category buckets. Smoke: test:guild-root-plain-triple-mention-pick.
v0.8.144: Among three parent buckets, a mention-weighted bucket (score 1001+) beats two higher plain-unread buckets. Smoke: test:guild-mention-triple-bucket-pick.
v0.8.143: Three parent buckets with equal unread totals pick the bucket with the lowest minPos. Smoke: test:guild-bucket-minpos-triple-pick.
v0.8.142: When two parent buckets share equal unread totals and equal minPos, the first bucket encountered in channel scan order wins. Smoke: test:guild-bucket-minpos-order-pick.
v0.8.141: Equal parent-bucket unread totals across root and categories tie-break to the bucket with the lower minimum channel position. Smoke: test:guild-bucket-minpos-root-pick.
v0.8.140: removeSearchRecent(query) drops a guild-scoped recent search from searchRecentByGuild. A mention in the root bucket beats higher plain unread in a category. Smokes: test:search-recent-remove, test:guild-root-mention-bucket-pick.
v0.8.139: A mention-weighted category bucket beats higher plain unread in the root (uncategorized) bucket. Smoke: test:guild-mention-root-bucket-pick.
v0.8.138: With unread in both root and category buckets, parent-bucket totals decide the pool (highest score wins). Smoke: test:guild-mixed-bucket-pick.
v0.8.137: When unread exists in only one category among several, selection stays inside that category pool. Smoke: test:guild-dual-category-isolation-pick.
v0.8.136: In a single-category unread pool, mention-weighted score (mentions × 1000 + unread) beats a lower-position channel with equal plain unread. Smoke: test:guild-mention-single-category-pick.
v0.8.135: When all unread channels share one parentId, _selectGuildChannelWithUnread limits the pool to that category before position/score tie-breaks. Smoke: test:guild-single-category-pool.
v0.8.134: Uncategorized (parentId empty) and category buckets compete by total unread score; uncategorized totals can beat a lower-total category. Smoke: test:guild-uncategorized-bucket-pick.
v0.8.133: Parent-bucket selection prefers the highest total unread score over a bucket with a lower minPos but fewer unreads. Smoke: test:guild-bucket-total-over-minpos.
v0.8.132: Within one category, a channel with a mention alert beats another with the same plain unread count (mentions × 1000 + unread). Smoke: test:guild-mention-in-bucket-pick.
v0.8.131: Within a single parent bucket, equal unread/mention scores pick the channel with the lower sibling position. Smoke: test:guild-in-bucket-position-tie.
v0.8.130: Equal parent-bucket unread totals break ties by lower minimum channel position among unread rows in that bucket (sibling index within the parent). Smoke: test:guild-bucket-minpos-tie.
v0.8.126: Parent bucket totals use mention-weighted scores (mentions × 1000 + unread), so a mention category beats higher plain unread elsewhere. Smoke: test:guild-mention-bucket-pick.
v0.8.125: _selectGuildChannelWithUnread picks the parent bucket (category or uncategorized) with the highest total unread score, then best channel within it. Smokes: test:guild-category-bucket-pick, test:guild-category-unread-pick.
v0.8.124: _unreadForChannel filters rows by channelId after HyperDB find. When all unread are in one category, _selectGuildChannelWithUnread picks only within that parentId. Smoke: test:guild-category-unread-pick.
v0.8.123: Equal unread/mention scores tie-break to lower channel.position. Smoke: test:guild-unread-position-tie.
v0.8.120: _selectGuildChannelWithUnread scores mentions × 1000 + unread; mention channel beats higher plain unread. Smoke: test:guild-mention-channel-pick.
v0.8.119: _selectGuildChannelWithUnread order: highest unread/mention score → lastChannelByGuild → first text. Smokes: test:guild-unread-over-last-channel, test:guild-unread-channel-jump.
v0.8.116: selectChannel persists savable channel types via _persistLastGuildChannel. Smoke: npm run test:guild-last-channel-prefs.
v0.8.237: View exposes sessionReady, guildLoading, guildLoadingGuildId, sessionError. Emits user-prefs and guild-loading for UI pushState. openHome() cancels in-flight loadGuild.
| platform → UI | state, message, channel, notification, session-ready, user-prefs, guild-loading, user |
See IPC.md for the full message table. UI should not import guild/message modules directly in production builds.
Related docs
- IPC.md — sidecar contract
- ARCHITECTURE.md — system diagram
- MODULES.md — all packages
- GETTING_STARTED.md
- ONBOARDING.md
- GUILD_SYNC.md
- PEARCORD_PARITY.md
- AUTOMATED_TESTING.md
- PLATFORM_ROADMAP.md
Tests
Most apps/pearcord/scripts/smoke-*.cjs scripts construct PearcordPlatform with a temp storagePath. Runner: npm run test:smoke / smoke-runner.cjs.
Representative targets:
- Guild/invite:
test:guild-sync,test:invite,test:storage-path - Mesh RPC:
smoke-voice-speaking-mesh.cjs,smoke-guild-sync.cjs - HyperDB:
test:hyperdb(db only, but platform uses same path layout)
Dependency integrity targets:
npm run test:deps— strict import/dependency parity check forpearcord-*modules.npm run test:imports— runtime module resolution smoke for importedpearcord-*modules.npm run ci:deps— CI guard (test:deps+test:imports), fails on undeclared peer-module imports.
Local module install order (multi-module edits)
When editing multiple workspace modules, install in dependency order to avoid stale file: links:
- Install changed leaf/shared modules first (for example
pearcord-shared,pearcord-db,pearcord-settings). - Reinstall
pearcord-platform(cd modules/pearcord-platform && npm install). - Reinstall direct consumers (
pearcord-agentctl,apps/pearcord) before live runs.
Quick recovery sequence:
cd modules/pearcord-platform && npm install
cd ../pearcord-agentctl && npm install
cd ../../apps/pearcord && npm install
Code example
const path = require('bare-path')
const os = require('bare-os')
const { PearcordPlatform } = require('pearcord-platform')
const storagePath = path.join(os.tmpdir(), `pearcord-demo-${Date.now()}`)
const platform = new PearcordPlatform({ storagePath })
await platform.ready()
await platform.register({ username: 'demo', displayName: 'Demo' })
const { guild } = await platform.createGuild({ name: 'Test Server' })
await platform.sendMessage('Hello from the facade')
const view = platform.getViewState?.() ?? { mode: platform.mode, guildId: guild.id }
console.log(view)
await platform.close?.()
Minimal sidecar-style usage:
platform.on('message', (msg) => { /* push to UI */ })
platform.on('session-ready', () => { /* enable guild list */ })
platform.on('user-prefs', () => pushState()) // theme/density mesh sync (v0.8.237)
platform.on('guild-loading', () => pushState()) // guild switch overlay
View fields sessionReady, guildLoading, guildLoadingGuildId, sessionError (v0.8.237). UI must not show the main shell until sessionReady is true.
v0.8.174: updateGuildSettings({ name, iconHash, nsfwGateEnabled }) persists server-wide age-restricted gate toggle on guild record. View exposes guild.nsfwGateEnabled.
Repository
Part of Pearcord.
- Org:
pearcord - Clone:
git clone https://git.ssh.surf/pearcord/pearcord-platform.git - Install:
npm install git+https://git.ssh.surf/pearcord/pearcord-platform.git#main
Depends on the full Pearcord module workspace (see package.json); install from monorepo modules/ for local development.