You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Let the digest size fit also trim leftover channel chats #454
The digest size fit trims only chats that sit under a changed heading. It never trims the chats of channels that are not a heading section. A document with many busy channels, or a combined mail of many documents, can pass the size limit on those chats alone. Gmail then clips the end of the mail. After #449, that end holds the closing line with the stop-all link.
Land after #449. It changes the status bars and the closing line that the fit measures.
Where
packages/email-templates/src/digestFit.ts:57-73: dropOldestChat walks only content_changes.sections[].chats.
digestFit.ts:82: an emptied chatOnly section is dropped. A channel needs the same rule.
digestFit.ts:116-120: the fit runs at most 500 steps. When no section chat and no passage can shrink, it returns the documents as they are, still over the limit.
packages/email-templates/src/digestWalk.ts:70-78: every leftover channel paints its first DIGEST_CHANNEL_LINES (5) notices, then +N more, with no limit on the number of channels. Notices arrive sorted by created_at ascending (packages/supabase/scripts/07-5-email-notifications-pgmq.sql:645), so the painted five are the oldest.
Read from code, not measured. With many busy channels, in one document or in a combined mail, the leftover channel notices grow the HTML. It passes the configured limit (default 90 KB) and Gmail's 102 KB clip limit. Gmail hides the tail behind "View entire message", so the closing line and its stop-all link do not show.
Fix
In dropOldestChat (packages/email-templates/src/digestFit.ts), also scan doc.channels[].notifications. Pick the oldest notice by created_at across section chats and channel notices. The rule "never drop a changed heading; drop oldest chat first" stays as it is.
When the pick is a channel notice, do this in one step:
If the channel has more than DIGEST_CHANNEL_LINES notices, first cut its list to the first DIGEST_CHANNEL_LINES. The walk paints only those. Without the cut, dropping one row pulls the next hidden row into view. The HTML then does not shrink, and the step is wasted against the 500-step cap.
Then drop the picked notice.
If the channel has no notice left, drop the channel, as line 82 drops an empty chatOnly section. Otherwise the walk paints a bare # name heading.
No new export, field or helper.
Out of scope
DIGEST_CHANNEL_LINES, which five notices a channel shows (the oldest), the 500-step cap, grouping, and the max-KB setting stay as they are.
The subject count and the bell badge fall with each dropped channel notice, through countDigestItems. Dropped section chats already do this.
Acceptance criteria
A new test sits beside the existing fit tests in packages/email-templates/src/__tests__/engine.test.ts. It builds one document with no content_changes and one channel of 7 notices. The previews have equal length. The limit is the full HTML size minus 1. The fitted HTML is at or under the limit. Notice 1 is gone, notice 2 still shows, and more in this channel is gone.
A second test puts the oldest notice alone in its channel. After the fit, that channel's # name heading is gone.
Both new tests fail against the unchanged dropOldestChat. Observed.
The existing fit tests still pass, so section chats and changed headings keep their rules. Observed green.
The HTML-cap bullet in apps/hocuspocus.server/CLAUDE.md §Digest Email Links And Counts says the fit also drops leftover channel chats, and drops a channel left with none.
Verify
bun run --filter @docs.plus/email-templates test. The package script sets APP_URL and NEXT_PUBLIC_APP_URL; a bare bun test does not.
cd apps/hocuspocus.server && bun test tests/unit/contentChangeDigest.test.ts. It runs the fit at maxBytes 1 and must stay green.
Open decisions
Once the fit cuts a long channel, its +N more count is gone. Is that acceptable? Recommended: yes. The fit runs only when the mail is over the cap. A true count would need a new payload field. The channel heading still links to the room.
Summary
The digest size fit trims only chats that sit under a changed heading. It never trims the chats of channels that are not a heading section. A document with many busy channels, or a combined mail of many documents, can pass the size limit on those chats alone. Gmail then clips the end of the mail. After #449, that end holds the closing line with the stop-all link.
Land after #449. It changes the status bars and the closing line that the fit measures.
Where
packages/email-templates/src/digestFit.ts:57-73:dropOldestChatwalks onlycontent_changes.sections[].chats.digestFit.ts:82: an emptiedchatOnlysection is dropped. A channel needs the same rule.digestFit.ts:116-120: the fit runs at most 500 steps. When no section chat and no passage can shrink, it returns the documents as they are, still over the limit.packages/email-templates/src/digestWalk.ts:70-78: every leftover channel paints its firstDIGEST_CHANNEL_LINES(5) notices, then+N more, with no limit on the number of channels. Notices arrive sorted bycreated_atascending (packages/supabase/scripts/07-5-email-notifications-pgmq.sql:645), so the painted five are the oldest.packages/email-templates/templates/digest.eta: after Add an "Unfollow this document" link to each document in the Change digest #449, the closing line with the stop-all link is the last thing in the mail.What happens
Read from code, not measured. With many busy channels, in one document or in a combined mail, the leftover channel notices grow the HTML. It passes the configured limit (default 90 KB) and Gmail's 102 KB clip limit. Gmail hides the tail behind "View entire message", so the closing line and its stop-all link do not show.
Fix
In
dropOldestChat(packages/email-templates/src/digestFit.ts), also scandoc.channels[].notifications. Pick the oldest notice bycreated_atacross section chats and channel notices. The rule "never drop a changed heading; drop oldest chat first" stays as it is.When the pick is a channel notice, do this in one step:
DIGEST_CHANNEL_LINESnotices, first cut its list to the firstDIGEST_CHANNEL_LINES. The walk paints only those. Without the cut, dropping one row pulls the next hidden row into view. The HTML then does not shrink, and the step is wasted against the 500-step cap.chatOnlysection. Otherwise the walk paints a bare# nameheading.No new export, field or helper.
Out of scope
DIGEST_CHANNEL_LINES, which five notices a channel shows (the oldest), the 500-step cap, grouping, and the max-KB setting stay as they are.countDigestItems. Dropped section chats already do this.Acceptance criteria
packages/email-templates/src/__tests__/engine.test.ts. It builds one document with nocontent_changesand one channel of 7 notices. The previews have equal length. The limit is the full HTML size minus 1. The fitted HTML is at or under the limit. Notice 1 is gone, notice 2 still shows, andmore in this channelis gone.# nameheading is gone.dropOldestChat. Observed.apps/hocuspocus.server/CLAUDE.md§Digest Email Links And Counts says the fit also drops leftover channel chats, and drops a channel left with none.Verify
bun run --filter @docs.plus/email-templates test. The package script setsAPP_URLandNEXT_PUBLIC_APP_URL; a barebun testdoes not.cd apps/hocuspocus.server && bun test tests/unit/contentChangeDigest.test.ts. It runs the fit atmaxBytes1 and must stay green.Open decisions
+N morecount is gone. Is that acceptable? Recommended: yes. The fit runs only when the mail is over the cap. A true count would need a new payload field. The channel heading still links to the room.