Skip to content

feat(notifications)!: replace Notifications 1.0 with persistent notifications - #1967

Open
hmans wants to merge 40 commits into
mainfrom
hmans/notifications-2-0-details
Open

feat(notifications)!: replace Notifications 1.0 with persistent notifications#1967
hmans wants to merge 40 commits into
mainfrom
hmans/notifications-2-0-details

Conversation

@hmans

@hmans hmans commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Why

Notifications 1.0 coupled attention, handling, and deletion: opening or dismissing an item removed it, and users could not retain read items. Notifications 2.0 replaces it with a persistent notification list whose attention state is independent from deletion.

Closes #1556.

What

  • replace Notifications 1.0 with deterministic per-recipient notification occurrences in RUNTIME_STATE, with a 90-day absolute TTL
  • expose one chronological list containing both Unread and Read notifications; read rows remain visible but subdued
  • group the list into Today, Yesterday, This Week, and month sections using each account's preferred time zone
  • render full localized activity sentences such as “Bob has sent you a direct message.”
  • mark an unread notification Read only after its target message is displayed successfully
  • make permanent Delete the sole explicit row action; there is no Done, Restore, Mark Unread, or Save operation
  • optimistically delete individual groups and restore them when the server rejects the mutation
  • add an optimistic Dismiss All action backed by one authoritative request per authenticated server, with per-server rollback on partial failure
  • use the semantic notification-orange attention colour for unread rows and indicators, and show bell/server attention whenever at least one unread notification exists
  • render streamlined clickable rows with pointer cursors, a framed trash button, uncluttered metadata, and no per-row count badge
  • coalesce list invalidations so initial load performs one list request per authenticated server; preserve fulfilled results when another server fails
  • make the shared durable notification consumer the sole occurrence/lifecycle writer; request paths wait for its acknowledged EVT boundary and fence their local occurrence index when freshness matters
  • protect delayed work with durable visibility and read boundaries, using EVT stream order rather than wall-clock timestamps
  • reconcile implicit visibility loss from universal-room, room-group placement, room-ban, and relevant RBAC/role facts through a snapshot-capable exact-boundary projection
  • keep configured-email owners and event-time visibility aligned by durably retrying owner-role materialization after verification
  • validate exact target/reaction visibility after capturing and waiting recipient, server-wide room-event, room-group-layout, and RBAC projection tails
  • capture and wait the current config tail on every source OCC attempt before evaluating notification policy; retries recompute recipients, policy, and prepared work
  • fence Alert claims and occurrence mutations behind the existing durable notification worker without adding fence events
  • preserve thread roots for reaction targets so notification navigation reaches the exact thread and event
  • support per-cause Off/Badge/Alert policy at server and room scope for causes that have real producers
  • expose single-occurrence, group, and whole-list deletion
  • keep EVT limited to domain-changing facts: Notifications 2.0 adds no occurrence, candidate, plan, revocation, authorization-fence, or worker-trigger events
  • remove all Notifications 1.0 services, handlers, stores, settings, live behavior, generated clients, tests, and documentation
  • update generated ConnectRPC references, ADR-071, ADR-072, FDR-012, related feature records, the glossary, release notes, and the runtime architecture inventory
  • strengthen the event-sourcing guidance: a new EVT type is a last resort for a domain-changing fact, not an outbox or worker trigger

API and compatibility

  • Breaking public API change: Notifications 2.0 replaces the legacy notification RPCs and the earlier in-PR Inbox/Done API with one list, MarkNotificationRead, and delete operations. The PR carries the api-breaking-change label.
  • NotificationOccurrence.unread is the public attention state; the public Inbox/Done enum, view filter, general occurrence update, and group-state update are removed.
  • DeleteAllNotificationOccurrences is additive within the Notifications 2.0 API. It deletes the occurrence set current at the authoritative mutation boundary; clients must not automatically retry after an ambiguous failure because later activity can arrive after that boundary.
  • Notifications 2.0 is the sole supported notification implementation. Existing Notifications 1.0 records are not migrated, translated, dual-read, or exposed through a compatibility adapter.
  • The persisted core state retains the old numeric DONE enum value for protobuf/storage compatibility. Current code treats it as read and never writes it.
  • The bundled 0.5.0 client/server version gate rejects a new client connected to an older server. External integrations must regenerate clients for the replacement NotificationService methods.
  • During a rolling upgrade, old replicas can still write only legacy records; new replicas do not translate them. Notifications 2.0 becomes consistent once all writers are upgraded.

