feat(chat): group chats and the current chat.bsky surface #163

Merged
Grandiras merged 1 commit from issue-128-group-chat into main 2026-09-25 23:57:34 +00:00
Owner

Closes #128.

Group chats (upstream April–August 2026) and the rest of the current chat.bsky.* surface. Coverage goes from 20 to 50 of the 51 chat methods. Every new model uses typed identifiers, AtDatetime, open unions with an Unknown* fallback and LexObject extension data, and passes the upstream drift test.

What changed

chat.bsky.group.* (16): client.Chat.Group (GroupClient)

  • Groups and membership: CreateGroupAsync, EditGroupAsync, AddMembersAsync, RemoveMembersAsync, ListMutualGroupsAsync / EnumerateMutualGroupsAsync.
  • Join links: CreateJoinLinkAsync, EditJoinLinkAsync, EnableJoinLinkAsync, DisableJoinLinkAsync, GetJoinLinkPreviewsAsync.
  • Join requests: RequestJoinAsync, ApproveJoinRequestAsync, RejectJoinRequestAsync, WithdrawJoinRequestAsync, ListJoinRequestsAsync / EnumerateJoinRequestsAsync, UpdateJoinRequestsReadAsync.
  • Methods that answer {convo} / {joinLink} unwrap it (as MuteConvoAsync already does); {} outputs return Task.
  • New namespace ATProtoNet.Lexicon.Chat.Bsky.Embed: the message-embed unions MessageEmbed (MessageRecordEmbed = app.bsky.embed.record, JoinLinkEmbed) and MessageEmbedView (MessageRecordEmbedView, JoinLinkEmbedView). They are dedicated chat unions rather than new variants of the post EmbedBase, so a chat message cannot be given an embed the chat service rejects, and App/Bsky/Embed is untouched.

Convo

  • GetUnreadCountsAsync, ListConvoRequestsAsync / EnumerateConvoRequestsAsync, GetConvoMembersAsync / EnumerateConvoMembersAsync, LockConvoAsync, UnlockConvoAsync.
  • ListConvosAsync / EnumerateConvosAsync gain kind and lockStatus.

Typed views (each union open, with an unknown fallback)

  • ConvoView.Kind: DirectConvo / GroupConvo (name, member count and limit, lock status and moderation override, join link, join request counts, created at). ConvoView.LastReaction: MessageAndReactionView. ConvoView.LastMessage is typed.
  • ChatMemberView.Kind: DirectConvoMember / GroupConvoMember (role, added by) / PastGroupConvoMember (viewer, createdAt, verification were already there).
  • Messages: one open base ConvoMessage for every message union (MessageView, DeletedMessageView, SystemMessageView, MessageBeforeUserJoinedGroupView). MessageView gains Reactions (ReactionView) and ReplyTo, and its Facets / Embed are typed. MessageInput gains ReplyTo (MessageReplyRef) and typed Facets / Embed.
  • GetMessagesResponse.Messages is IReadOnlyList<ConvoMessage> (RelatedProfiles already existed).
  • System messages: SystemMessageView.Data is one of 12 SystemMessageData* types.
  • Log: ConvoLogEntry is the abstract base of the 30 getLog variants (LogBeginConvo … LogReadJoinRequests). It keeps Rev and ConvoId, which every variant has, so routing an entry needs no pattern match. UnknownConvoLogEntry reads both from the raw object.
  • Constants: ConvoStatus, ConvoKinds, ConvoLockStatus, ConvoReadState, ChatMemberRole, JoinRule, JoinLinkEnabledStatus, RequestJoinStatus, ChatPreferenceInclude, all checked by the drift test's knownValues theory.

Actor, notification, moderation

  • ChatActorClient.GetStatusAsync (chat.bsky.actor.getStatus). ChatDeclarationRecord.AllowGroupInvites already existed.
  • client.Chat.Notification (ChatNotificationClient): GetPreferencesAsync / PutPreferencesAsync, the replacement for the deprecated app.bsky.notification.defs#preferences.chat (which the SDK never modelled, so there is nothing to deprecate).
  • client.Chat.Moderation (ChatModerationClient): GetActorMetadataAsync, GetMessageContextAsync, GetConvoAsync, GetConvosAsync, GetConvoMembersAsync / EnumerateConvoMembersAsync, UpdateActorAccessAsync. Design decision: these calls send no fixed atproto-proxy header. The chat service answers them only for moderation services (moderators reach them through Ozone), so a forced chat proxy would only produce auth errors. They follow the client-wide SetProxy default, as the tools.ozone.* clients do. A test pins this.

