Offer a conversation to be named only when the queue holds nothing for it - #587
Merged
Merged
Conversation
…r it A pass offers at most twenty unnamed conversations. One the pass could not name keeps no summary and stays in that set, and offering it again while its settled work row stands does nothing but take a place. Enough of them ahead of a new conversation left the new one unoffered on every pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
kevin9327
requested review from
MikeRyanDev,
davidmckayv,
guidovizoso,
mxmzb and
tylerslaton
as code owners
September 16, 2026 21:44
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
# Conflicts: # CHANGELOG.md
davidmckayv
approved these changes
Sep 16, 2026
davidmckayv
left a comment
Contributor
There was a problem hiding this comment.
Code-verified clean; CI green on this sha.
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.
What this changes
server/src/index.tsrunsofferChannelsAwaitingSummaryon every pass of its summary loop, 10 seconds after the last one ends. With nolimitpassed, it reads 20 channels with no summary, not deleted, and with a last message. It offers each one to the queue. There is noORDER BY, so the planner's order decides which 20 it gets, and that order is the same on every pass.A conversation the job cannot name keeps
summarynull and stays in that set:summariseOnereturns "nothing to name it with" and the work row is finished;openingOfreturns null, it is "not yet" until the attempts run out, and the row gives up;While that finished or given-up row stands (an hour or a day, per
forgetSettledSummaries),queue.offeris a no-op for that key. The query still returns the conversation, though, and it still takes one of the 20 places. Once enough of them sort ahead of a new conversation, the new one is never offered. Its sidebar entry keeps the plain channel name instead of a title. Conversations that cannot be named only pile up, because named ones leave the set and these do not.The fix adds
NOT EXISTS (work_items row for kind 'channel.summary' and this channel's id)to the offer query. The primary key(kind, key)answers that from its index. That covers:forgetSettledSummariesdeletes the row. After that it is offered again, exactly as today.Nothing about claiming, naming, retries or purging changes. The only difference is which conversations fill the 20 places.
Where it runs
work_itemstable the queue already keeps.(kind, key).offeris stillON CONFLICT DO NOTHING, and a row committed between this read and the offer is still a conflict.Boundary and audit
Changelog
CHANGELOG.mdunderUnreleased.Proof
I have no local PostgreSQL, so everything that touches the database ran on GitHub Actions in my fork. The job used the same
pgvector/pgvector:pg17service,bun install --frozen-lockfile,drizzle-kit migrateand Bun 1.3.14 asci.yml.Two cases added to
server/tests/channel-summary.integration.test.ts:null, then callofferChannelsAwaitingSummary.forgetSettledSummariesdoes, and the conversation is offered again. This passes before and after, and guards the "as before".With
summary.tsfrommain(only the tests added):With the fix,
bun test --coverage server/tests/channel-summary.integration.test.tspasses 17 of 17. The changed query runs, and the report's uncovered lines are all outside it.To see the symptom rather than the mechanism, I ran a throwaway test (not part of this PR) in the same job. It creates 20 used conversations and settles them all with a titler that answers
null. It then adds 5 new conversations and calls the production form,offerChannelsAwaitingSummary({ database, queue }), three times:On
main, 16 of the 20 places went to conversations that could not be named on every pass, and a new conversation was left out each time. With more conversations that cannot be named, more new ones are left out.Locally:
bun run typecheck(app, server, worker) exits 0, andbunx biome checkon the changed files is clean.This PR adds its entry at the top of
## Unreleased, where other open PRs add theirs, so the changelog may conflict. I can rebase if it does.🤖 Generated with Claude Code