Skip to content

feat(room-worker): publish InboxMemberEvent to local INBOX + fix inbox-worker Subjects#111

Merged
mliu33 merged 1 commit into
claude/recover-search-sync-worker-dCjyVfrom
claude/room-worker-inbox-publish
Apr 21, 2026
Merged

feat(room-worker): publish InboxMemberEvent to local INBOX + fix inbox-worker Subjects#111
mliu33 merged 1 commit into
claude/recover-search-sync-worker-dCjyVfrom
claude/room-worker-inbox-publish

Conversation

@Joey0538
Copy link
Copy Markdown
Collaborator

Summary

Wires the publisher side of the INBOX-based search-sync pipeline landed in #109. After this, search-sync-worker's spotlight + user-room collections actually receive events end-to-end instead of spinning up with an empty feed.

Stacked on top of #109 — base branch is claude/recover-search-sync-worker-dCjyV. Merge #109 first, then this; GitHub will auto-retarget to main when #109 lands.

Scope

room-worker

processAddMembers, processRemoveIndividual, processRemoveOrg now split their member list by home site and publish InboxMemberEvent:

  • Same-site accounts → one publish per call to chat.inbox.{site}.member_added/removed
  • Cross-site accounts → existing OUTBOX publish per destination, inner payload migrated from MemberAddEvent/MemberRemoveEventInboxMemberEvent so the remote site's search-sync sees RoomName + RoomType (needed for spotlight typeahead indexing).

Backward-compatibility: InboxMemberEvent is a strict JSON superset of MemberAddEvent on the fields inbox-worker reads (Accounts, RoomID, SiteID, JoinedAt, HistorySharedSince), so inbox-worker's handleMemberAdded / handleMemberRemoved continue to unmarshal into their existing types and simply ignore the extra RoomName/RoomType fields. OrgID on MemberRemoveEvent is dropped from the INBOX payload — not currently read by any consumer, and InboxMemberEvent stays focused on what search-sync needs. If inbox-worker ever needs OrgID, it can be added then.

Remove events omit RoomName/RoomType entirely — search-sync keys its deletes by {account}_{roomID} and script-removes by roomID; neither path needs room metadata, so the remove path skips the extra DB lookup.

inbox-worker

One-line fix: CreateOrUpdateStream was copying only .Name from stream.Inbox() and dropping .Subjects. The INBOX stream was being created with zero subjects, so any publish to chat.inbox.{site}.* would have failed once room-worker started publishing. Now passes both.

Out of scope: remote-site Sources + SubjectTransforms federation config — production multi-site isn't deployed yet, and when it is we want inbox-worker (not search-sync-worker's bootstrap path) to own that config. BOOTSTRAP_STREAMS in search-sync-worker stays as the dev-only toggle for now.

Tests

  • TestHandler_ProcessAddMembers_PublishesToInboxnew, asserts local INBOX publish carries RoomName/RoomType + cross-site OUTBOX payload is InboxMemberEvent with a split across local (bob, carol on site-a) and remote (dave on site-b) accounts.
  • TestHandler_ProcessRemoveMember_SelfLeave_IndividualOnly + _OwnerRemovesIndividual + _OwnerRemovesOrg — bumped expected publish count +1 and assert the local INBOX publish carries the right accounts.
  • TestHandler_ProcessRemoveMember_CrossSiteOutbox — assert OUTBOX payload is InboxMemberEvent (not MemberRemoveEvent) and confirm no local INBOX publish fires when the removed user is remote.

Test plan

Follows #109.

https://claude.ai/code/session_01XTmSpmv5dT6UXX7NpRdYqN

…x-worker Subjects

Wires the publisher side of the INBOX-based search-sync pipeline landed
in #109. After this, search-sync-worker's spotlight + user-room
collections actually receive events end-to-end instead of spinning up
with an empty feed.

room-worker changes (handler.go)
--------------------------------

processAddMembers, processRemoveIndividual, processRemoveOrg now split
their member list by home site and publish InboxMemberEvent:

  - Same-site accounts → one publish per call to
    `chat.inbox.{site}.member_added/removed`
  - Cross-site accounts → existing OUTBOX publish per destination,
    inner payload migrated MemberAddEvent/MemberRemoveEvent →
    InboxMemberEvent so the remote site's search-sync sees RoomName +
    RoomType (needed for spotlight typeahead indexing).

InboxMemberEvent is a strict JSON superset of MemberAddEvent on the
fields inbox-worker reads (Accounts, RoomID, SiteID, JoinedAt,
HistorySharedSince), so inbox-worker's handleMemberAdded /
handleMemberRemoved continue to unmarshal into their existing types and
simply ignore the extra RoomName/RoomType fields. OrgID on
MemberRemoveEvent is dropped from the INBOX payload — not currently
read by any consumer, and InboxMemberEvent stays focused on what
search-sync needs. If inbox-worker ever needs OrgID, add it then.

Remove events omit RoomName/RoomType entirely (search-sync keys its
deletes by {account}_{roomID} and script-removes by roomID; neither
needs room metadata) so we skip the extra DB lookup on the remove
path.

inbox-worker changes (main.go)
------------------------------

One-line fix: `CreateOrUpdateStream` was copying only `.Name` from
`stream.Inbox()` and dropping `.Subjects`. The INBOX stream was being
created with zero subjects, so any publish to `chat.inbox.{site}.*`
would have failed once room-worker started publishing. Now passes
both.

The remote-site Sources + SubjectTransforms federation config is
intentionally left out — production multi-site isn't deployed yet, and
when it is we want inbox-worker (not search-sync-worker's bootstrap
path) to own that config.

Tests
-----

- TestHandler_ProcessAddMembers_PublishesToInbox — new, verifies local
  INBOX publish carries RoomName/RoomType + cross-site OUTBOX payload
  is InboxMemberEvent.
- TestHandler_ProcessRemoveMember_SelfLeave_IndividualOnly +
  _OwnerRemovesIndividual + _OwnerRemovesOrg — bump expected publish
  count +1 and assert the local INBOX publish carries the right
  Accounts.
- TestHandler_ProcessRemoveMember_CrossSiteOutbox — assert OUTBOX
  payload is InboxMemberEvent (not MemberRemoveEvent) and confirm NO
  local INBOX publish fires when the removed user is remote.

Follows PR #109.

https://claude.ai/code/session_01XTmSpmv5dT6UXX7NpRdYqN
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 20, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e38d7368-0b33-4ba7-bd7c-db5f1a86eab6

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/room-worker-inbox-publish

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Collaborator

@mliu33 mliu33 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent work, thanks!

@mliu33 mliu33 merged commit 711cad4 into claude/recover-search-sync-worker-dCjyV Apr 21, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants