Notifications & Nudges
Notification Triggers
Notifications become nudges when they matter — deterministic app-level overrides fire instantly, everything else is judged on the existing extract cron, all routed through the mode-aware nudge engine.
Situation
Building directly on Slice 1's notification-extract cron and the live nudge engine, this is the P2 "trigger" reaction of the Notification Intelligence track: making Mallory react to a notification as it happens, surfacing the ones that matter (a person you care about, an urgent/invoice/delivery-failed alert) while staying quiet about the rest.
The only action triggers take is firing a nudge through the existing engine, which already mode-routes every nudge (interrupt/batch/defer-to-Updates, quiet-hours-aware, high always breaks through) — so triggers add zero new delivery machinery, just two new ways to decide when to fire one.
Key decisions
Hybrid matching: instant overrides plus batched judgment
A hard, user-set app-level allow/mute list fires or blocks deterministically on notification arrival with no model call; everything else is judged by the LLM on the existing 2-hour notification-extract cron.
Action = nudge only
Triggers never invent new delivery machinery — placement (push/batch/Updates) is entirely handled by the existing mode-gated nudge engine.
Shared dedupeKey coordinates both paths
Both the instant-override and batched-judgment paths key on trigger:<eventId> against the existing nudgeSeen ledger, so if judgment later flags an event the override already fired, the engine silently drops the duplicate.
Trigger nudges fire at normal urgency in v1
Even an alwaysNudge app still respects quiet hours and mode-gating — no override implies always-interrupt; per-override urgency and model-flagged high are explicitly deferred.
emitNudge extracted as the one justified refactor
The emit logic inline in handleNudge is pulled into a reusable emitNudge(env, payload) function specifically because two new callers (the override path and the judgment path) now need it, alongside the existing /nudge route.
Architecture
A deterministic on-arrival path and a batched LLM-judgment path both funnel into one shared emit function, deduped by a per-event key.
parseTriggerConfig, overrideVerdict (always/mute/null), triggerDedupeKey, alertToNudge — all unit-tested./reminders/config; backs a two-list (always-nudge, mute) editor in web /settings.shouldSendNow → insertNudge/notify) now called by /nudge, the override path, and the judgment path.ctx; after storing events, ctx.waitUntil runs the override path per notification — alwaysNudge fires immediately, mute is skipped from trigger consideration (still stored for recall).alerts:[{i,reason}] array alongside facts/reminders; muted apps excluded from candidates.Risks & guardrails
Double-firing on the same event
Prevented by the shared per-event dedupeKey — an override-fired event is already 'seen' when judgment later reconsiders it, so the engine drops the duplicate rather than firing twice.
Override path blocking or delaying ingest
Runs entirely in ctx.waitUntil, never blocking or failing the ingest response; emitNudge failures are swallowed best-effort like the existing nudge path.
No config means no false positives
Absent triggers:config or KV, overrides are simply empty and behaviour degrades to pure LLM judgment rather than failing closed or open unpredictably.
Delivery
Pure triggers lib + config route
triggers.ts built under tests first, then GET/POST /triggers/config wired and Access-gated.
On-arrival override path
handleNotificationIngest threads ctx and fires the deterministic always/mute path via emitNudge.
Batched judgment path
notificationExtract's prompt and parser extended to emit an indexed alerts array, mapped back to events and emitted via emitNudge.
Settings + full-suite verification
Web settings section for the two app lists; full-suite verification before PR — no D1 migration (KV only), no Android changes, deploy held for Alex.
Out of scope
docs/superpowers/specs/2026-06-21-notification-triggers-design.mdplan ·
docs/superpowers/plans/2026-06-21-notification-triggers.md