Registration, drift test, docs

  • ChatClients gains Group, Notification and Moderation. The AtProtoClient.cs diff is three using lines, the registration call, and the ChatClients class.
  • Drift test: the seven #128 OmittedProperties entries and the loose ConvoLogEntry DefOf entry are removed. New DefOf entries cover MessageReplyRef, the shared ConvoOutput / JoinLinkOutput, ModerationConvoView, ChatActorMetadata and ChatNotificationPreferences. The ConvoLogEntry base and UnknownConvoLogEntry are in NotLexiconObjects. There are nine new knownValues locations.
  • TypedIdentifierGuardTests: SystemMessageView.Rev and ModerationConvoView.Rev are added to the chat-rev exceptions.
  • docs/chat.md is rewritten around the typed views, with a new Group Chats section (creating a group, join links, join requests, moderation hooks), notification preferences and the typed log. api-reference.md, architecture.md, custom-records.md and README.md are updated to match.

Breaking changes

  • Chat messages, log entries and embeds are typed. GetMessagesResponse.Messages, EnumerateMessagesAsync and ConvoView.LastMessage were JsonElement and are now ConvoMessage. ConvoLogEntry is abstract, and loses Type and Message. MessageInput.Embed / MessageView.Embed were JsonElement and are now MessageEmbed / MessageEmbedView. Facets were JsonElement and are now Facet. MessageView.Text is required string. ConvoView, MessageView and DeletedMessageView now derive from union bases. Migration: pattern-match (msg is MessageView m, entry is LogCreateMessage { Message: MessageView m }) instead of reading $type, and write Embed = new MessageRecordEmbed { Record = strongRef }.
  • ListConvosAsync / EnumerateConvosAsync take kind and lockStatus before limit / pageSize, per the CONTRIBUTING parameter order. Migration: pass limit, cursor and pageSize by name.

Verification

  • dotnet build --no-incremental of the full solution: 0 warnings, 0 errors (rebased onto ca43404, #158).
  • Unit tests: 3,386 passed, 0 failed, 5 skipped (3,391 total). The chat tests (ATProtoNet.Tests.Chat.*) are 121, up from 16.
    • One test per endpoint (request method, NSID, parameters or body, proxy header, and response parsing) for all 16 group methods, the 5 new convo methods, getStatus, both notification methods and the 6 moderation methods.
    • Model tests with Lexicon-shaped fixtures: a full group convoView that round-trips field for field, messages with facets, joinLink and record embeds, reactions and each reply variant, a getMessages page. There are theories over all 12 system-message kinds and all 30 log entries (each round-trips, with no extension data left over), and a test that the log theory covers exactly the upstream getLog union. Nine unknown-variant tests check that an unknown variant survives and writes back byte for byte.
    • The chat service is not open source, so the fixtures follow the vendored snapshot's chat.bsky.* Lexicons field by field rather than captured traffic.
  • LexiconDriftTests (34) pass with the allow-list entries removed, and Allowlists_AllStillMatch confirms that none of them is stale.

Deferred

  • chat.bsky.moderation.subscribeModEvents (the seventh moderation method) is a private, service-authenticated event stream. Wrapping it needs an authenticated WebSocket and frame decoding, which #126 is consolidating into one streaming core with typed error frames. The 11 event models should land together with a transport after #126, not as unused types now.
  • Reporting a chat message goes through com.atproto.moderation.createReport with a chat.bsky.convo.defs#messageRef subject. ReportSubject does not model that variant yet (it reads as UnknownReportSubject). Adding it touches Com/AtProto/Moderation, and Ozone's ModerationSubject needs messageRef / convoRef too (#131), so it is left out here.
  • chat.bsky.convo.defs#logReadMessage is deprecated upstream in favour of logReadConvo. It is modelled (the service may still send it) but not marked [Obsolete], which would warn at its own [JsonDerivedType] registration.

🤖 Generated with Claude Code

Closes #128. Group chats (upstream April–August 2026) and the rest of the current `chat.bsky.*` surface. Coverage goes from 20 to 50 of the 51 chat methods. Every new model uses typed identifiers, `AtDatetime`, open unions with an `Unknown*` fallback and `LexObject` extension data, and passes the upstream drift test. ## What changed ### `chat.bsky.group.*` (16): `client.Chat.Group` (`GroupClient`) - Groups and membership: `CreateGroupAsync`, `EditGroupAsync`, `AddMembersAsync`, `RemoveMembersAsync`, `ListMutualGroupsAsync` / `EnumerateMutualGroupsAsync`. - Join links: `CreateJoinLinkAsync`, `EditJoinLinkAsync`, `EnableJoinLinkAsync`, `DisableJoinLinkAsync`, `GetJoinLinkPreviewsAsync`. - Join requests: `RequestJoinAsync`, `ApproveJoinRequestAsync`, `RejectJoinRequestAsync`, `WithdrawJoinRequestAsync`, `ListJoinRequestsAsync` / `EnumerateJoinRequestsAsync`, `UpdateJoinRequestsReadAsync`. - Methods that answer `{convo}` / `{joinLink}` unwrap it (as `MuteConvoAsync` already does); `{}` outputs return `Task`. ### `chat.bsky.embed.joinLink` - New namespace `ATProtoNet.Lexicon.Chat.Bsky.Embed`: the message-embed unions `MessageEmbed` (`MessageRecordEmbed` = `app.bsky.embed.record`, `JoinLinkEmbed`) and `MessageEmbedView` (`MessageRecordEmbedView`, `JoinLinkEmbedView`). They are dedicated chat unions rather than new variants of the post `EmbedBase`, so a chat message cannot be given an embed the chat service rejects, and `App/Bsky/Embed` is untouched. ### Convo - `GetUnreadCountsAsync`, `ListConvoRequestsAsync` / `EnumerateConvoRequestsAsync`, `GetConvoMembersAsync` / `EnumerateConvoMembersAsync`, `LockConvoAsync`, `UnlockConvoAsync`. - `ListConvosAsync` / `EnumerateConvosAsync` gain `kind` and `lockStatus`. ### Typed views (each union open, with an unknown fallback) - `ConvoView.Kind`: `DirectConvo` / `GroupConvo` (name, member count and limit, lock status and moderation override, join link, join request counts, created at). `ConvoView.LastReaction`: `MessageAndReactionView`. `ConvoView.LastMessage` is typed. - `ChatMemberView.Kind`: `DirectConvoMember` / `GroupConvoMember` (role, added by) / `PastGroupConvoMember` (`viewer`, `createdAt`, `verification` were already there). - Messages: one open base `ConvoMessage` for every message union (`MessageView`, `DeletedMessageView`, `SystemMessageView`, `MessageBeforeUserJoinedGroupView`). `MessageView` gains `Reactions` (`ReactionView`) and `ReplyTo`, and its `Facets` / `Embed` are typed. `MessageInput` gains `ReplyTo` (`MessageReplyRef`) and typed `Facets` / `Embed`. - `GetMessagesResponse.Messages` is `IReadOnlyList<ConvoMessage>` (`RelatedProfiles` already existed). - System messages: `SystemMessageView.Data` is one of 12 `SystemMessageData*` types. - Log: `ConvoLogEntry` is the abstract base of the 30 `getLog` variants (`LogBeginConvo` … `LogReadJoinRequests`). It keeps `Rev` and `ConvoId`, which every variant has, so routing an entry needs no pattern match. `UnknownConvoLogEntry` reads both from the raw object. - Constants: `ConvoStatus`, `ConvoKinds`, `ConvoLockStatus`, `ConvoReadState`, `ChatMemberRole`, `JoinRule`, `JoinLinkEnabledStatus`, `RequestJoinStatus`, `ChatPreferenceInclude`, all checked by the drift test's knownValues theory. ### Actor, notification, moderation - `ChatActorClient.GetStatusAsync` (`chat.bsky.actor.getStatus`). `ChatDeclarationRecord.AllowGroupInvites` already existed. - `client.Chat.Notification` (`ChatNotificationClient`): `GetPreferencesAsync` / `PutPreferencesAsync`, the replacement for the deprecated `app.bsky.notification.defs#preferences.chat` (which the SDK never modelled, so there is nothing to deprecate). - `client.Chat.Moderation` (`ChatModerationClient`): `GetActorMetadataAsync`, `GetMessageContextAsync`, `GetConvoAsync`, `GetConvosAsync`, `GetConvoMembersAsync` / `EnumerateConvoMembersAsync`, `UpdateActorAccessAsync`. **Design decision:** these calls send no fixed `atproto-proxy` header. The chat service answers them only for moderation services (moderators reach them through Ozone), so a forced chat proxy would only produce auth errors. They follow the client-wide `SetProxy` default, as the `tools.ozone.*` clients do. A test pins this. ### Registration, drift test, docs - `ChatClients` gains `Group`, `Notification` and `Moderation`. The `AtProtoClient.cs` diff is three `using` lines, the registration call, and the `ChatClients` class. - Drift test: the seven `#128` `OmittedProperties` entries and the loose `ConvoLogEntry` `DefOf` entry are removed. New `DefOf` entries cover `MessageReplyRef`, the shared `ConvoOutput` / `JoinLinkOutput`, `ModerationConvoView`, `ChatActorMetadata` and `ChatNotificationPreferences`. The `ConvoLogEntry` base and `UnknownConvoLogEntry` are in `NotLexiconObjects`. There are nine new knownValues locations. - `TypedIdentifierGuardTests`: `SystemMessageView.Rev` and `ModerationConvoView.Rev` are added to the chat-rev exceptions. - `docs/chat.md` is rewritten around the typed views, with a new **Group Chats** section (creating a group, join links, join requests, moderation hooks), notification preferences and the typed log. `api-reference.md`, `architecture.md`, `custom-records.md` and `README.md` are updated to match. ## Breaking changes - **Chat messages, log entries and embeds are typed.** `GetMessagesResponse.Messages`, `EnumerateMessagesAsync` and `ConvoView.LastMessage` were `JsonElement` and are now `ConvoMessage`. `ConvoLogEntry` is abstract, and loses `Type` and `Message`. `MessageInput.Embed` / `MessageView.Embed` were `JsonElement` and are now `MessageEmbed` / `MessageEmbedView`. `Facets` were `JsonElement` and are now `Facet`. `MessageView.Text` is `required string`. `ConvoView`, `MessageView` and `DeletedMessageView` now derive from union bases. **Migration:** pattern-match (`msg is MessageView m`, `entry is LogCreateMessage { Message: MessageView m }`) instead of reading `$type`, and write `Embed = new MessageRecordEmbed { Record = strongRef }`. - **`ListConvosAsync` / `EnumerateConvosAsync`** take `kind` and `lockStatus` before `limit` / `pageSize`, per the CONTRIBUTING parameter order. **Migration:** pass `limit`, `cursor` and `pageSize` by name. ## Verification - `dotnet build --no-incremental` of the full solution: **0 warnings, 0 errors** (rebased onto `ca43404`, #158). - Unit tests: **3,386 passed, 0 failed, 5 skipped** (3,391 total). The chat tests (`ATProtoNet.Tests.Chat.*`) are **121**, up from 16. - One test per endpoint (request method, NSID, parameters or body, proxy header, and response parsing) for all 16 group methods, the 5 new convo methods, getStatus, both notification methods and the 6 moderation methods. - Model tests with Lexicon-shaped fixtures: a full group `convoView` that round-trips field for field, messages with facets, joinLink and record embeds, reactions and each reply variant, a `getMessages` page. There are theories over all 12 system-message kinds and all 30 log entries (each round-trips, with no extension data left over), and a test that the log theory covers exactly the upstream `getLog` union. Nine unknown-variant tests check that an unknown variant survives and writes back byte for byte. - The chat service is not open source, so the fixtures follow the vendored snapshot's `chat.bsky.*` Lexicons field by field rather than captured traffic. - `LexiconDriftTests` (34) pass with the allow-list entries removed, and `Allowlists_AllStillMatch` confirms that none of them is stale. ## Deferred - **`chat.bsky.moderation.subscribeModEvents`** (the seventh moderation method) is a private, service-authenticated event stream. Wrapping it needs an authenticated WebSocket and frame decoding, which #126 is consolidating into one streaming core with typed error frames. The 11 event models should land together with a transport after #126, not as unused types now. - **Reporting a chat message** goes through `com.atproto.moderation.createReport` with a `chat.bsky.convo.defs#messageRef` subject. `ReportSubject` does not model that variant yet (it reads as `UnknownReportSubject`). Adding it touches `Com/AtProto/Moderation`, and Ozone's `ModerationSubject` needs `messageRef` / `convoRef` too (#131), so it is left out here. - `chat.bsky.convo.defs#logReadMessage` is deprecated upstream in favour of `logReadConvo`. It is modelled (the service may still send it) but not marked `[Obsolete]`, which would warn at its own `[JsonDerivedType]` registration. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat(chat)!: group chats and the current chat.bsky surface (closes #128)
All checks were successful
CI / pds-integration (pull_request) Successful in 27s
CI / build-and-test (pull_request) Successful in 1m4s
fa6c20c5d1
Group chats landed upstream between April and August 2026, and the SDK
covered 20 of the 51 chat.bsky methods. It read group conversations,
system messages and messages as loose JsonElements, and the log entries
as one bag with a $type string.

- chat.bsky.group (16 methods) as client.Chat.Group: groups, members,
  mutual groups, join links and join requests.
- chat.bsky.convo: getUnreadCounts, listConvoRequests, getConvoMembers,
  lockConvo and unlockConvo; listConvos gains kind and lockStatus.
- Typed open unions, each with an Unknown* fallback: convoView.kind and
  lastReaction, the member kind, messages (message, deleted, system,
  before-joined), replyTo, the message embeds (record and joinLink),
  system message data (12) and the getLog entries (30). ConvoLogEntry
  becomes the base of the log union and keeps rev and convoId.
- chat.bsky.actor.getStatus, chat.bsky.notification get/putPreferences,
  and six chat.bsky.moderation methods, which follow the client-wide
  proxy because the chat service answers them only for moderation
  services.

The drift test's group-chat allow-list entries are gone: every model now
declares every upstream property.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Author
Owner

@claude /review

@claude /review
Collaborator

🤖 Reviewing this PR — the review will link the full session transcript.

📜 Live transcript — updating every ~5 minutes while the review runs.

🤖 Reviewing this PR — the review will link the full session transcript. <!-- opencode-run:review --> 📜 [Live transcript](https://git.grandiras.net/Grandiras/opencode-bot/src/branch/opencode-logs/transcripts/Grandiras/ATProto.NET/pr-163/run-2026-09-25T21-00-32-856Z.md) — updating every ~5 minutes while the review runs.
Collaborator

⏳ Held on the OpenCode Go window — the 5-hour window is at 0% (the bot holds at 95%).
This continues automatically after the window resets (2026-09-26T02:00:33.556Z) — nothing to re-trigger.

⏳ **Held on the OpenCode Go window** — the 5-hour window is at 0% (the bot holds at 95%). This continues automatically after the window resets (2026-09-26T02:00:33.556Z) — nothing to re-trigger. <!-- opencode-run:paused --> <!-- opencode-session:ses_f25a14bcbffewTcfr9p6WwhtIE -->
Collaborator

🤖 Reviewing this PR — follow along in the action run; the reply will link the full session transcript.

📜 Live transcript — updating every ~5 minutes while the task runs.

🤖 Reviewing this PR — follow along in [the action run](https://git.grandiras.net/Grandiras/claude-bot/actions/runs/604); the reply will link the full session transcript. <!-- claude-run:pr_review --> 📜 [Live transcript](https://git.grandiras.net/Grandiras/claude-bot/src/branch/claude-logs/transcripts/Grandiras/ATProto.NET/pr-163/run-604.md) — updating every ~5 minutes while the task runs.
claude-reviewer left a comment

✅ Verdict: approve — no blocking issues found.

Large but well-structured PR extending chat.bsky.* coverage to group chats and the remaining surface (convo requests, unread counts, members, locking, moderation, notification prefs). Spot-checked against upstream: chat.bsky.group.createGroup semantics (100-member cap, non-idempotent) match the SDK docs (see atproto.blue), giving confidence the rest of the group/join-link/join-request/log surface is similarly grounded rather than invented.

Walked the diff for correctness issues:

  • The new open-union types (ConvoKind, ConvoMessage, ChatMemberKind, SystemMessageData, ConvoLogEntry, MessageEmbed/MessageEmbedView, JoinLinkPreview, ConvoRequestView) all follow the codebase's established Unknown* fallback pattern (AtProtoUnion + IUnknownUnionVariant) consistently, matching prior PRs (#155-#159).
  • EnumerateConvosAsync's call to ListConvosAsync was updated correctly to match the new kind/lockStatus parameter positions — no argument-order mismatch despite the breaking signature change.
  • ChatModerationClient's methods correctly omit options: ChatProxy everywhere, consistent with the PR's stated design decision that moderation calls follow the client-wide proxy instead of the fixed chat-service header.
  • ConvoOutput (internal) and GetConvoMembersResponse are correctly shared/reused across ConvoClient, GroupClient, and ChatModerationClient via the Convo namespace import — no visibility or circular-reference problems (internal is assembly-scoped, and the Convo/Group/Embed namespace cross-references are all within the same project).
  • UnknownConvoLogEntry correctly backfills Rev/ConvoId (defaulting to empty string) from the raw JSON via [SetsRequiredMembers], needed because ConvoLogEntry now declares those as required abstract-base members.
  • Reuse of the app.bsky.embed.record $type discriminator on the new MessageRecordEmbed/MessageRecordEmbedView chat types (separate hierarchy from the post EmbedBase union) is intentional and matches how upstream reuses that def in chat.bsky.convo.defs.

Nothing here looks like a functional bug; the CHANGELOG entries accurately describe the breaking changes (typed messages/log entries/embeds, ListConvosAsync parameter reordering). Docs updates in chat.md are thorough and consistent with the new API shapes.


⏱ 1m 4s · 4 turns · tokens: 83.8k in / 5.8k out (+338.7k cache-read)
📜 Full session transcript

✅ **Verdict: approve** — no blocking issues found. Large but well-structured PR extending chat.bsky.* coverage to group chats and the remaining surface (convo requests, unread counts, members, locking, moderation, notification prefs). Spot-checked against upstream: `chat.bsky.group.createGroup` semantics (100-member cap, non-idempotent) match the SDK docs (see atproto.blue), giving confidence the rest of the group/join-link/join-request/log surface is similarly grounded rather than invented. Walked the diff for correctness issues: - The new open-union types (`ConvoKind`, `ConvoMessage`, `ChatMemberKind`, `SystemMessageData`, `ConvoLogEntry`, `MessageEmbed`/`MessageEmbedView`, `JoinLinkPreview`, `ConvoRequestView`) all follow the codebase's established `Unknown*` fallback pattern (`AtProtoUnion` + `IUnknownUnionVariant`) consistently, matching prior PRs (#155-#159). - `EnumerateConvosAsync`'s call to `ListConvosAsync` was updated correctly to match the new `kind`/`lockStatus` parameter positions — no argument-order mismatch despite the breaking signature change. - `ChatModerationClient`'s methods correctly omit `options: ChatProxy` everywhere, consistent with the PR's stated design decision that moderation calls follow the client-wide proxy instead of the fixed chat-service header. - `ConvoOutput` (internal) and `GetConvoMembersResponse` are correctly shared/reused across `ConvoClient`, `GroupClient`, and `ChatModerationClient` via the `Convo` namespace import — no visibility or circular-reference problems (internal is assembly-scoped, and the Convo/Group/Embed namespace cross-references are all within the same project). - `UnknownConvoLogEntry` correctly backfills `Rev`/`ConvoId` (defaulting to empty string) from the raw JSON via `[SetsRequiredMembers]`, needed because `ConvoLogEntry` now declares those as `required` abstract-base members. - Reuse of the `app.bsky.embed.record` `$type` discriminator on the new `MessageRecordEmbed`/`MessageRecordEmbedView` chat types (separate hierarchy from the post `EmbedBase` union) is intentional and matches how upstream reuses that def in `chat.bsky.convo.defs`. Nothing here looks like a functional bug; the CHANGELOG entries accurately describe the breaking changes (typed messages/log entries/embeds, `ListConvosAsync` parameter reordering). Docs updates in `chat.md` are thorough and consistent with the new API shapes. --- ⏱ 1m 4s · 4 turns · tokens: 83.8k in / 5.8k out (+338.7k cache-read) 📜 [Full session transcript](https://git.grandiras.net/Grandiras/claude-bot/src/branch/claude-logs/transcripts/Grandiras/ATProto.NET/pr-163/run-604.md) <!-- claude-verdict:approve --> <!-- claude-reviewed-head:fa6c20c5d167932bc21b03a70d34fcd320c9fb9c -->
Collaborator

🤖 Resolving the merge conflicts — the reply will link the full session transcript.

📜 Live transcript — updating every ~5 minutes while the fix runs.

🤖 Resolving the merge conflicts — the reply will link the full session transcript. <!-- opencode-run:conflict --> 📜 [Live transcript](https://git.grandiras.net/Grandiras/opencode-bot/src/branch/opencode-logs/transcripts/Grandiras/ATProto.NET/pr-163/run-2026-09-25T21-03-06-571Z.md) — updating every ~5 minutes while the fix runs.
Collaborator

⏳ Held on the OpenCode Go window — the 5-hour window is at 0% (the bot holds at 95%).
This continues automatically after the window resets (2026-09-26T02:00:33.556Z) — nothing to re-trigger.

⏳ **Held on the OpenCode Go window** — the 5-hour window is at 0% (the bot holds at 95%). This continues automatically after the window resets (2026-09-26T02:00:33.556Z) — nothing to re-trigger. <!-- opencode-run:paused --> <!-- opencode-session:ses_f259ef59cffeZOhkpBQCYKtCkC -->
Grandiras force-pushed issue-128-group-chat from fa6c20c5d1
All checks were successful
CI / pds-integration (pull_request) Successful in 27s
CI / build-and-test (pull_request) Successful in 1m4s
to 8289c5413c
All checks were successful
CI / pds-integration (pull_request) Successful in 30s
CI / build-and-test (pull_request) Successful in 1m19s
Sync Closures to GitHub / sync-closure (pull_request) Successful in 6s
2026-09-25 21:04:12 +00:00
Compare
Collaborator

🤖 Reviewing this PR — the review will link the full session transcript.

📜 Live transcript — updating every ~5 minutes while the review runs.

🤖 Reviewing this PR — the review will link the full session transcript. <!-- opencode-run:review --> 📜 [Live transcript](https://git.grandiras.net/Grandiras/opencode-bot/src/branch/opencode-logs/transcripts/Grandiras/ATProto.NET/pr-163/run-2026-09-25T21-31-15-606Z.md) — updating every ~5 minutes while the review runs.
Collaborator

⏳ Held on the OpenCode Go window — the 5-hour window is at 0% (the bot holds at 95%).
This continues automatically after the window resets (2026-09-26T02:31:16.041Z) — nothing to re-trigger.

⏳ **Held on the OpenCode Go window** — the 5-hour window is at 0% (the bot holds at 95%). This continues automatically after the window resets (2026-09-26T02:31:16.041Z) — nothing to re-trigger. <!-- opencode-run:paused --> <!-- opencode-session:ses_f25852ee3ffexvezNa4f4wA3mn -->
Author
Owner

Rebased onto main to resolve a CHANGELOG-only conflict (#162 merged). verify-rebase: the PR's patch outside CHANGELOG.md is byte-identical between the approved head fa6c20c5 and the new head 8289c541 (4699 changed lines). CI is green on the new head (3479 unit tests pass locally), so the approval carries over under the maintainer-approved policy for CHANGELOG-only rebases.

Rebased onto main to resolve a CHANGELOG-only conflict (#162 merged). `verify-rebase`: the PR's patch outside `CHANGELOG.md` is byte-identical between the approved head `fa6c20c5` and the new head `8289c541` (4699 changed lines). CI is green on the new head (3479 unit tests pass locally), so the approval carries over under the maintainer-approved policy for CHANGELOG-only rebases.
Grandiras deleted branch issue-128-group-chat 2026-09-25 23:57:34 +00:00
Sign in to join this conversation.
No description provided.