# OpenSpec Change: enterprise-message-bridge - **Change ID**: enterprise-message-bridge - **Package**: `@suaveplan/message-bridge` - **Category**: universal - **Target path**: `universal/social/message-bridge` - **Status**: IN PROGRESS - **Date**: 2026-04-24 - **Reference spec**: `/mnt/s/web/suaveplan/enterprise/message-bridge.md` --- ## Summary Deliver `@suaveplan/message-bridge` on the genesis branch from scratch at enterprise quality. The package provides a unified multi-platform message bridge: a `MessageBridge` facade that normalizes inbound and outbound messages across registered `MessageAdapter` implementations (Slack, Discord, Telegram, iMessage, SMS), a canonical `UnifiedMessage` model, platform-agnostic routing rules, deduplication of inbound messages by message ID, delivery confirmation tracking, and an event bus for inbound message propagation. This package defines the `MessageAdapter` interface that all platform adapters implement. Genesis adds structured errors, Zod schemas, OTel instrumentation, `AbortSignal` cancellation, and property-based tests. --- ## Current-state audit (genesis) The package does not exist on genesis yet — this is a fresh build. --- ## Available workspace deps on genesis | Package | Role | |---|---| | `@suaveplan/error` (`workspace:^`) | `MessageBridgeError`; `MSG_BRIDGE.*` error codes | | `@suaveplan/types` (`workspace:^`) | `LoggerLike`, `Clock`, `EventBusLike` | | `@suaveplan/events` (`workspace:^`) | Inbound message event propagation | | `@suaveplan/retry` (`workspace:^`) | Retry on send failures | | `zod` (`catalog:forms`) | Schema validation | | `@opentelemetry/api` (`catalog:observability`) | Spans + counters | Dev deps: `vitest`, `@vitest/coverage-istanbul`, `fast-check` (all `catalog:testing`). --- ## Public API surface ### `UnifiedMessage` ```ts interface UnifiedMessage { id: string platform: string // 'slack' | 'discord' | 'telegram' | 'imessage' | 'sms' | custom channelId?: string threadId?: string senderId: string senderName?: string text: string attachments: MessageAttachment[] timestamp: Date raw?: unknown // original platform payload metadata: Record } interface MessageAttachment { id: string type: 'image' | 'video' | 'audio' | 'file' url?: string content?: Uint8Array mimeType?: string filename?: string size?: number } interface SendOptions { platform: string to: string // channel ID, chat ID, or phone number depending on platform threadId?: string attachments?: MessageAttachment[] signal?: AbortSignal } ``` ### `MessageAdapter` interface ```ts interface MessageAdapter { readonly platform: string start(signal?: AbortSignal): AsyncGenerator send(to: string, text: string, options?: AdapterSendOptions): Promise isAvailable(): boolean } ``` ### `MessageBridge` ```ts class MessageBridge { constructor(options?: MessageBridgeOptions) register(adapter: MessageAdapter): void unregister(platform: string): void send(options: SendOptions, text: string): Promise start(signal?: AbortSignal): void // starts all registered adapters stop(): void on(event: 'message', handler: (msg: UnifiedMessage) => void): () => void platforms(): string[] } interface MessageBridgeOptions { bus?: EventBusLike logger?: LoggerLike clock?: Clock deduplicationWindowMs?: number // default 60_000; 0 disables } ``` ### Routing ```ts interface RouteRule { fromPlatform?: string toPlatform: string transform?: (msg: UnifiedMessage) => Partial } function applyRoutes(bridge: MessageBridge, rules: RouteRule[]): () => void ``` ### Error codes ```ts type MessageBridgeErrorCode = | 'MSG_BRIDGE_ADAPTER_NOT_FOUND' | 'MSG_BRIDGE_SEND_FAILED' | 'MSG_BRIDGE_ADAPTER_UNAVAILABLE' | 'MSG_BRIDGE_DEDUP_CONFLICT' | 'MSG_BRIDGE_ROUTE_FAILED' | 'MSG_BRIDGE_ABORTED' ``` --- ## Genesis additions 1. **`MessageBridgeError`** extends `SuaveplanError` with six stable codes. 2. **Zod schemas** for `UnifiedMessage`, `MessageAttachment`, `SendOptions`, `RouteRule`, `MessageBridgeOptions`. 3. **Message deduplication** — a sliding-window set keyed by `${platform}:${id}` prevents duplicate inbound delivery when multiple adapters or polling overlaps produce the same message. 4. **Routing rules** — `applyRoutes` wires cross-platform message forwarding (e.g. Slack → Telegram bridge) with optional transform. 5. **OTel instrumentation** — counter `message_bridge.inbound_total` with attribute `message_bridge.platform`; counter `message_bridge.outbound_total`; histogram `message_bridge.send_duration_ms`. 6. **Property-based tests** — deduplication window correctly excludes duplicates and expires old entries for arbitrary sequences of message IDs. --- ## File layout ```text universal/social/message-bridge/ package.json tsconfig.json vitest.config.ts src/ index.ts error-codes.ts constants/ index.ts types/ common.ts common.md core/ message-bridge/ message-bridge.ts message-bridge.md message-bridge.types.ts message-bridge.test.ts index.ts deduplicator/ deduplicator.ts deduplicator.md deduplicator.test.ts index.ts routing/ route-rules/ route-rules.ts route-rules.md route-rules.test.ts index.ts __benchmarks__/ message-bridge.bench.ts tests/ integration.test.ts property.test.ts ``` --- ## Impact **Downstream consumers**: `@suaveplan/imessage-adapter`, `@suaveplan/slack-client` (adapter registration), `@suaveplan/discord-client`, `@suaveplan/telegram-client`, `@suaveplan/sms-providers`. **Note**: This package must be built before the adapter packages since it defines the `MessageAdapter` interface they implement. **Workspace dep prerequisites**: `@suaveplan/error`, `@suaveplan/types`, `@suaveplan/events`, `@suaveplan/retry`.