Summary
The messaging service's L2 event object sys_notification is never provisioned by the runtime, so MessagingService.emit()'s dedupKey idempotency silently does not work: every duplicate emit with the same dedupKey fans out again and produces duplicate sys_inbox_message rows.
Environment
@objectstack/* 17.0.0-rc.0 (runtime + service-messaging + platform-objects), sqlite driver, single-tenant dev stack (objectstack dev).
Observed
MessagingServicePlugin loads and emit() → inbox channel works: sys_inbox_message and sys_notification_receipt rows are written, the Console bell reads them fine.
- But the database contains no
sys_notification table at all (.tables shows sys_notification_preference/_subscription/_template/_receipt and sys_inbox_message, not the event object itself).
- Consequently the
notification_id carried on inbox/receipt rows does not exist in any table — writeEvent falls into its synthesize-an-id degradation path (the code comments this as the "no data layer" case, but here the data layer exists; the event object is simply not provisioned).
findEventByDedupKey therefore never finds a prior event ("tolerating lookup failure"), so a repeated emit with the same dedupKey is not short-circuited.
Expected
sys_notification should be provisioned with the rest of the messaging schema so that:
emit() persists a real L2 event row, and
dedupKey dedup actually short-circuits repeats, as documented in EmitInput.
Impact
Every caller relying on emit({ dedupKey }) idempotency (flow notify node, app-level notification bridges) can deliver duplicate in-app messages on retry/replay. Inbox read/mark-read is unaffected (it keys consistently off the synthesized id), though receipts reference a dangling notification_id.
Summary
The messaging service's L2 event object
sys_notificationis never provisioned by the runtime, soMessagingService.emit()'sdedupKeyidempotency silently does not work: every duplicateemitwith the samededupKeyfans out again and produces duplicatesys_inbox_messagerows.Environment
@objectstack/*17.0.0-rc.0 (runtime + service-messaging + platform-objects), sqlite driver, single-tenant dev stack (objectstack dev).Observed
MessagingServicePluginloads andemit()→ inbox channel works:sys_inbox_messageandsys_notification_receiptrows are written, the Console bell reads them fine.sys_notificationtable at all (.tablesshowssys_notification_preference/_subscription/_template/_receiptandsys_inbox_message, not the event object itself).notification_idcarried on inbox/receipt rows does not exist in any table —writeEventfalls into its synthesize-an-id degradation path (the code comments this as the "no data layer" case, but here the data layer exists; the event object is simply not provisioned).findEventByDedupKeytherefore never finds a prior event ("tolerating lookup failure"), so a repeatedemitwith the samededupKeyis not short-circuited.Expected
sys_notificationshould be provisioned with the rest of the messaging schema so that:emit()persists a real L2 event row, anddedupKeydedup actually short-circuits repeats, as documented inEmitInput.Impact
Every caller relying on
emit({ dedupKey })idempotency (flownotifynode, app-level notification bridges) can deliver duplicate in-app messages on retry/replay. Inbox read/mark-read is unaffected (it keys consistently off the synthesized id), though receipts reference a danglingnotification_id.