Appearance
Notification Route Rehearsals
Status: Static non-default route-table proof only for promotion; current live API recheck and a disposable student-inbox browser candidate exist, while the default route remains unchanged.
This proof covers the notification inbox and preference compatibility routes that can be rehearsed against notification-service without moving default traffic. It does not promote the default route table. The student browser candidate proves inbox list/read actions and Profile notification preferences; it is not default-route parity.
Route Tables
| Table | Target |
|---|---|
deploy/gateway/routes.json | Default rollback table; broad /api/notifications, /api/alerts, /api/admin, and fallback stay legacy_proxy. |
deploy/gateway/routes.notifications-native-example.json | Compose non-default rehearsal targeting http://notification-service:8080. |
deploy/gateway/routes.notifications-native-localhost-example.json | Localhost non-default rehearsal targeting http://localhost:8092. |
Native Rehearsal Scope
The non-default notification route tables may route only these paths to notification-service:
GET /api/notificationsGET /api/notifications/{notificationId}POST /api/notifications/{notificationId}/readPOST /api/notifications/{notificationId}/deletePOST /api/notifications/read-allPOST /api/notifications/delete-allGET /api/notifications/preferencesPATCH /api/notifications/preferencesGET /api/alerts/preferencesPATCH /api/alerts/preferences
Every native notification route must require gateway auth header injection. The route tables intentionally do not require organization at the gateway because the legacy notification shell is user-scoped and can fetch with only Authorization.
Still Legacy
These surfaces must remain legacy-proxied until separate recipient-resolution, admin audit, support, and delivery-provider adapters exist:
- notification create/batch/snapshot routes
- weak-topic alert creation
- parent-alert routes outside their dedicated parent-alert rehearsal
- admin inbox/broadcast routes
- broad
/api/notifications,/api/alerts,/api/admin, and fallback
Verification
bash
make test-notification-routesThe guard verifies the default rollback table, both non-default route tables, method specificity, route ordering before broad legacy routes, auth requirement, and absence of global role or organization requirements on the notification inbox/preference rehearsal.
Promotion Conditions
Before any default notification route promotion:
- run the static guard above
- attach live gateway proof against disposable notification rows
- attach browser proof for the notification bell/preferences path
- prove rollback by returning to
deploy/gateway/routes.json - document any recipient-resolution, admin broadcast, weak-topic, or provider delivery surfaces that stay legacy
Current Live API Recheck (2026-07-13)
An isolated notification-service on :8092 received an already-resolved attempt grade-release recipient. Its duplicate source event returned the same event id and did not create another inbox row. A temporary Gateway on :18093 used only deploy/gateway/routes.notifications-native-localhost-example.json.
An HS256 student token reached list, detail, mark-read, and preferences with the matching notifications-* native_read/native_write route headers. The mark-read response persisted readAt. Restarting that temporary Gateway with GATEWAY_ROUTE_TABLE=deploy/gateway/routes.json returned the same request to legacy_proxy; the expected 502 was from the intentionally unavailable legacy proof target. Artifacts are in output/qa/notification-recipient-route-proof-20260713/.
Student Inbox Browser Candidate (2026-07-15)
make test-student-notification-browser starts a memory-only notification-service, a temporary api-gateway, and a Student Vite process on random local ports. It copies the localhost candidate route table into the artifact directory with only the notification target rewritten; it never edits deploy/gateway/routes.json.
The runner seeds two notifications directly into its own memory-only service as an admin, rejects the anonymous Gateway list, and creates an HS256 STUDENT session only in the browser context. The browser opens /student/notifications?studentNotificationGateway=native, verifies notifications-list with native_read, renders two owned inbox rows, marks one through notifications-read, then marks the remaining inbox row through notifications-read-all, both with native_write. Request artifacts redact the bearer token. It then opens /student/profile?studentNotificationGateway=native, reads preferences via notifications-preferences-read, and saves the merged preference map through notifications-preferences-write. This is disposable candidate evidence for the student inbox and Profile notification preferences, not a default-route promotion or full notification-bell parity. The latest local artifact is output/qa/student-notification-browser-local-runtime-20260715-034031/: its browser trace recorded two authenticated GET, two authenticated POST, and one authenticated PATCH, with all five candidate route headers. The Gateway list request took 1.06 ms in the shell check and 14 ms in the browser trace; request artifacts contain only Bearer [redacted].
Related Storage Atomicity Proof (2026-07-15)
NOTIFICATION_EVENT_FANOUT_POSTGRES_CONFIRM=disposable-postgres make test-notification-event-fanout-postgres starts a named PostgreSQL 16 container on a random local port, then each focused test creates and removes a temporary schema. It never uses Compose or a configured notification-service DSN.
TestPostgresNotificationEventFanoutTransaction installs a trigger that fails the second preference-approved recipient write. It proves the failure rolls back both the source event and the first recipient row; after removing the trigger, retry creates one source event and two inbox rows, while a later source-event replay returns the same event without duplicating rows.
The runner first self-checks artifact generation without Docker, then writes a time-stamped summary.json and focused go-test.log. The current local bundle is output/qa/notification-event-fanout-postgres-20260715-034023/. This is owner-storage evidence, not delivery-provider, browser, default-route, or production proof.