Skip to content

feat(room-service): add member.statuses + subscription.mentionable RPCs#260

Open
vjauhari-work wants to merge 3 commits into
mainfrom
claude/zen-volta-mZ7G4
Open

feat(room-service): add member.statuses + subscription.mentionable RPCs#260
vjauhari-work wants to merge 3 commits into
mainfrom
claude/zen-volta-mZ7G4

Conversation

@vjauhari-work
Copy link
Copy Markdown
Collaborator

@vjauhari-work vjauhari-work commented Jun 2, 2026

Summary

  • Adds two read-only NATS RPCs to room-service for the message-composer experience: member.statuses (lists room members with their engName/chineseName/statusIsShow/statusText) and subscription.mentionable (@-mention autocomplete returning a discriminated user | app union with a regex-escaped substring filter over a 5-field keyword).
  • Both handlers are membership-gated, parallelize GetSubscription + GetRoom via errgroup (membership error takes precedence on conflict), emit room.id/site.id OTel span attributes, and clamp a missing limit to the room cap (or short-circuit empty when the room is empty). The mentionable pipeline hides p_ platform-admin accounts entirely; the member.statuses pipeline applies $limit after the user-join so orphan subscriptions in the prefix don't cause silent under-delivery.
  • Refactors bot/admin classification into model.User.IsBot() / model.User.IsPlatformAdmin() — narrows the predicate so only .bot-suffixed accounts are bots and p_ accounts are platform admins (no longer conflated). Drops the package-level pkg/model.IsBotAccount and consolidates all room-service and room-worker call sites onto the methods.

Supporting changes

  • pkg/model/member.go: new MemberStatus, MentionableSubscription (+ inner HRInfo/App/AppAssistant), and request/response wrapper types.
  • pkg/model/user.go: new StatusIsShow, StatusText fields on User; IsBot() / IsPlatformAdmin() methods.
  • pkg/subject/subject.go: new MemberStatuses/MemberStatusesWildcard + MentionableSubscriptions/MentionableSubscriptionsWildcard builders.
  • room-service/store.go + store_mongo.go: new ListMemberStatuses and ListMentionableSubscriptions store methods; shared botAccountRegex + platformAdminRegex constants for the Mongo $regexMatch stages.
  • room-service/helper.go: two new sentinel errors (errMemberStatusesLimitInvalid, errMentionableLimitInvalid) wired into sanitizeError; the shared errNotRoomMember text is generalized to "not a room member" now that four handlers reuse it.
  • docs/client-api.md: full request/response/error documentation for both RPCs.

Test plan

  • make lint — 0 issues
  • make test — unit tests green with -race (12 + 13 handler subtests, plus pkg/model + pkg/subject + room-worker)
  • Cross-engine integration test (TestBotAndAdminPredicate_GoAndMongoAgree_Integration) locks Go predicates against the Mongo regex on 9 probes (.bot, p_, look-alikes, case sensitivity, boundary cases)
  • make test-integration SERVICE=room-service — runs in CI (Docker not available locally); covers ListMemberStatuses (5 subtests incl. post-join $limit under-delivery regression) and ListMentionableSubscriptions (8 subtests incl. p_ exclusion + orphan-row null-leaf safety)
  • make sast — runs in CI per CLAUDE.md §5

https://claude.ai/code/session_01P23mygfBErAmFFMd1VVtWr


Generated by Claude Code

Summary by CodeRabbit

  • New Features

    • Added member-statuses listing and mentionable-subscriptions autocomplete (membership-gated, substring filtering, sensible defaults, and empty-room short-circuits).
    • Users can show a visible "show" flag and status text.
  • Bug Fixes

    • Standardized membership error messaging to "not a room member".
    • Added explicit validation errors for invalid limits.
  • Documentation

    • Published API docs, design spec, and examples for both endpoints.
  • Tests

    • Added unit and integration tests for shapes, limits, filtering, classification, and store behavior.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 2, 2026

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds two room-scoped read RPCs (member.statuses, subscription.mentionable) with model/contracts, subject builders, RoomStore interface methods and Mongo aggregations, NATS handlers with membership gating and limit/filter validation, unit/integration tests, and client API doc updates.

Changes

Member Statuses & Mentionable Subscriptions Implementation

Layer / File(s) Summary
Client API docs standardization
docs/client-api.md
Standardizes membership-gated error wording to "not a room member" across several room RPC docs and adjusts one example error text for Mark Messages Read.
Design & Implementation Plan Documentation
docs/superpowers/specs/..., docs/superpowers/plans/...
Adds a full design spec and implementation plan covering subjects, model shapes, handler/store flows, Mongo aggregation strategies, error sentinel definitions, and test matrix.
Model types and DTOs
pkg/model/member.go
Adds request/response DTOs and projection types for member.statuses and subscription.mentionable, using pointer nested structs and omitempty to model discriminated-union shapes.
User Model Status Fields
pkg/model/user.go
Adds StatusIsShow and StatusText fields to model.User with JSON/BSON tags.
Model JSON contract tests
pkg/model/model_test.go
Adds round-trip and shape tests for new User fields, member-status requests/responses, and mentionable-subscription discrimination/omission behavior.
NATS Subject Builders & Routing Patterns
pkg/subject/subject.go, pkg/subject/subject_test.go
Adds concrete subject builders and per-site wildcard patterns for both RPCs and tests asserting parseability via ParseUserRoomSubject.
Error Sentinels & Classification Constants
room-service/helper.go, room-service/helper_test.go
Documents shared errNotRoomMember, adds platformAdminRegex and two new limit-validation sentinel errors, and extends tests for sentinel mapping.
Store Interface & Mocks
room-service/store.go, room-service/mock_store_test.go
Extends RoomStore with ListMemberStatuses and ListMentionableSubscriptions; updates generated mocks for unit testing.
MongoStore Aggregations
room-service/store_mongo.go
Implements aggregation pipelines: member-statuses joins subscriptions→users and drops orphans before limiting; mentionable-subscriptions joins users/apps, excludes platform-admins, classifies app vs user, constructs keyword for case-insensitive escaped-filter matching, and projects discriminated results.
Handler Registration, Membership Gating & Validation
room-service/handler.go
Registers NATS queue subscriptions for both RPCs; adds requireMembershipAndGetRoom to concurrently check membership and load room (membership error precedence); implements handlers with default/clamped/validated limits, zero-cap short-circuits, and regex-escaped filter handling.
Handler Unit Tests
room-service/handler_test.go
Adds TestHandler_ListMemberStatuses and TestHandler_ListMentionableSubscriptions covering defaults, nil-limit clamping, explicit limits, membership gating and race precedence, filter escaping, error propagation, invalid subject, and malformed JSON cases.
Integration & Bot-Predicate Agreement Tests
room-service/integration_test.go
Adds integration tests for ListMemberStatuses and ListMentionableSubscriptions validating projection, filtering, limits, orphan handling, classification, and a Go/Mongo agreement probe for bot/platform-admin predicates.

