fix(notification): stamp matched types and provider cache in getCommonNotificationTxes - #11007
Open
clayrisser wants to merge 1 commit into
Open
Conversation
clayrisser
force-pushed
the
fix/notification-common-inbox-type-attribution
branch
from
August 13, 2026 06:47
eceff16 to
d48a20d
Compare
…nNotificationTxes getCommonNotificationTxes passes a literal [] where the matched notification types belong (index.ts:131) and never writes AvailableProvidersCache, so every notification born through it is types: [] with no cache entry. Its sibling getNotificationTxes does both correctly, four hundred lines away in the same file; this lifts the two blocks verbatim so the paths agree. types: [] defeats the tree's own fallbacks. push.ts:248 and gmail-resources/src/index.ts:324 both compute allowed providers from (n.types ?? [])[0] when the cache misses, and skip when it is undefined -- gmail logging "NotificationsHandler: skipping notification without type". Three callers are affected: time-resources (ToDo), and activity-resources for reactions and mentions. The reaction and mention callers are the tell: both resolve a NotificationType into a local, put it in notifyResult, and the callee drops it. Tests: server-plugins/notification-resources had jest --passWithNoTests and no suites; commonNotificationTypes.test.ts is the first. Three cases were red before this change (the type on the document, the cache entry, and the cache entry under the ignored-provider shape). Co-authored-by: Cursor <cursoragent@cursor.com> Signed-off-by: Clay Risser <clayrisser@gmail.com> Co-authored-by: Cursor <cursoragent@cursor.com>
clayrisser
force-pushed
the
fix/notification-common-inbox-type-attribution
branch
from
August 13, 2026 07:00
d48a20d to
d9d10a1
Compare
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.
Problem
getCommonNotificationTxes(server-plugins/notification-resources/src/index.ts:96-137) computes the matched notification types, then throws them away:pushInboxNotificationswrites that argument straight onto the notification document (:389-398,typesat:396), so every notification created through this function is borntypes: []— regardless of what actually matched.It also never populates
AvailableProvidersCache.getNotificationTxes:638-645publishes the allowed providers after creating its notification;getCommonNotificationTxesdoes not, and it is the only other place that creates one.The sibling function does both correctly, four hundred lines away in the same file:
That asymmetry is the whole bug.
Three callers are affected
server-plugins/time-resources/src/index.ts:303,314-328CommonInboxNotificationisShouldNotifyTx(...)'s fullnotifyResultserver-plugins/activity-resources/src/index.ts:138-160ReactionInboxNotificationnew Map(allowedProviders.map((it) => [it, [type]]))server-plugins/activity-resources/src/references.ts:136-172MentionInboxNotificationMentionNotificationTypeThe reaction and mention callers are the tell: each resolves a
NotificationTypeinto a local variable, puts it innotifyResult, and the callee drops it on the floor.This defeats the tree's own fallbacks
push.ts:248-260carries a fallback whose comment describes exactly the situation it is then defeated by:With
types: [],typeisundefinedand the fallback gives up.gmail-resources/src/index.ts:221,324has the same fallback and logs when it fires —NotificationsHandler: skipping notification without type. Both halves have to be fixed: a consumer that gates on the cache never gets as far as readingtypes, and a consumer that falls back totypesfinds it empty.Fix
14 lines, both blocks lifted verbatim from
getNotificationTxesso the two paths agree:const notifyContexts = await control.findAll(ctx, notification.class.DocNotifyContext, { objectId: attachedTo }) + const types = (notifyResult.get(notification.providers.InboxNotificationProvider) ?? []).map((it) => it._id) - await pushInboxNotifications( + const notificationTx = await pushInboxNotifications( … - [], + types, true, tx ) + + if (notificationTx !== undefined) { + const current: AvailableProvidersCache = control.contextCache.get(AvailableProvidersCacheKey) ?? new Map() + const providers = Array.from(notifyResult.keys()) + if (providers.length > 0) { + current.set(notificationTx.objectId, providers) + control.contextCache.set(AvailableProvidersCacheKey, current) + } + }Scope and residual risk
Nothing is force-enabled.
notifyResultis computed by the callers fromisAllowed, which already honoursignoredTypes, the per-user Settings → Notifications toggles, and each type'sdefaultEnabled. What changes is that the answer stops being discarded.That does mean this unblocks delivery for every notification born through this function, for every provider
isAllowedalready approved — so it is worth being explicit about what starts flowing:time:ids:ToDoCreated)activity:ids:AddReactionNotification)MentionInboxNotification) → emailisAllowed, then droppeddefaultEnabled: false, noenabledTypesentrynotification:ids:MentionNotificationTypeisdefaultEnabled: true(models/notification/src/index.ts:599-611) and gmail's provider isdefaultEnabled: truewith anignoredTypesthat does not list it (models/gmail/src/notification.ts:53-81), soisAllowedapproves email for mentions today and only the missing type suppresses the send. After this change a mention inside a followed conversation can produce two emails — one from theActivityInboxNotificationthat already worked, and one from theMentionInboxNotification.I have deliberately not changed that here. Suppressing it would mean adding
MentionNotificationTypeto gmail'signoredTypes, which is a product call rather than a bug fix, and I would rather surface the consequence than quietly bundle a behaviour decision into a correctness fix. Happy to add it in this PR if you'd prefer it landed together.Deliberately not touched:
getNotificationTxes:653sets the cache with the string literal'AvailableNotificationProviders'rather than the exportedAvailableProvidersCacheKey. Equal today, a drift hazard tomorrow — but it is not this bug, and widening a fix to carry a nit is how patches stop applying cleanly. Flagging rather than fixing.Verification
server-plugins/notification-resourceshadjest --passWithNoTestsand no suites;commonNotificationTypes.test.tsis the first one in it. jest, matching the package's own config.server-plugins/notification-resourcesserver-plugins/activity-resources(a caller)server-plugins/calendar-resourcesThe three reds were exactly the three new behaviours — the type on the document, the provider-cache entry, and the cache entry under the ignored-provider shape. The six that passed red are the control: three of them drive the real
isShouldNotifyTxwith the realToDoCreatedshape over a bareTxCreateDocand assert that a type is matched, which pins that the matcher was never the problem and the loss happens after it.Cases added:
stamps the ToDo type onto the notification it createsthe activity path is unchanged › keeps writing the types it is handeddoes not invent a type for a genuinely typeless notification—typesstays[], so gmail/push still skip itstill writes nothing at all when the inbox provider is absentdrops the type when the provider ignores itdrops the type when it is neither enabled nor defaultEnabledleaves a type out of the cache for a provider that did not allow ittsc --noEmit: thesrc/index.tserror set is byte-identical before and after once line numbers are normalised — the fix adds none.No UI change, so there is nothing to screenshot.
Provenance
Found while integrating a third-party notification consumer against a self-hosted deployment: ToDo notifications were created but never delivered, and the gmail handler's
skipping notification without typeline was the thread that led here. The mechanism above is read from the code and pinned by the tests; I have not measured the mention-email volume change on a live instance, which is why it is flagged as a question rather than asserted as safe.