Verification

  • merged current origin/main (2b3b18736)
  • mise codegen-proto
  • mise test-cli
  • mise test-frontend (125 server files / 1,137 tests; 159 client files / 1,596 tests; 60 Storybook files / 180 tests; performance comparison passed)
  • mise lint-frontend diagnostics passed; focused ESLint passed after the final Svelte reactivity fix
  • production frontend build and bundle budgets
  • mise license-check
  • notification E2E: 33 passed before this UI follow-up; the changed interactions have focused browser-component coverage
  • Chrome DevTools verification: localized full-sentence rows, Today heading, notification-orange unread styling, Dismiss All button styling, immediate optimistic removal under Slow 3G, and one bulk-delete request followed by one reconciliation list request

hmans added 13 commits August 10, 2026 21:26
…-0-details

* origin/main:
  feat(events): add durable pull-worker execution (#1972)
…-0-details

* origin/main:
  perf(core): defer server member detail hydration (#1977)
  refactor(core): use durable consumers for cleanup (#1974)
  fix(core): make user key shredding crash-safe (#1973)
  test(e2e): add large-server performance CI (#1976)

# Conflicts:
#	cli/internal/evtstream/events_integration_test.go
#	cli/internal/evtstream/incremental_effect_consumer.go
#	cli/internal/pb/chatto/core/v1/event.pb.go
#	docs/adr/INDEX.md
#	docs/architecture/durable-effects.md
#	docs/architecture/nats-resources.md
#	docs/architecture/runtime-state.md
#	docs/fdr/INDEX.md
…-0-details

* origin/main:
  feat(admin): report durable worker health (#1979)

# Conflicts:
#	docs/architecture/durable-effects.md
…-0-details

* origin/main:
  fix(workers): harden durable recovery (#1978)

# Conflicts:
#	docs/architecture/durable-effects.md
@hmans hmans changed the title feat(notifications): add triageable notification inbox feat(notifications)!: replace Notifications 1.0 with triageable inbox Aug 11, 2026
@hmans hmans added the api-breaking-change Intentional pre-1.0 public API breaking change label Aug 11, 2026
hmans added 8 commits August 11, 2026 17:14
…-0-details

* origin/main:
  docs(events): document integration and logging contracts (#2004)
  fix(events): guard single-run lifecycles (#2003)
  fix(events): add bounded subject reads (#2002)
  fix(events): validate snapshot bindings (#2001)
  fix(events): require pointer projections (#2000)
  fix(events): make nil loggers safe (#1999)
  ci: run pull request workflows for stack bases (#1998)
  feat(auth): add server invite links (#1983)

# Conflicts:
#	apps/frontend/src/lib/api-client-tests/viewer.spec.ts
#	cli/internal/connectapi/api_contract_test.go
#	cli/internal/core/projection_wiring.go
#	cli/internal/evtstream/publisher.go
#	cli/internal/pb/chatto/core/v1/event.pb.go
#	docs/adr/INDEX.md
#	docs/architecture/interfaces.md
…-0-details

* origin/main:
  feat(messages): add pinned channel messages (#1990)

# Conflicts:
#	apps/docs-website/src/content/docs/releases/0-5-0.mdx
#	cli/internal/pb/chatto/core/v1/event.pb.go
#	cli/internal/pb/chatto/core/v1/projection_snapshots.pb.go
#	cli/internal/pb/chatto/realtime/v1/realtime.pb.go
@hmans hmans changed the title feat(notifications)!: replace Notifications 1.0 with triageable inbox feat(notifications)!: replace Notifications 1.0 with persistent notifications Aug 11, 2026
hmans added 2 commits August 11, 2026 22:04
…-0-details

* origin/main:
  feat(frontend): improve pinned message presentation (#2016)
  fix(frontend): preserve inline ordered-list content (#2010)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api-breaking-change Intentional pre-1.0 public API breaking change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Notifications 2.0

1 participant