Sequence Diagram

sequenceDiagram
  participant Client
  participant RoomService as RoomService.Handler
  participant MongoStore as MongoStore
  Client->>RoomService: Publish request on subject (member.statuses / subscription.mentionable)
  RoomService->>RoomService: Parse subject, decode JSON, set trace attrs
  RoomService->>MongoStore: GetSubscription (membership probe)
  RoomService->>MongoStore: GetRoom (room probe)
  RoomService->>MongoStore: ListMemberStatuses / ListMentionableSubscriptions (escapedFilter, limit)
  MongoStore-->>RoomService: Aggregation results
  RoomService-->>Client: Sanitized JSON reply or sentinel error
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • hmchangw/chat#218: Touches the shared room-service membership-gated sentinel/error path related to errNotRoomMember.
  • hmchangw/chat#108: Also modifies membership-gating sentinel handling in room-service.
  • hmchangw/chat#72: Related changes to helper-level sentinel/sanitizeError plumbing in room-service/helper.go.

Suggested labels

ready

Suggested reviewers

  • mliu33
  • Allan-Code-hub

Poem

🐰 Two RPCs hop into the room,
Status flags glow and mentioners bloom,
Regex-quoted filters snug and tight,
Mongo joins bring results to light,
Membership gates keep order right.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(room-service): add member.statuses + subscription.mentionable RPCs' is clear, specific, and accurately describes the main changes—two new RPC endpoints added to room-service.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/zen-volta-mZ7G4

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.

@vjauhari-work
Copy link
Copy Markdown
Collaborator Author

@coderabbitai full-review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 2, 2026

@vjauhari-work Sure! I'll kick off a full review of this PR right away.

✅ Actions performed

Full review triggered.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (2)
room-service/handler_test.go (1)

4046-4266: ⚡ Quick win

Add a regression case for GetRoom failing before GetSubscription completes.

These suites cover the happy/error branches well, but they don't pin the cancellation race inside requireMembershipAndGetRoom. A test that forces GetRoom to fail first and GetSubscription to observe cancellation would catch the current error-masking bug and keep the precedence contract from regressing.

