feat(soup): channel_thread participants filter#4441
Conversation
Add a `participants` filter to the channel_thread entity across all three soup surfaces (typed filters, AST endpoint, and GraphQL). A user is a participant of a thread when they are still an active channel member and they sent the root message or any reply, were @-mentioned anywhere in the thread, or the thread contains a group mention (e.g. @here), which makes every active channel member a participant. - item_filters: `participant_ids` on ChannelThreadFilters and a `Participant` ChannelThreadLiteral variant (AST endpoint picks this up automatically) - channels: compile the literal to an EXISTS subquery over thread messages, user mentions, and group-mention content, gated on active channel membership - graphql_soup: `participant` oneof field on GraphqlChannelThreadLiteral (schema.graphql regenerated) - service-clients: openapi + generated types updated for the new field Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RQWdxN1982fdqbqXdRh337
📝 WalkthroughWalkthroughThis change adds participant-based filtering for channel threads. A new Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant GraphqlSoup
participant ItemFiltersAST
participant PgChannelsRepo
participant Postgres
Client->>GraphqlSoup: GraphqlChannelThreadLiteral(Participant)
GraphqlSoup->>ItemFiltersAST: parse_macro_user_id -> ChannelThreadLiteral::Participant
ItemFiltersAST->>PgChannelsRepo: push_channel_thread_filter_expr(Participant)
PgChannelsRepo->>PgChannelsRepo: push_channel_thread_participant_filter_expr
PgChannelsRepo->>Postgres: EXISTS query (membership, sender, mention match)
Postgres-->>PgChannelsRepo: matching thread rows
PgChannelsRepo-->>Client: filtered threads
Related Issues: None found in provided context. Related PRs: None found in provided context. Suggested labels: rust, graphql, database, feature Suggested reviewers: None specified. Poem: 🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
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. Comment |
…ilter Drop the group-mention content LIKE check from the channel_thread participants filter. The client already expands @here into per-user mention rows for every channel member at send time, so those rows cover group mentions; a TODO on the filter documents the follow-up of persisting group mentions as entity_type='group' rows parsed server-side, which would also cover late joiners and non-expanding producers. Fixtures now mirror production data: the @here threads carry the client-expanded user mention rows, and the departed-user test asserts the membership gate wins over stale expansion rows. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RQWdxN1982fdqbqXdRh337
Adds a
participantsfilter to thechannel_threadentity across all three soup surfaces (regular typed filters, AST endpoint, and GraphQL).A user is a participant of a thread when they are still an active member of the channel AND any of:
Group mentions (
@here) are covered by the second arm: the client expands them into per-user mention rows for every channel member at send time. That expansion is a send-time snapshot from the web client only, so members who join later and messages from non-expanding producers (bots, webhooks) are missed — a TODO on the filter documents the follow-up of persisting group mentions asentity_type = 'group'rows incomms_entity_mentions(parsed server-side from the<m-group-mention>content tag) and treating every active member as a participant of threads containing one.How
item_filters: newparticipant_idsfield onChannelThreadFiltersand aParticipantvariant onChannelThreadLiteral. The AST endpoint (cthf) and MCP toolset pick the new literal up automatically.channels:push_channel_thread_filter_exprcompiles the literal to anEXISTSsubquery over the thread's non-deleted messages (senders) andcomms_entity_mentionsuser mentions, gated on active channel membership (left_at IS NULL), mirroring the semantics ofget_channel_participants_for_thread_id.graphql_soup:participantoneof field onGraphqlChannelThreadLiteral;schema.graphqlregenerated.service-clients:openapi.json+ generated types updated for the new field (storage and search).