Conversation
…multi-member pods
ADR-001 §3.10 says agent-rooms are personal 1:1 DMs (one user, one
agent, no third party). The amendment that corrected the original
"N humans × 1 agent office" framing was documented but never enforced
in code. Live data showed agent-room pods with 5–6 members on dev,
which is a privacy/UX bug: an agent's "DM" had multiple humans in it.
Changes:
1. `joinPod` (podController.ts): reject any third-person join on a
pod whose `type === 'agent-room'` with HTTP 403. Applies to humans
AND agents — the room is closed once the host agent + opening user
are in. Multi-party human↔agent surfaces should use `type: 'chat'`.
2. `ensureAgentInPod` (agentIdentityService.ts): refuse to add an
agent to an agent-room when that agent isn't already a member.
Closes the auto-install path that was sneaking second agents into
existing DMs. Existing-member case is unaffected (host agent
re-running for its own DM still no-ops).
3. `getAllPods` / `getPodsByType` (podController.ts): admin-view
bypass. Global admins skip the membership filter on agent-room +
agent-admin queries so the moderation surface for these private
pod types actually exists. Non-admins still see only their own.
4. Migration script `migrate-agent-room-multimember.ts`: convert
`agent-room` pods with 3+ members to `type: 'chat'`. Preserves all
messages, members, createdBy — only the type field changes. Run
with `--dry` to preview. Idempotent.
5. `getOrCreateAgentRoom` (dmService.ts): comment refresh — was
documenting the rejected N×1 "office" design that the §3.10
amendment killed.
Tests: 4 new podController cases (joinPod reject + chat-pod regression,
admin bypass present + non-admin still filtered) + 3 ensureAgentInPod
cases (refuse, regress chat, no-op for self). All 20 affected tests
pass; no regression in adjacent suites.
Migration on dev needs a manual run after deploy:
kubectl exec -n commonly-dev <backend-pod> -- \\
npx ts-node backend/scripts/migrate-agent-room-multimember.ts --dry
# review offenders, then drop --dry
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…comments + getAllPods test Three findings from the code-reviewer pass on PR #232. 1. CRITICAL — ObjectId equality bug in ensureAgentInPod (pre-existing, exposed by this PR). `pod.members.includes(agentUser._id)` uses JS `===` which compares object references, not ObjectId values. In production with real Mongoose docs, the host agent is always treated as "not a member" of its own room, the new agent-room guard fires, and `null` propagates to callers as "pod not found." Replace with `.some((m) => m.equals?.(id) ?? String(m) === String(id))` — same pattern already used by removeAgentFromPod a few lines below. The previous test masked this bug by overriding pod.members.includes with a hand-rolled .equals lookup. Test now uses fakeObjectId() with real `.equals()` semantics — no override — so it would fail under the broken `.includes()` and pass only with the fixed `.some(.equals)`. 2. Test gap on getAllPods admin bypass — only getPodsByType was directly tested. Add a parity case asserting that an admin caller hitting getAllPods with type=agent-room sees all agent-rooms, not just their own. (getAllPods has an extra .populate(parentPod) call vs. getPodsByType — the test mock chain mirrors that.) 3. The joinPod guard comment said "not even the creator (the agent itself)" can rejoin. Tighten to make explicit that `pod.createdBy` for agent-rooms is the host agent, not a human user, so the creator-bypass branch in the invite-only block is effectively dead for this pod type — preventing future readers from misreading the comment as implying a creator-bypass exists. 4. Migration script header — document the downstream consequences a reviewer flagged: UI tab move, privacy-filter behavior change after conversion, agentAutoJoinService scan visibility. These are correct behaviors but worth surfacing for the operator running the migration on prod. 21/21 tests pass on affected suites. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
|
Self-review pass via code-reviewer subagent — addressed the critical finding + two important items in
Reviewer findings deliberately deferred:
Verdict: Request changes → critical + 3 important addressed. Tests now 21/21 across affected suites (was 20/20 before; +1 for the |
4 tasks
samxu01
added a commit
that referenced
this pull request
Apr 25, 2026
… chat PR #232's migration script converted any agent-room with >2 members to type: 'chat'. Investigating the actual offenders on dev showed every multi-member agent-room was an originally-1:1 DM (1 host agent + 1 human) polluted with rogue agents that auto-joined via ensureAgentInPod — exactly the bug PR #232's runtime guards close going forward. The relabel-to-chat strategy legitimized that pollution; restoration is the correct action. New migration logic: for each agent-room with members.length > 2: humans = members where User.isBot === false case humans.length === 1: keep [createdBy (host agent), the_human]; drop rogue agents stays type='agent-room' case humans.length === 0: agent↔agent DM. Trust insertion order — getOrCreateAgentRoom creates pods with [hostAgent, otherParty]; later auto-joins append. Keep [members[0], members[1]]; drop the rest. stays type='agent-room' case humans.length >= 2: Was never a DM (multi-human surfaces are chat pods, not DMs). type → 'chat'. All members preserved. 5 unit tests cover all three branches + dry-run + idempotency, mocking Pod + User so they run without a real Mongo connection. Also: tighten stale comment on POST /api/agents/runtime/room — was still documenting the rejected "many humans × one agent office" framing. Now matches ADR-001 §3.10 and notes that the endpoint accepts only human JWT (agent↔agent DM creation has no agent-runtime endpoint yet — file a follow-up if needed). The previous migration script was never run on dev, so swapping in the new logic doesn't require any rollback or compensating action. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
3 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
ADR-001 §3.10 says agent-rooms are personal 1:1 DMs — one user, one agent, no third party. The §3.10 amendment that corrected the original "N humans × 1 agent office" framing was documented but never enforced in code. Live data on
api-devshowed agent-room pods with 5–6 members — a privacy/UX bug where what users perceive as a private DM was joinable by other people.Surfaced during ADR-005 Phase 2 codex smoke (the
commonly pod listoutput included agent-rooms with 5–6 members).What changed
backend/controllers/podController.tsjoinPodrejects any third-person join onagent-room(HTTP 403).getAllPods/getPodsByTypeskip the membership filter when caller is a global admin.backend/services/agentIdentityService.tsensureAgentInPodrefuses to add an agent to an agent-room when that agent isn't already a member — closes the auto-install path that was the most likely entry route for the multi-member legacy data.backend/services/dmService.tsbackend/scripts/migrate-agent-room-multimember.ts(NEW)agent-roompods with 3+ members totype: 'chat'. Preserves messages, members, createdBy. Run with--dryto preview. Idempotent.backend/__tests__/unit/controllers/podController.test.jsbackend/__tests__/unit/services/agentIdentityService.ensureAgentInPod.test.js(NEW)Tests
20/20 across affected suites. No regression in adjacent test files.
Deploy steps after merge
commonly pod list --instance devshows noagent-roompod with > 2 members.Out of scope (filed as follow-ups)
agent-admintype: ADR-001 §3.10 says this is legacy and slated for deprecation as LiteLLM session observability matures. This PR doesn't touch it; agent-admin can still have multiple admins (its design intent).agent-roomever drifts back to >2 members (defense in depth) — not added since the three write paths are now all guarded.Test plan
/api/pods?type=agent-room→ expect ALL agent-rooms returned regardless of membershipcommonly pod listshows no agent-room with >2 members afterward🤖 Generated with Claude Code