Also applies to: 4308-4554

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@room-service/handler_test.go` around lines 4046 - 4266, Add a regression test
to TestHandler_ListMemberStatuses that forces GetRoom to fail before
GetSubscription finishes to exercise requireMembershipAndGetRoom’s cancellation
race: mock MockRoomStore.GetRoom to immediately return an error (e.g.
fmt.Errorf("mongo exploded")) and mock MockRoomStore.GetSubscription to
block/return after context cancellation (simulate by returning a
channel-blocking stub or an error wrapped with context.Canceled), then call
Handler.handleListMemberStatuses and assert the returned error originates from
the GetRoom path (contains "get room" or matches the expected store error).
Reference the functions/objects: Handler.handleListMemberStatuses,
requireMembershipAndGetRoom, MockRoomStore.GetRoom,
MockRoomStore.GetSubscription, and ensure the test asserts the GetRoom error is
not masked by a subscription cancellation.
room-service/handler.go (1)

459-467: ⚡ Quick win

Add the request ID to these new error logs.

Both handlers already build ctx via wrappedCtx, but the new slog.Error calls drop the correlation ID, which makes cross-service tracing harder when these RPCs fail.

Suggested fix
 func (h *Handler) natsListMemberStatuses(m otelnats.Msg) {
 	ctx := wrappedCtx(m)
+	requestID := natsutil.RequestIDFromContext(ctx)
 	resp, err := h.handleListMemberStatuses(ctx, m.Msg.Subject, m.Msg.Data)
 	if err != nil {
-		slog.Error("list member statuses failed", "error", err, "subject", m.Msg.Subject)
+		slog.Error("list member statuses failed", "error", err, "subject", m.Msg.Subject, "request_id", requestID)
 		natsutil.ReplyError(m.Msg, sanitizeError(err))
 		return
 	}
 	natsutil.ReplyJSON(m.Msg, resp)
 }

 func (h *Handler) natsListMentionableSubscriptions(m otelnats.Msg) {
 	ctx := wrappedCtx(m)
+	requestID := natsutil.RequestIDFromContext(ctx)
 	resp, err := h.handleListMentionableSubscriptions(ctx, m.Msg.Subject, m.Msg.Data)
 	if err != nil {
-		slog.Error("list mentionable subscriptions failed", "error", err, "subject", m.Msg.Subject)
+		slog.Error("list mentionable subscriptions failed", "error", err, "subject", m.Msg.Subject, "request_id", requestID)
 		natsutil.ReplyError(m.Msg, sanitizeError(err))
 		return
 	}
 	natsutil.ReplyJSON(m.Msg, resp)
 }

As per coding guidelines, "Generate or extract a unique request/correlation ID at the entry point ... include in all log lines".

Also applies to: 559-567

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@room-service/handler.go` around lines 459 - 467, The error logs in
natsListMemberStatuses are missing the request/correlation ID; update the
slog.Error call to include the request ID pulled from ctx (use the same context
key or helper used elsewhere in the package, e.g., requestIDFromCtx(ctx) or
ctx.Value(requestIDKey)) as an extra field (e.g., "request_id", id) so the log
contains the correlation id; apply the same change to the other NATS handler in
this file that has a similar slog.Error call later in the file.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@docs/superpowers/plans/2026-05-29-room-member-statuses-and-mentionable-subscriptions.md`:
- Around line 243-249: Update the documented default and error text to match
shipped behavior: change MentionableSubscriptionsRequest's default Limit from 1
to 3 (and note the constraint stays >0 and <= room.UserCount + room.AppCount)
and replace the old membership error string "only room members can list members"
with the generalized "not a room member" wherever the
subscription.mentionable/member-status contract is documented (including the
other occurrences referenced). Locate mentions by symbol names like
MentionableSubscriptionsRequest and any documented RPC error messages for
subscription.mentionable/member-status and update the text accordingly so
generated tests/docs reflect the final contract.
- Around line 7-9: Update the plan to reflect the landed account classification
by replacing the old combined bot/platform-admin rule (`\.bot$|^p_`) with the
new split semantics: treat accounts matching `\.bot$` as assistant/bot accounts
and treat accounts prefixed with `p_` as platform-admin/webhook accounts; update
any prose, examples, and the referenced test steps that rely on `botPattern` or
the regex in the plan sections (including the occurrences around the ranges you
noted) so they assert and document the two distinct rules separately and ensure
references to `botPattern` and any Mongo/Go regex use the two-pattern behavior.

In
`@docs/superpowers/specs/2026-05-29-room-member-statuses-and-mentionable-subscriptions-design.md`:
- Around line 110-114: Update the spec to reflect the implemented default limit
of 3 for mentionable subscriptions: change the comment on the
MentionableSubscriptionsRequest struct from "nil → default 1" to "nil → default
3", replace any inline example assigning `limit := 1` with `limit := 3`, and
update the descriptive text that says "default of 1 — also intentionally tight"
to state the default is 3; ensure these edits align with the implementation in
pkg/model/member.go and the PR summary which raised the default to 3.
- Around line 47-64: The spec and implementation disagree on the bot regex:
either update the spec to document the two-constant design (botAccountRegex and
platformAdminRegex) and show how the Feature 2 Mongo pipeline uses both (or a
combined expression derived from them), or change the implementation to match
the spec by consolidating into a single constant (botAccountRegex =
`\.bot$|^p_`) and update botPattern and isBot accordingly; reference the symbols
botAccountRegex, platformAdminRegex, botPattern, and isBot so the spec, unit
tests, and the Feature 2 pipeline all use the same source of truth.

In `@room-service/handler.go`:
- Around line 487-509: The concurrent fetch uses errgroup.WithContext which
cancels the sibling call on the first error, causing GetSubscription to surface
context.Canceled instead of its original error; replace
errgroup.WithContext(ctx) with a plain errgroup.Group (e.g., declare var g
errgroup.Group), call h.store.GetSubscription and h.store.GetRoom with the
original ctx (not gctx), keep assigning subErr and roomErr from those
goroutines, and preserve the existing post-Wait checks (errors.Is(subErr,
model.ErrSubscriptionNotFound) -> errNotRoomMember, then other error wraps) so
both real errors are visible and priority is unchanged.

---

Nitpick comments:
In `@room-service/handler_test.go`:
- Around line 4046-4266: Add a regression test to TestHandler_ListMemberStatuses
that forces GetRoom to fail before GetSubscription finishes to exercise
requireMembershipAndGetRoom’s cancellation race: mock MockRoomStore.GetRoom to
immediately return an error (e.g. fmt.Errorf("mongo exploded")) and mock
MockRoomStore.GetSubscription to block/return after context cancellation
(simulate by returning a channel-blocking stub or an error wrapped with
context.Canceled), then call Handler.handleListMemberStatuses and assert the
returned error originates from the GetRoom path (contains "get room" or matches
the expected store error). Reference the functions/objects:
Handler.handleListMemberStatuses, requireMembershipAndGetRoom,
MockRoomStore.GetRoom, MockRoomStore.GetSubscription, and ensure the test
asserts the GetRoom error is not masked by a subscription cancellation.

In `@room-service/handler.go`:
- Around line 459-467: The error logs in natsListMemberStatuses are missing the
request/correlation ID; update the slog.Error call to include the request ID
pulled from ctx (use the same context key or helper used elsewhere in the
package, e.g., requestIDFromCtx(ctx) or ctx.Value(requestIDKey)) as an extra
field (e.g., "request_id", id) so the log contains the correlation id; apply the
same change to the other NATS handler in this file that has a similar slog.Error
call later in the file.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 96e49861-3c7c-496c-a947-e16c4fa7abef

📥 Commits

Reviewing files that changed from the base of the PR and between 11b360f and c657ab1.

📒 Files selected for processing (22)
  • docs/client-api.md
  • docs/superpowers/plans/2026-05-29-room-member-statuses-and-mentionable-subscriptions.md
  • docs/superpowers/specs/2026-05-29-room-member-statuses-and-mentionable-subscriptions-design.md
  • pkg/model/account.go
  • pkg/model/account_test.go
  • pkg/model/member.go
  • pkg/model/model_test.go
  • pkg/model/user.go
  • pkg/subject/subject.go
  • pkg/subject/subject_test.go
  • room-service/handler.go
  • room-service/handler_test.go
  • room-service/helper.go
  • room-service/helper_test.go
  • room-service/integration_test.go
  • room-service/mock_store_test.go
  • room-service/store.go
  • room-service/store_mongo.go
  • room-worker/handler.go
  • room-worker/integration_test.go
  • room-worker/store_mongo.go
  • room-worker/subscription_isbot_test.go
💤 Files with no reviewable changes (2)
  • pkg/model/account.go
  • pkg/model/account_test.go

Comment thread room-service/handler.go Outdated
vjauhari-work pushed a commit that referenced this pull request Jun 2, 2026
Concurrency fix:
  - requireMembershipAndGetRoom: switch from errgroup.WithContext to plain
    errgroup.Group. WithContext cancelled the sibling on first error, so a
    fast GetRoom failure could turn GetSubscription into context.Canceled
    and mask errNotRoomMember. Both probes now run to completion on the
    original ctx; precedence is enforced post-Wait. (CodeRabbit Major)

Test coverage:
  - Add a "not-member takes precedence over GetRoom error" subtest to both
    TestHandler_ListMemberStatuses and TestHandler_ListMentionableSubscriptions
    — locks in the precedence contract that the errgroup fix preserves.
    (CodeRabbit nitpick)

Observability:
  - Add requestID via natsutil.RequestIDFromContext to the slog.Error in
    natsListMemberStatuses and natsListMentionableSubscriptions, per
    CLAUDE.md "include request ID in all log lines". (CodeRabbit nitpick)

Spec/plan drift:
  - Spec §3.3 rewritten to describe the two-constant split
    (botAccountRegex + platformAdminRegex) the code actually uses, with
    the User.IsBot / User.IsPlatformAdmin Go-side equivalents.
  - Spec MentionableSubscriptionsRequest comment + example + "Future
    optimizations" note updated for the default limit of 3 (was 1).
  - Plan: added an explicit "Drift from shipped implementation" preamble
    listing the four design-vs-code divergences (split classification,
    default limit 3, "not a room member" error text, parallelized
    membership probe). Plus targeted edits to the affected code snippets
    inside the plan so cut-and-paste from those examples doesn't produce
    stale behavior.

Skipped:
  - CodeRabbit's "Docstring Coverage 38% < 80%" pre-merge warning runs
    counter to CLAUDE.md's "default to writing no comments" guidance.
    Not a project-side requirement.
@vjauhari-work vjauhari-work force-pushed the claude/zen-volta-mZ7G4 branch from d490cbd to 7e8d9ed Compare June 2, 2026 05:05
vjauhari-work pushed a commit that referenced this pull request Jun 2, 2026
Concurrency fix:
  - requireMembershipAndGetRoom: switch from errgroup.WithContext to plain
    errgroup.Group. WithContext cancelled the sibling on first error, so a
    fast GetRoom failure could turn GetSubscription into context.Canceled
    and mask errNotRoomMember. Both probes now run to completion on the
    original ctx; precedence is enforced post-Wait. (CodeRabbit Major)

Test coverage:
  - Add a "not-member takes precedence over GetRoom error" subtest to both
    TestHandler_ListMemberStatuses and TestHandler_ListMentionableSubscriptions
    — locks in the precedence contract that the errgroup fix preserves.
    (CodeRabbit nitpick)

Observability:
  - Add requestID via natsutil.RequestIDFromContext to the slog.Error in
    natsListMemberStatuses and natsListMentionableSubscriptions, per
    CLAUDE.md "include request ID in all log lines". (CodeRabbit nitpick)

Spec/plan drift:
  - Spec §3.3 rewritten to describe the two-constant split
    (botAccountRegex + platformAdminRegex) the code actually uses, with
    the User.IsBot / User.IsPlatformAdmin Go-side equivalents.
  - Spec MentionableSubscriptionsRequest comment + example + "Future
    optimizations" note updated for the default limit of 3 (was 1).
  - Plan: added an explicit "Drift from shipped implementation" preamble
    listing the four design-vs-code divergences (split classification,
    default limit 3, "not a room member" error text, parallelized
    membership probe). Plus targeted edits to the affected code snippets
    inside the plan so cut-and-paste from those examples doesn't produce
    stale behavior.

Skipped:
  - CodeRabbit's "Docstring Coverage 38% < 80%" pre-merge warning runs
    counter to CLAUDE.md's "default to writing no comments" guidance.
    Not a project-side requirement.
@vjauhari-work vjauhari-work force-pushed the claude/zen-volta-mZ7G4 branch 2 times, most recently from 96481c0 to 757e98d Compare June 3, 2026 07:10
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (2)
docs/superpowers/plans/2026-05-29-room-member-statuses-and-mentionable-subscriptions.md (2)

700-739: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Critical: Implementation guidance has incorrect $limit placement.

The pipeline (line 708) applies $limit BEFORE $lookup, identical to the design spec issue. This implementation will be copy-pasted by developers and will ship the bug.

Problem: If the first N subscriptions include orphaned users, the response will have fewer than limit members even when valid members exist.

See the design spec review comment for detailed explanation and fix.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@docs/superpowers/plans/2026-05-29-room-member-statuses-and-mentionable-subscriptions.md`
around lines 700 - 739, The pipeline in ListMemberStatuses applies the $limit
before the $lookup/$unwind, so orphaned subscriptions can reduce returned
members; move the $limit so it is applied after the $lookup/$unwind (i.e., limit
the resulting joined user documents), ensuring the stage ordering is: $match,
$lookup, $unwind (preserveNullAndEmptyArrays:false), $replaceWith (or $project),
then $limit (using int64(limit)) before aggregation execution; update the
mongo.Pipeline accordingly so ListMemberStatuses returns up to `limit` valid
member documents.

