Skip to Content

← All archived intake epics

Intake: notification-reliability

breakdown.md

Breakdown: Notification reliability — inbox accents and failure tracking

  • scope-slug: notification-reliability · source: Jamie's platform-audit notes (2026-08-17) + the web-app feature audit; no scope.md behind this batch. Notes quoted per stub.
  • initiative: Refine the bridge / objective: Q2-2026 Objective 3 — Validate Technical Infrastructure & Payout Flow
  • personas: Admin, CSM, SDM, Expert, Vendor, Customer

What I understood

Two notification defects. The inbox lost its per-family accent colours when a schema cleanup deleted the UPPER_SNAKE constants the colour map keyed on — every row now renders the default accent (a known issue accepted at the remove-dead-misc-schema gate but never filed). And the notification system fails silently by design — Ably and Resend errors are swallowed — with nothing anywhere recording a failed delivery, so a broken template or key would go unnoticed indefinitely. Jamie wants accents restored and a place where failures are tracked.

Where it sits

Cross-cutting (notifications reach all six personas); feature-role-matrix: notifications. Lands in apps/web (inbox UI, an admin visibility surface) and packages/services/src/notifications/ (delivery outcome recording).

Build order

  1. inbox-accent-colours — re-key the inbox accent map to the live dot.case types — depends-on: none
  2. notification-failure-tracking — record delivery failures and surface them to admins — depends-on: none

Out of scope (whole scope)

  • Per-tenant notification routing config, escalation rules, multi-language templates — docs roadmap, not in the notes.
  • Retry/redelivery machinery — tracking first; retrying is a follow-up once failure volume is visible.

_done/inbox-accent-colours.md

Stub: Restore the notification inbox accent colours

  • feature-slug: inbox-accent-colours
  • scope: notification-reliability
  • personas: Admin, CSM, SDM, Expert, Vendor, Customer
  • initiative: Refine the bridge / objective: Q2-2026 Objective 3 — Validate Technical Infrastructure & Payout Flow
  • depends-on: none
  • sequence: 1 of 2

Problem

notification-inbox-list.tsx keys its accent map on the UPPER_SNAKE notification constants that the remove-dead-misc-schema run deleted, so every inbox row renders the default accent — the visual grouping by family (bids vs invoices vs SLA breaches) is gone. Known at that run's ship gate, left for intake, never filed. Jamie's note: "file a fix for the inbox row default accent color issue We need to have accent colours in the notification inbox."

Proposed change

Re-key the accent map to the live dot.case type families (bid.*, invoice.*, quote.*, project.*, service-lead.*, user.*, messaging.*, sla.*) — mapping by family prefix rather than per-type constants so future types in a family inherit their colour — using the semantic tokens so both themes hold.

Acceptance criteria (rough)

  • Each notification family renders a distinct accent in the inbox and the sidebar notification centre, in light and dark mode.
  • A type with no mapped family still renders the default accent (no crash, no blank).
  • A new type added to an existing family needs no accent-map edit.

Out of scope (this feature)

  • Failure tracking (stub 2), preference changes, or template work.

Notes for Define

Evidence: remove-dead-misc-schema release note ("notification-inbox-list.tsx keys its accent map on the UPPER_SNAKE constants this run deletes, so every inbox row now renders the default accent — left for intake but no stub filed"); the 25 live dot.case types across 8 families in packages/services/src/notifications/. Colour choices follow the brand-guidelines skill (tint for surfaces, semantic tokens). touches: apps/web/components (notification inbox + centre).

_done/notification-failure-tracking.md

Stub: Track failed notification deliveries

  • feature-slug: notification-failure-tracking
  • scope: notification-reliability
  • personas: Admin
  • initiative: Refine the bridge / objective: Q2-2026 Objective 3 — Validate Technical Infrastructure & Payout Flow
  • depends-on: none
  • sequence: 2 of 2

Problem

Notifications fail silently by design — the Ably publish warns and swallows, email sending no-ops when unconfigured, and per-send errors vanish. Sensible for the sender's UX, but nothing records that a delivery failed, so a broken template, revoked API key, or bad recipient would go unnoticed until a human complains. Jamie's note: "notifications fail silently by design, we need somewhere to track failed notifications."

Proposed change

Record the delivery outcome per channel (in-app / Ably / email) on or alongside the notification record — failure reason, channel, timestamp — without making sends any less fire-and-forget for the caller. Surface it for admins: a failure count with recent failures (type, recipient, channel, reason) on an admin surface, and a loud signal on the dashboard when failures spike. Keep volume sane on the platform's highest-volume collection (cap or TTL the failure detail).

Acceptance criteria (rough)

  • A failed Ably publish or Resend send is recorded with channel + reason; the triggering user flow is unaffected (still non-blocking).
  • An admin can see recent failures and per-channel counts; zero-failure state is honest.
  • A misconfigured key (e.g. ABLY_API_KEY unset in an env where it should be set) becomes visible within a day, not never.
  • Storage is bounded (TTL or cap) — no unbounded failure log on the busiest collection.

Out of scope (this feature)

  • Automatic retries/redelivery — visibility first.
  • Alerting to external channels (email-the-admin-about-email is circular; revisit later).

Open questions

  • Q1: Where does failure state live — fields on Notification, or a separate bounded notification-delivery-log? The db-audit already flags notifications as the highest-volume collection with no TTL; Define should answer both together (the missing TTL is db-audit finding #7).
  • Q2: What counts as a "failure" for in-app (the DB write itself failing is a different class than Ably being down)? Define the taxonomy before instrumenting.

Notes for Define

Evidence: swallow points in packages/services/src/notifications/core/publish.ts (warn + swallow), core/send-email.ts, core/instance.ts (documented fail-silent design, getResendIfConfigured no-op); models/notification.ts. New model → demo-reset purge-policy classification (its drift test will insist). Follow the notification skill; any new admin route follows web-route. touches: packages/services/src/notifications/, apps/web (admin surface).

Answers from Jamie — interrogation 2026-08-17

Recorded by the intake-easy-features session; these rulings bind Define.

  • Q1 (failure state home): separate bounded delivery log — a capped/TTL'd notification-delivery-log collection; the notification document stays lean. Pairs with db-audit finding #7 (missing TTL) — answer both together at Define.

Resolved at Build — 2026-08-18

Q2 (what counts as a failure) had no ruling to inherit, so the taxonomy was settled in the build and now lives on the model (packages/services/src/db/models/notification-delivery-log.ts):

  • in-apppersist-failed: the notifications write itself failed, so no notification exists at all. A different class from a channel being down, exactly as Q2 anticipated.
  • realtimetransport-error (the Ably publish threw) and not-configured (ABLY_API_KEY absent).
  • emailtransport-error (Resend threw or returned an error in its response), not-configured (RESEND_API_KEY absent), template-missing (no TEMPLATE_REGISTRY entry — the silent never-sends), recipient-missing (no address on the user record).

Deliberate suppression is not a failure and is never recorded: a preference switched off, a type in EMAIL_DISABLED_TYPES, or the fan-out's one-email-per-person dedupe. A not-configured row is written once per channel per process, so a missing key is visible within the day without burying the per-send failures.