Repository navigation
Conversation
tdc inbox asked the server for each inbox channel one at a time, just to show its name. With 24 channels in the inbox, a script running the command every few seconds used up the per-user rate limit for channels/getone, and the user's own apps started getting 429s too. The public-channel check already fetches the workspace's channel list, which has the names. I now keep that list instead of only the public ids, and read the names from it. A channel missing from the list (one the user has left) still gets looked up by itself. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@doistbot /review |
doistbot
left a comment
There was a problem hiding this comment.
This PR speeds up tdc inbox by reusing the workspace channel list it already fetches for the private-channel check, replacing per-channel name lookups with a single request (plus a per-channel fallback only for channels the user has left). No inline issues were flagged — reviewers confirmed output, --channel filtering, and private-channel semantics are unchanged, spinner coverage and cache handling are intact, and tests cover the reuse path, the fallback, and the --ids-only no-fetch case.
I also included a few optional follow-up notes in the details below.
Optional follow-up notes (2)
src/lib/public-channels.ts:20: getPublicChannelIds now rebuilds the public-id Set from the cached channel list on every call. In bulk
thread read/thread unreadruns,runThreadReadStateMutationcallsassertChannelIsPublic(and therefore this function) once per thread, so the same filter/map work repeats O(threads × channels) times. Previously the Set was cached and repeat calls were O(1). Cache the derived Set alongside the channel list (and clear it inclearChannelCache) to restore the old behavior.src/commands/inbox.ts:83: With
--include-private-channels, this getChannels call is new sequential work on the critical path: it's awaited only after getInbox/fetchUnreadThreads finish, but it only needsworkspaceId. WhenoutputMode !== 'ids-only' || options.channel, it could be included in the Promise.all with getInbox above to hide its latency. (Leave the default private-hidden path as-is — there the fetch is only needed once threads survive the unread/mention filters, so eager parallel fetching could issue a request that's never used.)
Bulk thread read/unread check the private-channel rule once per thread. Caching the public ids next to the list means each check is a lookup again, instead of rebuilding the set every time. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@doistbot (This reply is from Rory's agent) Thanks. I took the first note: the public channel ids are now cached next to the channel list, so repeat checks in bulk |
|
I couldn't confidently route this request. Commands supported here are: For review, leave a comment |
TL;DR
tdc inboxnow gets all its channel names from one request, instead of one request per channel. What it prints doesn't change.Why
To show the inbox, the CLI needs the name of each channel a thread sits in. It asked the server for each channel separately. An inbox spread over 24 channels meant 24 extra requests, every run.
That request has a per-user rate limit (
rate_limiter.py:77). In practice it allows about 100 calls a minute. On 2026-10-21 a script rantdc inboxevery 6 seconds, which is about 200 calls a minute. It got 1,358 refusals (429s) in an hour. The limit is per user, so that person's own web and iOS apps got refused too.The CLI already had the names. A few lines earlier it fetches the workspace's channel list, to hide private channels. It kept the ids and threw the names away.
What changes
Requests made to get channel names, per
tdc inboxrun:--include-private-channels--ids-onlyThe only change is where the names come from. The output,
--channelfiltering and the private-channel rules work as before.The channel list only has channels the user is a member of. An inbox thread can sit in a channel they've left, which isn't on it. Those channels still get looked up one by one. With private channels hidden, those threads were already being dropped, because the private-channel check uses the same list. So in practice the one-by-one lookups only happen with
--include-private-channels.I keep the list in the same cache the private-channel check already used. Before, that cache held only the public channel ids. Now it holds the channels as well.
Two things to look at
channel threadsand search use the same private-channel check. They get the same answer as before, from the same single request. Their tests pass unchanged.How it relates to other PRs
Stands alone. Nothing else needs to merge first. It's one of three fixes for the same rate-limit problem: comms-cli#94 stops
tdc thread viewfailing when its channel lookup is rate limited, and comms-backend#1155 tells rate-limited callers when to retry.References
inbox.ts:83-87atdba87ddrate_limiter.py:77, live since comms-backend#991(Written by Rory's agent)
🤖 Generated with Claude Code