1218-1276: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Major: Implementation is missing platform-admin filtering.

The pipeline (lines 1230-1234) filters excludeAccount but does not filter out p_ platform-admin accounts. Per the drift note (lines 15-16), "the mentionable pipeline...hides p_ accounts entirely."

This implementation guidance will ship without the required filtering.

🔧 Add the missing filter
     pipeline := mongo.Pipeline{
         {{Key: "$match", Value: bson.M{
             "roomId":    roomID,
             "u.account": bson.M{"$ne": excludeAccount},
         }}},
+        {{Key: "$match", Value: bson.M{
+            "u.account": bson.M{"$not": bson.M{"$regex": platformAdminRegex}},
+        }}},
         {{Key: "$lookup", Value: bson.M{
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@docs/superpowers/plans/2026-05-29-room-member-statuses-and-mentionable-subscriptions.md`
around lines 1218 - 1276, The ListMentionableSubscriptions pipeline is missing
the platform-admin exclusion — update the initial $match in the pipeline (the
bson.M that currently has "roomId": roomID and "u.account": bson.M{"$ne":
excludeAccount}) to also exclude accounts that start with "p_" (platform
admins); for example add a condition on "u.account" such as bson.M{"$ne":
excludeAccount, "$not": bson.RegEx{Pattern: "^p_", Options: ""}} (or an
equivalent "$not": bson.M{"$regex": "^p_"}) so platform-admin accounts are
filtered out alongside excludeAccount.
🧹 Nitpick comments (1)
docs/superpowers/plans/2026-05-29-room-member-statuses-and-mentionable-subscriptions.md (1)

1663-1704: ⚡ Quick win

Minor: Test references undefined isBot() helper.

Line 1687 calls isBot(acct) but this function is never defined in the integration test file or imported.

Likely intent: A local test helper that implements strings.HasSuffix(acct, ".bot") to match the Go-side classification, separate from the (*model.User).IsBot() method.

Suggestion: Add the helper definition to the test preamble for clarity:

// isBot returns true if account ends in ".bot" (assistant bot).
// Matches the classification in (*model.User).IsBot().
func isBot(account string) bool {
    return strings.HasSuffix(account, ".bot")
}

This makes the test self-contained and documents the Go-side classification rule being verified.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@docs/superpowers/plans/2026-05-29-room-member-statuses-and-mentionable-subscriptions.md`
around lines 1663 - 1704, The test calls an undefined helper isBot(acct); add a
local helper function isBot(account string) bool that returns
strings.HasSuffix(account, ".bot") (and import "strings" if needed), placing it
in the test file preamble so TestBotPattern_GoAndMongoAgree_Integration can call
isBot() to mirror the Go-side classification used when seeding
Subscription.User.IsBot.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@docs/superpowers/specs/2026-05-29-room-member-statuses-and-mentionable-subscriptions-design.md`:
- Around line 358-414: The pipeline in ListMentionableSubscriptions is missing a
filter to exclude platform admin accounts (those matching platformAdminRegex),
so add an early $match stage (before the $lookup stages) that applies
{"u.account": {"$not": platformAdminRegex}} (together with the existing roomId
and excludeAccount conditions) to prevent any "p_" accounts from proceeding
through the joins and appearing in results; update the mongo.Pipeline
construction in ListMentionableSubscriptions to include this $match as the
first/second stage so platform admins are fully excluded from mention
autocomplete.
- Around line 253-290: The pipeline in ListMemberStatuses applies the $limit
before $lookup/$unwind, which can return fewer than the requested valid members
when early subscriptions are orphaned; move the $limit stage to after the
$unwind (or after the $replaceWith) so that orphaned subscriptions are dropped
first and the limit is applied to the post-join, filtered member documents;
update the mongo.Pipeline in ListMemberStatuses accordingly and keep
preserveNullAndEmptyArrays: false so the later $limit bounds the number of valid
members.

---

Duplicate comments:
In
`@docs/superpowers/plans/2026-05-29-room-member-statuses-and-mentionable-subscriptions.md`:
- Around line 700-739: The pipeline in ListMemberStatuses applies the $limit
before the $lookup/$unwind, so orphaned subscriptions can reduce returned
members; move the $limit so it is applied after the $lookup/$unwind (i.e., limit
the resulting joined user documents), ensuring the stage ordering is: $match,
$lookup, $unwind (preserveNullAndEmptyArrays:false), $replaceWith (or $project),
then $limit (using int64(limit)) before aggregation execution; update the
mongo.Pipeline accordingly so ListMemberStatuses returns up to `limit` valid
member documents.
- Around line 1218-1276: The ListMentionableSubscriptions pipeline is missing
the platform-admin exclusion — update the initial $match in the pipeline (the
bson.M that currently has "roomId": roomID and "u.account": bson.M{"$ne":
excludeAccount}) to also exclude accounts that start with "p_" (platform
admins); for example add a condition on "u.account" such as bson.M{"$ne":
excludeAccount, "$not": bson.RegEx{Pattern: "^p_", Options: ""}} (or an
equivalent "$not": bson.M{"$regex": "^p_"}) so platform-admin accounts are
filtered out alongside excludeAccount.

---

Nitpick comments:
In
`@docs/superpowers/plans/2026-05-29-room-member-statuses-and-mentionable-subscriptions.md`:
- Around line 1663-1704: The test calls an undefined helper isBot(acct); add a
local helper function isBot(account string) bool that returns
strings.HasSuffix(account, ".bot") (and import "strings" if needed), placing it
in the test file preamble so TestBotPattern_GoAndMongoAgree_Integration can call
isBot() to mirror the Go-side classification used when seeding
Subscription.User.IsBot.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0d012e8b-72c0-445e-a58f-26feee7aea8e

📥 Commits

Reviewing files that changed from the base of the PR and between c657ab1 and 96481c0.

📒 Files selected for processing (16)
  • docs/client-api.md
  • docs/superpowers/plans/2026-05-29-room-member-statuses-and-mentionable-subscriptions.md
  • docs/superpowers/specs/2026-05-29-room-member-statuses-and-mentionable-subscriptions-design.md
  • pkg/model/member.go
  • pkg/model/model_test.go
  • pkg/model/user.go
  • pkg/subject/subject.go
  • pkg/subject/subject_test.go
  • room-service/handler.go
  • room-service/handler_test.go
  • room-service/helper.go
  • room-service/helper_test.go
  • room-service/integration_test.go
  • room-service/mock_store_test.go
  • room-service/store.go
  • room-service/store_mongo.go
💤 Files with no reviewable changes (13)
  • pkg/subject/subject_test.go
  • pkg/model/user.go
  • pkg/subject/subject.go
  • room-service/helper.go
  • room-service/store.go
  • room-service/handler.go
  • room-service/helper_test.go
  • room-service/integration_test.go
  • pkg/model/member.go
  • room-service/store_mongo.go
  • pkg/model/model_test.go
  • room-service/mock_store_test.go
  • room-service/handler_test.go
✅ Files skipped from review due to trivial changes (1)
  • docs/client-api.md

@vjauhari-work vjauhari-work force-pushed the claude/zen-volta-mZ7G4 branch 2 times, most recently from 6d950c9 to c24fd4a Compare June 4, 2026 02:24
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
docs/superpowers/plans/2026-05-29-room-member-statuses-and-mentionable-subscriptions.md (1)

1376-1386: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Unify mentionable default-limit guidance to 3 across the plan.

Several executable snippets/checklists still encode default 1, which conflicts with the drift note and shipped behavior and can cause future replays/tests/docs to regress.

Suggested doc diff
- name:    "default limit 1, empty filter, happy path",
+ name:    "default limit 3, empty filter, happy path",

- ListMentionableSubscriptions(gomock.Any(), roomID, requester, "", 1).
+ ListMentionableSubscriptions(gomock.Any(), roomID, requester, "", 3).

- | `limit`  | number | no       | Defaults to `1`. Must be `> 0` and `<= room.userCount + room.appCount`. |
+ | `limit`  | number | no       | Defaults to `3` (effectively `min(3, room.userCount + room.appCount)`). Must be `> 0` and `<= room.userCount + room.appCount`. |

- - Default-limit numbers (3 / 1) match between handler code, tests, docs, and the spec.
+ - Default-limit numbers (3 / 3) match between handler code, tests, docs, and the spec.

Also applies to: 1502-1503, 1833-1834, 1941-1941

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@docs/superpowers/plans/2026-05-29-room-member-statuses-and-mentionable-subscriptions.md`
around lines 1376 - 1386, The tests and doc snippets that call
ListMentionableSubscriptions (e.g., the case named "default limit 1, empty
filter, happy path") still use a default limit of 1; update those calls to use
the unified default limit of 3 (change the literal 1 passed to
ListMentionableSubscriptions to 3) and update the other similar occurrences
referenced in the plan (the spots around the other examples that call
ListMentionableSubscriptions) so the examples, tests, and docs consistently
reflect the shipped default of 3.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@docs/superpowers/plans/2026-05-29-room-member-statuses-and-mentionable-subscriptions.md`:
- Around line 20-22: The doc contains a typo in the membership-error wording
that renders the contrast meaningless; update the sentence to show the intended
contrasting error strings and correct the drift-note so it accurately states
which text is used (e.g., clarify that the shared sentinel errNotRoomMember
yields "not a room member" across the four membership-gated RPCs rather than
repeating the same phrase twice). Locate the sentence referencing
errNotRoomMember and replace the incorrect quoted pair with the correct
contrasting phrases so the note clearly communicates the intended difference.

---

Duplicate comments:
In
`@docs/superpowers/plans/2026-05-29-room-member-statuses-and-mentionable-subscriptions.md`:
- Around line 1376-1386: The tests and doc snippets that call
ListMentionableSubscriptions (e.g., the case named "default limit 1, empty
filter, happy path") still use a default limit of 1; update those calls to use
the unified default limit of 3 (change the literal 1 passed to
ListMentionableSubscriptions to 3) and update the other similar occurrences
referenced in the plan (the spots around the other examples that call
ListMentionableSubscriptions) so the examples, tests, and docs consistently
reflect the shipped default of 3.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d8363f61-2b3a-4ad2-9613-e4b289787c43

📥 Commits

Reviewing files that changed from the base of the PR and between 96481c0 and c24fd4a.

📒 Files selected for processing (16)
  • docs/client-api.md
  • docs/superpowers/plans/2026-05-29-room-member-statuses-and-mentionable-subscriptions.md
  • docs/superpowers/specs/2026-05-29-room-member-statuses-and-mentionable-subscriptions-design.md
  • pkg/model/member.go
  • pkg/model/model_test.go
  • pkg/model/user.go
  • pkg/subject/subject.go
  • pkg/subject/subject_test.go
  • room-service/handler.go
  • room-service/handler_test.go
  • room-service/helper.go
  • room-service/helper_test.go
  • room-service/integration_test.go
  • room-service/mock_store_test.go
  • room-service/store.go
  • room-service/store_mongo.go
💤 Files with no reviewable changes (10)
  • room-service/helper.go
  • pkg/subject/subject_test.go
  • room-service/store.go
  • pkg/subject/subject.go
  • room-service/mock_store_test.go
  • room-service/store_mongo.go
  • room-service/helper_test.go
  • room-service/handler_test.go
  • room-service/handler.go
  • room-service/integration_test.go
✅ Files skipped from review due to trivial changes (1)
  • docs/client-api.md
🚧 Files skipped from review as they are similar to previous changes (3)
  • pkg/model/member.go
  • pkg/model/user.go
  • pkg/model/model_test.go

@vjauhari-work vjauhari-work force-pushed the claude/zen-volta-mZ7G4 branch 2 times, most recently from 2b5658d to 951c7b0 Compare June 4, 2026 06:18
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@room-service/handler.go`:
- Around line 668-670: The fast-path returns use the zero-value
model.ListMemberStatusesResponse{} which JSON-encodes slices as null; change
these return sites (the branch checking room.UserCount == 0 and the other
empty-room branch around the subscriptions return) to return an explicit
response with empty slices (e.g. Members: []model.MemberStatus{} and
Subscriptions: []model.Subscription{} as appropriate) so JSON encodes [] instead
of null; locate the returns that currently return
model.ListMemberStatusesResponse{} and replace them with a constructed
model.ListMemberStatusesResponse containing the empty slice fields.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3e8baa11-6b50-4c1f-8e1a-12d4e8bf2aa1

📥 Commits

Reviewing files that changed from the base of the PR and between c24fd4a and 951c7b0.

📒 Files selected for processing (16)
  • docs/client-api.md
  • docs/superpowers/plans/2026-05-29-room-member-statuses-and-mentionable-subscriptions.md
  • docs/superpowers/specs/2026-05-29-room-member-statuses-and-mentionable-subscriptions-design.md
  • pkg/model/member.go
  • pkg/model/model_test.go
  • pkg/model/user.go
  • pkg/subject/subject.go
  • pkg/subject/subject_test.go
  • room-service/handler.go
  • room-service/handler_test.go
  • room-service/helper.go
  • room-service/helper_test.go
  • room-service/integration_test.go
  • room-service/mock_store_test.go
  • room-service/store.go
  • room-service/store_mongo.go
✅ Files skipped from review due to trivial changes (1)
  • room-service/mock_store_test.go
🚧 Files skipped from review as they are similar to previous changes (11)
  • pkg/subject/subject.go
  • room-service/store.go
  • room-service/store_mongo.go
  • pkg/subject/subject_test.go
  • pkg/model/user.go
  • room-service/helper_test.go
  • room-service/integration_test.go
  • pkg/model/model_test.go
  • docs/client-api.md
  • room-service/helper.go
  • room-service/handler_test.go

Comment thread room-service/handler.go
@vjauhari-work vjauhari-work force-pushed the claude/zen-volta-mZ7G4 branch 2 times, most recently from 469f645 to da32f18 Compare June 4, 2026 09:19
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (2)
docs/superpowers/plans/2026-05-29-room-member-statuses-and-mentionable-subscriptions.md (1)

20-23: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Update the drift note membership error text to the shipped sentinel string.

This still says the shared text is "only room members can perform this action", but the shipped contract is "not a room member" across membership-gated RPCs.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@docs/superpowers/plans/2026-05-29-room-member-statuses-and-mentionable-subscriptions.md`
around lines 20 - 23, Update the drift note to reflect the actual shipped
sentinel string by replacing the quoted membership error text `"only room
members can perform this action"` with the shipped `"not a room member"`;
reference the shared sentinel errNotRoomMember which is reused across the four
membership-gated RPCs so the wording is RPC-agnostic and must match that
sentinel.
docs/superpowers/specs/2026-05-29-room-member-statuses-and-mentionable-subscriptions-design.md (1)

20-21: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Remove the stale botPattern contract and align all sections to split bot/admin semantics.

The spec still states combined \.bot$|^p_ behavior and even claims p_ becomes optionType="app", which contradicts the shipped split (.bot => app, p_ => hidden).

Also applies to: 525-526, 564-565

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@docs/superpowers/specs/2026-05-29-room-member-statuses-and-mentionable-subscriptions-design.md`
around lines 20 - 21, The spec incorrectly describes a single `botPattern` regex
(\.bot$|^p_) and claims `p_` becomes optionType="app"; update the document to
remove this stale combined contract and instead consistently describe the
shipped split: `.bot` identifies app accounts (optionType="app") and `^p_`
identifies hidden accounts (optionType="hidden"). Replace any references to a
single `botPattern` and any assertions that `p_` maps to "app" with the explicit
split semantics, and update the sections that mention the Go-side and Mongo
pipeline usage as well as the new `docs/client-api.md` RPC docs to reflect `.bot
=> app` and `p_ => hidden` consistently throughout (including all other
occurrences of `botPattern`, `optionType="app"`, and the RPC descriptions).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@docs/superpowers/plans/2026-05-29-room-member-statuses-and-mentionable-subscriptions.md`:
- Around line 956-993: The handler handleListMemberStatuses currently calls
GetSubscription then GetRoom sequentially; change it to invoke
h.store.GetSubscription(ctx, requesterAccount, roomID) and h.store.GetRoom(ctx,
roomID) in parallel (e.g., goroutines + err channels) and then reconcile results
so subscription membership errors take precedence (if GetSubscription returns
model.ErrSubscriptionNotFound return errNotRoomMember immediately), while other
errors from either call are wrapped normally. Also adjust limit handling to
mirror shipped behavior: treat nil or non-positive req.Limit as the default (3),
clamp the final limit to room.UserCount (and allow empty-room behavior by
permitting limit==0 only if that matches shipped semantics), and keep using
ListMemberStatuses(ctx, roomID, limit) afterward; refer to
handleListMemberStatuses, h.store.GetSubscription, h.store.GetRoom,
h.store.ListMemberStatuses, errNotRoomMember, and errMemberStatusesLimitInvalid
when making changes.

In
`@docs/superpowers/specs/2026-05-29-room-member-statuses-and-mentionable-subscriptions-design.md`:
- Around line 202-203: The membership-check comment and error mapping are
outdated: when h.store.GetSubscription(ctx, requesterAccount, roomID) fails, the
handler currently references the old sentinel text "only room members can list
members"; update the sanitizeError/handler mapping so that the sentinel returned
for subscription/membership failures uses the shared text "not a room member"
instead, ensuring any code paths that call sanitizeError (or the error message
constructed around h.store.GetSubscription) produce the exact sentinel "not a
room member" used by shipped behavior.

---

Duplicate comments:
In
`@docs/superpowers/plans/2026-05-29-room-member-statuses-and-mentionable-subscriptions.md`:
- Around line 20-23: Update the drift note to reflect the actual shipped
sentinel string by replacing the quoted membership error text `"only room
members can perform this action"` with the shipped `"not a room member"`;
reference the shared sentinel errNotRoomMember which is reused across the four
membership-gated RPCs so the wording is RPC-agnostic and must match that
sentinel.

In
`@docs/superpowers/specs/2026-05-29-room-member-statuses-and-mentionable-subscriptions-design.md`:
- Around line 20-21: The spec incorrectly describes a single `botPattern` regex
(\.bot$|^p_) and claims `p_` becomes optionType="app"; update the document to
remove this stale combined contract and instead consistently describe the
shipped split: `.bot` identifies app accounts (optionType="app") and `^p_`
identifies hidden accounts (optionType="hidden"). Replace any references to a
single `botPattern` and any assertions that `p_` maps to "app" with the explicit
split semantics, and update the sections that mention the Go-side and Mongo
pipeline usage as well as the new `docs/client-api.md` RPC docs to reflect `.bot
=> app` and `p_ => hidden` consistently throughout (including all other
occurrences of `botPattern`, `optionType="app"`, and the RPC descriptions).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: dabf03fc-48df-4903-bdaf-f0f9ffd8421c

📥 Commits

Reviewing files that changed from the base of the PR and between 951c7b0 and da32f18.

📒 Files selected for processing (16)
  • docs/client-api.md
  • docs/superpowers/plans/2026-05-29-room-member-statuses-and-mentionable-subscriptions.md
  • docs/superpowers/specs/2026-05-29-room-member-statuses-and-mentionable-subscriptions-design.md
  • pkg/model/member.go
  • pkg/model/model_test.go
  • pkg/model/user.go
  • pkg/subject/subject.go
  • pkg/subject/subject_test.go
  • room-service/handler.go
  • room-service/handler_test.go
  • room-service/helper.go
  • room-service/helper_test.go
  • room-service/integration_test.go
  • room-service/mock_store_test.go
  • room-service/store.go
  • room-service/store_mongo.go
✅ Files skipped from review due to trivial changes (2)
  • room-service/mock_store_test.go
  • docs/client-api.md
🚧 Files skipped from review as they are similar to previous changes (11)
  • room-service/store.go
  • pkg/model/user.go
  • pkg/model/member.go
  • pkg/subject/subject_test.go
  • room-service/helper.go
  • pkg/model/model_test.go
  • room-service/handler.go
  • room-service/store_mongo.go
  • room-service/helper_test.go
  • room-service/handler_test.go
  • room-service/integration_test.go

claude added 3 commits June 4, 2026 09:32
Two new per-room read-only NATS RPCs:

- chat.user.{account}.request.room.{roomID}.{siteID}.member.statuses
  Returns up to `limit` MemberStatus rows (engName, chineseName,
  statusIsShow, statusText) for the room. Default limit is min(3, cap)
  where cap is room.UserCount. Caller must be a room member.

- chat.user.{account}.request.room.{roomID}.{siteID}.subscription.mentionable
  Returns up to `limit` MentionableSubscription rows for @-mention
  autocomplete. Discriminated union: human accounts → HRInfo, ".bot"
  accounts → App + Assistant. Platform-admin ("p_") accounts are
  filtered out at the Mongo $match stage and never appear as mention
  targets. The caller is excluded server-side. Filter is a case-
  insensitive regex match against the dash-joined keyword (account,
  engName, chineseName, app.name, app.assistant.name); regex
  metacharacters in the filter are escaped via regexp.QuoteMeta.
  Default limit is min(3, cap) where cap is room.UserCount +
  room.AppCount.

Wire contracts (pkg/model + pkg/subject):

- pkg/model: MemberStatus, ListMemberStatusesRequest/Response,
  MentionableSubscription discriminated union (user|app),
  ListMentionableSubscriptionsRequest/Response, MentionableHRInfo,
  MentionableApp, MentionableAppAssistant.
- pkg/model/User: new StatusIsShow + StatusText fields surfaced
  through the member.statuses projection.
- pkg/subject: MemberStatuses / MemberStatusesWildcard and
  MentionableSubscriptions / MentionableSubscriptionsWildcard
  builders.

Both handlers use:

- requireMembershipAndGetRoom: sync.WaitGroup-based parallel
  GetSubscription + GetRoom with post-Wait precedence — membership-
  not-found wins over racing GetRoom errors, preventing context.
  Canceled from masking the not-member signal.
- Post-join $limit so orphan subscriptions (user doc hard-deleted)
  don't silently shrink the result below the requested limit.
- errcode-typed errors + errnats.Reply at the boundary, matching
  the pattern established in PR #250.

No outbox / cross-site federation: both RPCs are scoped to the
room's owning siteID and read-only.

Includes unit tests (table-driven, full membership/limit/filter
coverage) and integration tests against testcontainers Mongo.
- docs/client-api.md: wire reference for the two new room-scoped RPCs,
  including subject patterns, request/response shape, the discriminated
  union for mentionable subscriptions (user vs. app), error cases, and
  the platform-admin (p_) filtering rule.
- docs/superpowers/specs: design doc for the feature, covering the
  membership-gate precedence, post-join $limit rationale, filter regex
  escaping, and the choice not to publish to outbox.
- docs/superpowers/plans: implementation plan with TDD checkpoints.
- Plan drift-note "shared membership-error text is X, not Y": the X side
  was a stray duplicate of Y. Set X to the actual shipped string
  ("only room members can perform this action") that errNotRoomMember
  uses across the four membership-gated RPCs.
- Plan/spec mentionable default-limit snippets still showed `1` in:
    - test case name + ListMentionableSubscriptions(... , 1) mock call
    - store-error test mock call
    - request-body table entry
    - drift-grep checklist
    - spec test catalogue
  Updated all to `3` so plan/spec match the shipped default.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants