Skip to content

feat: a daily digest of what is waiting on your review - #3

Open
Andy9822 wants to merge 6 commits into
ablaszkiewicz:mainfrom
Andy9822:feat/daily-digest
Open

feat: a daily digest of what is waiting on your review#3
Andy9822 wants to merge 6 commits into
ablaszkiewicz:mainfrom
Andy9822:feat/daily-digest

Conversation

@Andy9822

@Andy9822 Andy9822 commented Sep 11, 2026

Copy link
Copy Markdown

What problem does this PR solve?

Proke only tells you about things as they happen. There's no summary or digest concept when you start your day or anything similar

What was done to solve the problem?

Added an optional daily digest: one Slack DM at an hour you pick, in your own timezone, listing the pull requests still waiting on your review, oldest first with an age and a file count. It's off by default, since unlike every other kind here it's a message on a schedule rather than an answer to a webhook.

The digest runs on a timer, similar to the inbox warmer. Before building anything, it claims that person's digest for the day with a conditional write so two replicas can't both send the same Slack message. The timezone is saved from the browser whenever settings change, since GitHub doesn't expose one and we can't reliably derive it from the session.

Happy to change any of that if you'd prefer different behavior. The six commits also stand on their own if that's easier to review.

Links

Closes #2

QA

Risk Assessment

🤖 Generated with Claude Code

Andy9822 and others added 6 commits September 12, 2026 00:31
Both are scalars on a fragment the query already requests, so they add
no cost, and createdAt was being fetched and thrown away.

The digest needs them to sort rows by age and to show how big a review
is. updatedAt can't do the sorting: it moves on every comment, so the
most argued-about pull request would look like the newest.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
localMomentIn returns the day and hour in a given IANA zone, using Intl
rather than arithmetic on a stored offset. A stored offset is wrong as
soon as the clocks move, and the digest promises nine in the morning
where the user is.

pool is the inbox warmer's concurrency helper, moved to shared unchanged
so the digest sweep can use it instead of copying it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Off by default. Every other poke answers a webhook the user already
asked to hear about, while this one is a new scheduled message.

The settings write sets each field rather than replacing the whole
pokeSettings subdocument, so an older client saving the fields it knows
can't wipe a digest schedule it has never heard of. Mutes keep the
whole-set replacement, which is how unmuting is expressed.

claimDigest reserves one digest per user per local day in a single
conditional write, so two sweeps or two replicas can't both send. It
stores a local date rather than an instant, so the rule holds across
daylight saving and half-hour zones.

Enabling the digest marks today as already sent, but only if the chosen
hour has passed. Turning it on at 16:00 with an hour of 09:00 won't fire
minutes later; turning it on at 08:00 still gets today's at 09:00.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
One message: a heading, then the rows under it oldest first. Delivery is
a fourth wrapper alongside the test and welcome messages, since the
private send() already resolves the workspace, opens and caches the DM,
and records failures.

No avatars. Slack fetches every image while posting and rejects the
whole message if one fails. A poke carries a single avatar; a digest
would carry one per row.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A timer shaped like the inbox warmer: bootstrap hook, setInterval, and 0
to disable it, which is what the test suite runs with. sweep takes the
instant to run at, so a spec can cover two timezones at once.

A pass sends to anyone whose hour has passed, not only those whose hour
it is right now, so a deploy across 09:00 costs minutes rather than the
whole day.

The day is claimed after the list is built, not before. Building only
reads, so two passes both building costs one GitHub query each, while
claiming first would let a single 502 burn the day. An empty list still
claims the day, or the rest of it costs an inbox rebuild every sweep.

The list goes through groupWaitingOnYou rather than reading the snapshot
directly, because author filters are applied on the way out. Reading the
rows directly would list authors the user has told the inbox to ignore.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The switch sits under the reel rather than as a tenth row in the list
above it. Those rows are kinds of event, counted in the header and
previewed in the window, and this is a schedule. The hour picker appears
only once the switch is on.

The browser's timezone is sent on every settings save. Only the browser
knows it, since GitHub exposes none and there is nothing on the session
to derive one from, so sending it each time follows a user who moves
without ever asking them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Andy9822

Copy link
Copy Markdown
Author

hey @ablaszkiewicz 👋

I opened this possible implementation and approach for the suggestion/feature request I opened.
LMK your thoughts and thanks for the project!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature idea: daily digest of what's waiting on you

1 participant