Skip to content

tech-debt: memberships DB table is dead — audit readers, align on Matrix/chain as source of truth #2506

Open

Description

@rozagerardo

Context

Discovered while building #2470's chat.message/chat.mention notifications: the DB
memberships table (packages/storage-postgres/src/schema/membership.ts) is effectively dead.

Evidence: prod's memberships table has 2 rows total, both created 2025-03-03, none
since — 18+ months of real space/member activity with zero growth. Searched the entire
packages/apps tree for any code that writes to this table (.insert(memberships) or
equivalent) — found none. No webhook, no server action, no sync job in hypha-web populates
it. It looks like an early attempt at a canonical membership table that was superseded by
reading membership/access live from chain (and, for chat, from Matrix room state) without ever
being cleaned up — some code paths kept querying it, unaware the data was stale/absent.

Where this is known to matter today

  1. #2470's new chat notifications (packages/notifications/src/events/chat/resolver.ts)
    originally resolved "who's in this space" via memberships — always returned zero recipients
    for any real space. Already fixed in #2470: switched to querying Matrix's own room
    membership (matrixGetJoinedRoomMembers, packages/core/src/matrix/server/ matrix-http-client.ts) instead — live, authoritative, and a better architectural fit anyway
    since these events are already Matrix-room-sourced.
  2. assertCanEditCoherence (packages/core/src/coherence/server/mutations.ts) checks
    creator-first, then falls back to a memberships lookup for non-creator editors. If
    memberships is genuinely empty for a space, any member other than a signal's creator has
    likely never been able to edit/interact with signals they don't own
    — a real, currently
    silent permissions gap, separate from notifications entirely. Not investigated further here —
    flagging for this ticket's own triage.
  3. Likely other call sites exist — git grep -n memberships across packages/core/src to find
    every remaining reader before deciding what to do with each one.

Relation to #2450

hypha-web#2450 already raised the same
underlying question for signal_team-scoped call recipients: "does a signal-team concept exist
in the DB, or is it purely inferred — should Matrix room membership be the real source of
truth?" This ticket's finding is the same answer applied more broadly: Matrix room membership
(or on-chain reads, depending on context) is the actual live source of truth across this
codebase; the memberships DB table is not being kept in sync with anything and should either be
properly wired up or retired, not left as a trap for future code to accidentally trust.

Suggested scope (not decided — needs its own triage)

  • Audit every remaining reader of the memberships table
    (git grep -rn "memberships" packages/core/src) and classify each: safe to leave (already
    migrated/fixed), needs migrating to Matrix/chain reads, or acceptable to leave with a
    documented caveat.
  • Root-cause and fix assertCanEditCoherence's non-creator-member edit-permission gap
    (item 2 above) — likely its own scoped fix within this ticket.
  • Decide, together with #2450: fix memberships properly (start writing to it on real
    join/leave events) vs. retire it entirely in favor of Matrix/chain reads everywhere it's
    still consulted.
  • Not in scope: cleaning up the 2 stale prod rows — not worth it, this is about the code
    paths, not the data.

Links

  • #2470 — hypha-web#2470 (where this was
    discovered and partially fixed for chat)
  • #2450 — hypha-web#2450 (same root
    question, narrower scope — signal-team call recipients)
  • Decisions: hypha-context/progress/members/gerroza/tickets/2470-notification-service-redesign/decisions.md D21

Addendum (2026-09-24): scheduled items are affected too

  1. Scheduled-item invitations and reminders. resolveScheduledItemRecipientSlugs (packages/core/src/schedule/server/recipients.ts) falls back to findSpaceMemberSlugsBySpaceId (the dead memberships table) whenever the item is not linked to a signal, or the linked signal has no assignees besides the creator. Space-wide meetings and calls therefore resolve to zero recipients, and both scheduled_item.invited and the reminders route (api/v1/ops/scheduled-items/dispatch-reminders) silently send nothing.

    Separate, undiagnosed: in prod testing after feat(notifications): notification service redesign (decision/delivery layer) + Matrix AS receiver #2507, an invite for a meeting linked to a signal (with a valid assignee who does receive signal.assigned) was also not delivered. It was not investigated. To triage: check prod logs for the create POST .../scheduled-items (Failed to dispatch scheduled item invitation:), whether rows exist in space_scheduled_item_invitation_dispatches, and that the item is meeting/call with auto-link or a meeting URL and a filled coherence_id. Not critical; picked up here rather than as its own ticket.

  • Migrate the space-wide fallback to a live source (on-chain member read as in proposal.created, or Matrix room membership) alongside the other readers.
  • Triage the undiagnosed signal-linked invite non-delivery above.

Activity

  1. added
    choreRepo Management and Architecture
    matrixMatrix chat integration
    on Sep 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    choreRepo Management and ArchitecturematrixMatrix chat integration

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions