feat: group DM conversations — lifecycle + members (1/3)#56
Merged
jackparnell merged 1 commit intoMay 27, 2026
Merged
Conversation
First of three PRs adding group-DM coverage to the SDK. Wraps the 13
endpoints under /api/v1/messages/groups/* that handle group creation,
metadata, message-send, and member management.
Lifecycle:
create_group_conversation(title, members)
list_group_templates()
create_group_from_template(template, members, title_override=None)
get_group_conversation(conv_id, limit=50, offset=0)
update_group_conversation(conv_id, title=None, description=None)
send_group_message(conv_id, body, reply_to_message_id=None,
idempotency_key=None)
Members:
list_group_members(conv_id)
add_group_member(conv_id, username)
remove_group_member(conv_id, user_id)
set_group_admin(conv_id, user_id, is_admin)
transfer_group_creator(conv_id, new_creator_username)
respond_to_group_invite(conv_id, accept)
mark_group_all_read(conv_id)
Per the user's instruction, no version bump in this PR — the version
will move once the remaining two PRs (per-message ops + attachments)
have landed.
idempotency_key is sync-only for now: the async _raw_request doesn't
yet thread the Idempotency-Key header through. This matches the
existing async send_message gap; both will close together in a
follow-up.
53 new tests across sync (TestGroupConversationsLifecycle,
TestGroupMembership), async (TestAsyncGroupConversationsLifecycle,
TestAsyncGroupMembership), and mock-client coverage. 100% line
coverage preserved. Lint + format + mypy + 559 tests all green.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
ColonistOne
approved these changes
May 27, 2026
Collaborator
ColonistOne
left a comment
There was a problem hiding this comment.
Reviewed end-to-end against main. CI is fully green and the surface looks ready to merge.
Strengths
- Three-client parity is tight. Every sync method has a matching async + mock entry with the same signature (one deliberate exception, see below).
MockColonyClientreuses the existing_respondrecording protocol — zero new mock machinery. - Three known traps explicitly pinned by tests:
description=""clears vsdescription=Noneomits —test_update_group_conversation_empty_clears_descriptionguards against accidental falsy-collapse.- FastAPI boolean coercion:
is_admin=true/falselowercase, neverstr(bool)capitalised — pinned intest_set_group_admin_demote. Idempotency-Keyheader threading on syncsend_group_message— pinned intest_send_group_message_with_idempotency_key.
- Wire-format tests cover both shape and encoding —
test_create_group_conversation_escapes_special_charsconfirmstitle=R%26D+Labrather than ampersand-as-separator. The right depth for an SDK whose entire job is producing the correct wire bytes. - Documented deliberate gaps rather than silently divergent: PR description calls out the three-PR plan + no-version-bump, the async
send_group_messagedocstring acknowledges the missingidempotency_key(matching the pre-existing 1:1send_messageprecedent), and the CHANGELOG mirrors both.
Minor observations (none blocking)
from urllib.parse import urlencodeis repeated inline inside each async method — but this matches the pre-existing style inasync_client.py(lines 476, 632, 989, 1016, 1042), so the PR is consistent with the codebase rather than introducing a new wart. Worth a separate cleanup sweep someday.- No client-side validation of bounds (title 1..100, description 0..500, members 1..49) — all server-validated. Fine for v1; could fail-fast in client later if useful.
update_group_conversationwith both fields omitted PATCHes a query-less URL and lets the server 400. Symmetric with the PATCH-omits-no-change contract, so I'd leave it.
Approving. Holding the merge button for a human reviewer per the TheColonyCC/* convention.
5 tasks
jackparnell
pushed a commit
that referenced
this pull request
May 27, 2026
Second of three PRs adding group-DM coverage. Layers conversation state + within-group search on top of the lifecycle methods landed in #56. State (per-participant — affects only the caller's row): mute_group_conversation(conv_id, until=None) unmute_group_conversation(conv_id) snooze_group_conversation(conv_id, duration) unsnooze_group_conversation(conv_id) set_group_read_receipts(conv_id, show=None) # 3-state override Pins (group-wide, admin-only): pin_group_message(conv_id, msg_id) unpin_group_message(conv_id, msg_id) Search: search_group_messages(conv_id, q, limit=50, offset=0) Group-avatar upload + serve were originally planned for this PR but pulled out: they need multipart/form-data, which the SDK doesn't yet support, and the work belongs alongside attachment uploads (PR 3 already needs the same transport). Keeps PR 2's surface focused on query-string endpoints we can land cleanly with the existing infrastructure. Per the user's instruction, still no version bump. 35 new tests covering the three-state set-receipts surface (true / false / cleared), the lowercase-bool quirk that FastAPI's query coercion enforces, query-string escaping for special characters, and pagination defaults. 100% line coverage preserved across sync, async, and mock clients. 594 tests + lint + format + mypy green. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This was referenced May 27, 2026
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
First of three PRs adding group-DM coverage to the SDK. Wraps 13 endpoints under
/api/v1/messages/groups/*:Lifecycle:
create_group_conversation,list_group_templates,create_group_from_template,get_group_conversation,update_group_conversation,send_group_messageMembers:
list_group_members,add_group_member,remove_group_member,set_group_admin,transfer_group_creator,respond_to_group_invite,mark_group_all_readAll three clients (
ColonyClient,AsyncColonyClient,MockColonyClient) get the new methods.Why three PRs
Group-DM coverage is ~30 endpoints across lifecycle, state (mute/snooze/receipts/pin), avatar, search, per-message ops (read/reactions/edit/star/forward), and attachments. Splitting keeps review surface manageable:
Per author request, no version bump until all three are in.
Test plan
TestGroupConversationsLifecycle,TestGroupMembership, async + mock counterparts)ruff check+ruff format --checkcleanmypy src/cleanNotes for review
"true"/"false"— FastAPI'sboolquery coercion rejects Python's capitalisedstr(bool)default. There's an explicit test for this.send_group_messageacceptsidempotency_keyon the sync client only. The async_raw_requestdoesn't yet thread theIdempotency-Keyheader through — same gap as the existing asyncsend_message. Documented in the docstring; will close in a follow-up that fixes both at once.update_group_conversation(conv_id, description="")deliberately sendsdescription=on the wire (clears the description), distinct from omitting the kwarg (no change). Pinned bytest_update_group_conversation_empty_clears_description.🤖 Generated with Claude Code