Notifications: deleting a notification creates a 'Notification deleted' notification #1194

Open
opened 2026-10-06 08:54:31 +00:00 by kayg · 13 comments
Owner

Owner report (2026-10-06, production f5fbced3c): "every notification delete is also a notification?"

Deleting a notification shows an Undo toast, and #1142's rule ("every toast also goes to Notifications") then writes "Notification deleted · Undo" into Notifications — a loop.

Fix (refines #1142):

  1. Actions performed inside the Notifications panel (delete, clear all, mark read/unread) never create a toast and never create a notification. Undo is inline: the deleted row collapses into a slim "Deleted · Undo" row in place for ~5 s (clear all: one slim "Cleared 12 · Undo" row), using the shared row/Pill styles.
  2. General rule for the shared toast adapter: a toast is written to Notifications only for background results (imports, uploads, syncs, jobs) and for actions whose subject is no longer on screen. Actions confirmed in place, or performed on the notification surface itself, are never persisted. Encode this as an explicit persist decision on each toast category in the adapter (default false for user-initiated in-place actions), with a design-rule test that fails if any notification-panel action or any notification.* toast is persisted.
  3. Remove the already-created "Notification deleted" entries: a one-time cleanup migration deleting notifications of kind web_toast whose text is a notification-management confirmation (match by the toast category id, not by text), with a preservation test.
    Screenshots at actual size: delete one, clear all, Undo, light + dark. Merge origin/dev; bun run check + bun run test pass. Commit; comment with head SHA. Do not push.
## Owner report (2026-10-06, production f5fbced3c): "every notification delete is also a notification?" Deleting a notification shows an Undo toast, and #1142's rule ("every toast also goes to Notifications") then writes "Notification deleted · Undo" into Notifications — a loop. Fix (refines #1142): 1. Actions performed inside the Notifications panel (delete, clear all, mark read/unread) never create a toast and never create a notification. Undo is inline: the deleted row collapses into a slim "Deleted · Undo" row in place for ~5 s (clear all: one slim "Cleared 12 · Undo" row), using the shared row/Pill styles. 2. General rule for the shared toast adapter: a toast is written to Notifications only for background results (imports, uploads, syncs, jobs) and for actions whose subject is no longer on screen. Actions confirmed in place, or performed on the notification surface itself, are never persisted. Encode this as an explicit `persist` decision on each toast category in the adapter (default false for user-initiated in-place actions), with a design-rule test that fails if any notification-panel action or any `notification.*` toast is persisted. 3. Remove the already-created "Notification deleted" entries: a one-time cleanup migration deleting notifications of kind web_toast whose text is a notification-management confirmation (match by the toast category id, not by text), with a preservation test. Screenshots at actual size: delete one, clear all, Undo, light + dark. Merge origin/dev; `bun run check` + `bun run test` pass. Commit; comment with head SHA. Do not push.
Author
Owner

Owner (2026-10-06): the row ✕ tooltip shows "Delete ⌫" although rows cannot be selected. Fix in this issue: keyboard-navigable rows (↑/↓, Enter, ⌫ deletes the focused row) and shortcut hints only where they work, with a shared test.

Owner (2026-10-06): the row ✕ tooltip shows "Delete ⌫" although rows cannot be selected. Fix in this issue: keyboard-navigable rows (↑/↓, Enter, ⌫ deletes the focused row) and shortcut hints only where they work, with a shared test.
Author
Owner

Starting #1194 on branch job/notifloop-1194, based on origin/dev at 5301e02085. I am tracing the toast adapter, notification actions, migrations and shortcut contexts before making the UI and cleanup changes.

Starting #1194 on branch job/notifloop-1194, based on origin/dev at 5301e020859c1d61b4a7f812a5049a64241203e8. I am tracing the toast adapter, notification actions, migrations and shortcut contexts before making the UI and cleanup changes.
Author
Owner

Finding: apps/web/src/lib/notifications/inbox.svelte.ts emits an actionable "Notification deleted" toast from #confirmInboxMutation, and apps/web/src/lib/stores/toasts.svelte.ts persists every actionable toast. That path reproduces the reported loop. InboxPanel.svelte has a Delete shortcut on the row-open control, but the rows are separate Tab stops and have no ArrowUp/ArrowDown roving focus.

Finding: apps/web/src/lib/notifications/inbox.svelte.ts emits an actionable "Notification deleted" toast from #confirmInboxMutation, and apps/web/src/lib/stores/toasts.svelte.ts persists every actionable toast. That path reproduces the reported loop. InboxPanel.svelte has a Delete shortcut on the row-open control, but the rows are separate Tab stops and have no ArrowUp/ArrowDown roving focus.
Author
Owner

Evidence for the legacy cleanup constraint: migration 0007 creates notifications with no toast category field. persist_web_toast used a random row UUID and stored only a web_toast:<random event UUID> dedupe key. The old category cannot be recovered from the row or receipt. Migration 0008 tags those rows legacy; migration 0009 deletes only the new management category IDs. It preserves old ambiguous rows because matching their title would violate the issue's category-only cleanup rule.

Evidence for the legacy cleanup constraint: migration 0007 creates `notifications` with no toast category field. `persist_web_toast` used a random row UUID and stored only a `web_toast:<random event UUID>` dedupe key. The old category cannot be recovered from the row or receipt. Migration 0008 tags those rows `legacy`; migration 0009 deletes only the new management category IDs. It preserves old ambiguous rows because matching their title would violate the issue's category-only cleanup rule.
Author
Owner

Finding during verification: the first bun run check reported an unused or changed exact coverage.surfaces pin for CanvasView.svelte. Reclassifying a toast inside that Svelte file changed its source fingerprint. I refreshed only the three live CanvasView surface hashes in contracts/perf/exceptions.json and contracts/perf/adoption-1058.json; the rule limits and ratchet counts are unchanged. The same check also found that the generated API client does not yet include this change's category field and routes, plus two union-narrowing errors and a swipe-type annotation. I am updating the generated contract and fixing those type errors before rerunning the check.

Finding during verification: the first `bun run check` reported an unused or changed exact `coverage.surfaces` pin for `CanvasView.svelte`. Reclassifying a toast inside that Svelte file changed its source fingerprint. I refreshed only the three live CanvasView surface hashes in `contracts/perf/exceptions.json` and `contracts/perf/adoption-1058.json`; the rule limits and ratchet counts are unchanged. The same check also found that the generated API client does not yet include this change's category field and routes, plus two union-narrowing errors and a swipe-type annotation. I am updating the generated contract and fixing those type errors before rerunning the check.
Author
Owner

Shortcut-context finding: the first web type-check also reported a redundant panel condition on Quick Look's Info tooltip. Review found that Search's “See all results” button overrode the shared tooltip's scoped aria-keyshortcuts even when the shortcut handler was inactive. I removed that override, supplied the active surface scope while the action is available, and added a no-context regression assertion. The Quick Look button now declares its Photos scope only in the branch where its handler is mounted.

Shortcut-context finding: the first web type-check also reported a redundant `panel` condition on Quick Look's Info tooltip. Review found that Search's “See all results” button overrode the shared tooltip's scoped `aria-keyshortcuts` even when the shortcut handler was inactive. I removed that override, supplied the active surface scope while the action is available, and added a no-context regression assertion. The Quick Look button now declares its Photos scope only in the branch where its handler is mounted.
Author
Owner

Finding: cargo run -p calternal-server -- openapi compiled successfully but panicked at action_contract.rs:74 with notifications_inbox_clear: declare action policy before publishing the operation. The new notifications_inbox_clear and notifications_inbox_mark_unread operations were absent from both contracts/action-policy.json and contracts/action-authority.json. I added each as a User-scoped data write with no replay guarantee; no new adapter authority or server capability is granted. I will regenerate OpenAPI and the API clients after the server build.

Finding: `cargo run -p calternal-server -- openapi` compiled successfully but panicked at `action_contract.rs:74` with `notifications_inbox_clear: declare action policy before publishing the operation`. The new `notifications_inbox_clear` and `notifications_inbox_mark_unread` operations were absent from both `contracts/action-policy.json` and `contracts/action-authority.json`. I added each as a User-scoped data write with no replay guarantee; no new adapter authority or server capability is granted. I will regenerate OpenAPI and the API clients after the server build.
Author
Owner

#1194 verification finding: the first cargo test -p calternal-server run exposed that the production upgrade fixtures stopped at Notifications migration 7. The registry now correctly applies migrations 8, 9 and 10, so the pending-migration assertion omitted those three IDs and the preservation-count assertions were short by three. I updated the server upgrade test to pin 8–10 and preserve all earlier receipts. The same run had a shared-host startup timeout in wire::tests::startup_serves_http_while_upgrade_backfills_wait (16.38 s); it ran inside the long live_apps_run_in_separate_processes harness after the suite had taken 110.95 s. I will report this load-sensitive test result separately from the migration fix.

#1194 verification finding: the first `cargo test -p calternal-server` run exposed that the production upgrade fixtures stopped at Notifications migration 7. The registry now correctly applies migrations 8, 9 and 10, so the pending-migration assertion omitted those three IDs and the preservation-count assertions were short by three. I updated the server upgrade test to pin 8–10 and preserve all earlier receipts. The same run had a shared-host startup timeout in `wire::tests::startup_serves_http_while_upgrade_backfills_wait` (16.38 s); it ran inside the long `live_apps_run_in_separate_processes` harness after the suite had taken 110.95 s. I will report this load-sensitive test result separately from the migration fix.
Author
Owner

#1194 screenshot finding: the first production-build Playwright run failed while saving its light theme. The API returned HTTP 507 (Home storage quota or server free-space reserve reached); df -h showed 24G available on the host filesystem. The screenshot only needs the selected appearance in the browser, so the runner now uses the existing setTheme(..., { persist: false }) path. Notification rows and mutations remain real server data. I am rerunning the full 24-case capture.

#1194 screenshot finding: the first production-build Playwright run failed while saving its light theme. The API returned HTTP 507 (`Home storage quota or server free-space reserve reached`); `df -h` showed 24G available on the host filesystem. The screenshot only needs the selected appearance in the browser, so the runner now uses the existing `setTheme(..., { persist: false })` path. Notification rows and mutations remain real server data. I am rerunning the full 24-case capture.
Author
Owner

#1194 e2e finding: the context menu did open from the focused row with Shift+F10, but the test scoped its menu-item lookup to the Notifications dialog. The shared Menu is rendered in the page overlay layer, outside that dialog subtree. I changed the test to query the page-level menu item, preserving the actual keyboard interaction and action assertion.

#1194 e2e finding: the context menu did open from the focused row with Shift+F10, but the test scoped its menu-item lookup to the Notifications dialog. The shared Menu is rendered in the page overlay layer, outside that dialog subtree. I changed the test to query the page-level menu item, preserving the actual keyboard interaction and action assertion.
Author
Owner

#1194 screenshot finding: after switching the test theme to browser-seeded mode, a later full route load restored the previous cached appearance before the assertion. The first width/theme pair captured correctly; the next light case rendered dark. I moved ephemeral theme seeding to the already-open Notifications route, so the review theme is applied after the final navigation without a server preference write.

#1194 screenshot finding: after switching the test theme to browser-seeded mode, a later full route load restored the previous cached appearance before the assertion. The first width/theme pair captured correctly; the next light case rendered dark. I moved ephemeral theme seeding to the already-open Notifications route, so the review theme is applied after the final navigation without a server preference write.
Author
Owner

#1194 profile finding: the initial local benchmark's 10,000-row burst fixture seeded a second 10,000 rows after the prior clear had been undone, so it exercised 20,000 rows while expecting a 10,000-row receipt. That run failed its all-200 burst assertion and emitted no profile report. A separate fresh 10,000-row status-only diagnostic returned HTTP 200 for all four concurrent clears. I fixed the benchmark to clear restored rows between scenarios; the next run will measure the declared 10,000-row burst.

#1194 profile finding: the initial local benchmark's 10,000-row burst fixture seeded a second 10,000 rows after the prior clear had been undone, so it exercised 20,000 rows while expecting a 10,000-row receipt. That run failed its all-200 burst assertion and emitted no profile report. A separate fresh 10,000-row status-only diagnostic returned HTTP 200 for all four concurrent clears. I fixed the benchmark to clear restored rows between scenarios; the next run will measure the declared 10,000-row burst.
Author
Owner

#1194 complete

Branch: job/notifloop-1194
Head: bbbaaf3c555ddf853df734d59135ba67df76687a
No push, deploy or merge was performed.

Built

  • Added explicit persistence choices for every toast category. Background results and outcomes whose subject left the screen persist; in-place actions default to false, and every notification.* category is false.
  • Notifications-panel delete, clear-all and read-state actions do not toast or write Notifications. Delete and clear use inline five-second Undo rows backed by server receipts. Clear preserves up to 10,000 rows in 80-row database batches in one transaction.
  • Added roving Arrow-key focus, Enter to open, Delete/Backspace to delete the focused row, focus restoration, and a shortcut hint only while its notifications handler scope is active.
  • Added category-ID migrations and production migration preservation assertions, the mark-unread and clear/Undo routes, OpenAPI/client generation, toast-policy and adversarial coverage, plus the route perf profile.

Files

  • Backend: crates/plugins/notifications/src/routes.rs, store.rs, migrations 0008_toast_category_ids.sql, 0009_cleanup_notification_management_toasts.sql, 0010_inbox_clear_undo.sql; crates/calternal-server/src/upgrade_tests.rs.
  • Notifications UI and tests: apps/web/src/lib/notifications/{InboxPanel.svelte,inbox.svelte.ts,model.ts,inbox.svelte.test.ts,model.test.ts}; toast adapter/policy tests, shortcut registry/tests and tooltip tests under apps/web/src/lib/{stores,shortcuts,tooltip}.
  • Shared UI and affected tooltip call sites: packages/ui/src/components/tooltip/tip.ts and its callers in app/shared controls; generated API/action/OpenAPI and perf contracts under packages/api-client/src/generated.ts and contracts/.
  • Review and verification: apps/web/e2e/notifications-1194.mjs, apps/web/package.json, bench/notifications-inbox-1194.mjs, tests/adversarial/{run.sh,toast_notifications.mjs}, docs/DESIGN.md.

UX gaps closed / left

Closed: pointer, touch swipe/long-press, and keyboard row actions; accessible row names and read state; page-level context menu; inline delete/clear Undo; keyboard focus restoration; shortcut hints disappear outside the active row scope; real API loading, empty, error and pagination states remain in use. Shared controls used are OverlaySurface, Menu, CopyLink, Pill, PillGroup, and shared tip tooltips. The existing row X action keeps its existing icon-button control and now advertises Delete only in its active keyboard context.

Left: deployed legacy web_toast rows have no category ID. Migration 0008 tags them legacy; migration 0009 deletes only the four exact management category IDs and preserves untagged rows. The old “Notification deleted” rows therefore cannot be removed safely by category without guessing from mutable text. Visual quality remains for the orchestrator to review from the attached production screenshots.

Decisions where the design was silent

  • Note and Canvas recovery actions keep their subject on screen, so they use the non-persistent in-place toast policy.
  • The mutation-receipt GET category is background because only the service worker uses it on notification clicks.
  • Historic toast rows without category IDs stay intact; the migration does not infer categories from title/body text.
  • Screenshot theme selection uses the existing browser-seeded mode after the panel opens. The local server had 24 GB free, below its free-space reserve, so writing the test User's Appearance preference returned 507. Notification rows and actions still use the built server/API.

Performance

Expected clear/Undo work is O(n) in inbox rows: read and serialize each row and any reminder inverse, batch snapshot writes in groups of 80, then delete or restore rows in one transaction. The profile measured 64 rows for average use and 10,000 rows for the retention limit.

Local production-build server; perf VM was unavailable. There is no matching baseline in docs/perf/baseline.json. Host load average was 33.25, 28.86, 28.47 before and 34.11, 29.71, 28.78 after.

  • 64 rows, 10 rounds: clear p50/p95 37 / 56.8 ms; Undo 27 / 47.8 ms; mean/peak RSS 175.3 / 175.6 MB; mean/peak CPU 19.54 / 176.8%.
  • 10,000 rows: clear 2,275.1 ms; Undo 2,085.9 ms; mean/peak RSS 202.0 / 209.0 MB; mean/peak CPU 64.52 / 394.3%.
  • Four concurrent clears at 10,000 rows: wall 3,084.16 ms; request p50/p95 2,904.9 / 3,039.3 ms; mean/peak RSS 212.8 / 213.3 MB; mean/peak CPU 49.61 / 4,024.42%; all four responses were 200 and one receipt cleared 10,000 rows.

Gate output

cargo fmt --check exited 0 with no output.

cargo clippy -p calternal-plugin-notifications --all-targets -- -D warnings exited 0.
cargo test -p calternal-plugin-notifications:

test result: ok. 42 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 6.99s
Doc-tests calternal_plugin_notifications
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

cargo clippy -p calternal-server --all-targets -- -D warnings exited 0 after the migration fixture update. The focused post-update cargo test -p calternal-server production_ passed:

test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 268 filtered out; finished in 5.88s

The earlier full server test run was before that fixture update and reported:

test result: FAILED. 260 passed; 3 failed; 10 ignored; 0 measured; 0 filtered out; finished in 110.95s

Two failures were stale expectations for the new migrations and pass in the focused run after correction. The remaining failure was the load-sensitive startup_serves_http_while_upgrade_backfills_wait timeout (16.38s); the full server suite was not rerun. Merge round should rerun cargo test -p calternal-server.

Web check output:

perf-lint: PASS; 0 violations; 22352 scoped exceptions
svelte-check found 0 errors and 2 warnings in 2 files

The two warnings are existing empty CSS rulesets in AttachmentDeck.svelte and AgendaList.svelte.

Full web test output:

Test Files 274 passed (274)
Tests 1915 passed (1915)
Duration 483.26s (transform 30%, environment 26%, import 23%, tests 15%, setup 5%)

Production builds succeeded. Screenshot E2E output:

PASS Notifications #1194: 24 screenshots at 390, 820 and 1440 px in light and dark

The initial profile fixture accidentally seeded 20,000 rows in its 10,000-row burst; it was corrected, and the final profile passed. cargo clean completed:

Removed 19545 files, 15.7GiB total

Web build output was removed. Screenshots remain ignored in artifacts/notifications-1194/ and were not committed.

For the merge round

Run tests/adversarial/run.sh to prove the full cross-user, authorization, malformed-input and concurrency matrices, including the new toast category and clear/Undo probes. Run the full web E2E suite and cargo test -p calternal-server; the focused #1194 production screenshot runner already passed all 24 cases.

Production screenshots (macOS emulation; 390/820/1440 px; light and dark):

#1194 complete Branch: `job/notifloop-1194` Head: `bbbaaf3c555ddf853df734d59135ba67df76687a` No push, deploy or merge was performed. ## Built - Added explicit persistence choices for every toast category. Background results and outcomes whose subject left the screen persist; in-place actions default to false, and every `notification.*` category is false. - Notifications-panel delete, clear-all and read-state actions do not toast or write Notifications. Delete and clear use inline five-second Undo rows backed by server receipts. Clear preserves up to 10,000 rows in 80-row database batches in one transaction. - Added roving Arrow-key focus, Enter to open, Delete/Backspace to delete the focused row, focus restoration, and a shortcut hint only while its `notifications` handler scope is active. - Added category-ID migrations and production migration preservation assertions, the mark-unread and clear/Undo routes, OpenAPI/client generation, toast-policy and adversarial coverage, plus the route perf profile. ## Files - Backend: `crates/plugins/notifications/src/routes.rs`, `store.rs`, migrations `0008_toast_category_ids.sql`, `0009_cleanup_notification_management_toasts.sql`, `0010_inbox_clear_undo.sql`; `crates/calternal-server/src/upgrade_tests.rs`. - Notifications UI and tests: `apps/web/src/lib/notifications/{InboxPanel.svelte,inbox.svelte.ts,model.ts,inbox.svelte.test.ts,model.test.ts}`; toast adapter/policy tests, shortcut registry/tests and tooltip tests under `apps/web/src/lib/{stores,shortcuts,tooltip}`. - Shared UI and affected tooltip call sites: `packages/ui/src/components/tooltip/tip.ts` and its callers in app/shared controls; generated API/action/OpenAPI and perf contracts under `packages/api-client/src/generated.ts` and `contracts/`. - Review and verification: `apps/web/e2e/notifications-1194.mjs`, `apps/web/package.json`, `bench/notifications-inbox-1194.mjs`, `tests/adversarial/{run.sh,toast_notifications.mjs}`, `docs/DESIGN.md`. ## UX gaps closed / left Closed: pointer, touch swipe/long-press, and keyboard row actions; accessible row names and read state; page-level context menu; inline delete/clear Undo; keyboard focus restoration; shortcut hints disappear outside the active row scope; real API loading, empty, error and pagination states remain in use. Shared controls used are `OverlaySurface`, `Menu`, `CopyLink`, `Pill`, `PillGroup`, and shared `tip` tooltips. The existing row X action keeps its existing icon-button control and now advertises Delete only in its active keyboard context. Left: deployed legacy `web_toast` rows have no category ID. Migration 0008 tags them `legacy`; migration 0009 deletes only the four exact management category IDs and preserves untagged rows. The old “Notification deleted” rows therefore cannot be removed safely by category without guessing from mutable text. Visual quality remains for the orchestrator to review from the attached production screenshots. ## Decisions where the design was silent - Note and Canvas recovery actions keep their subject on screen, so they use the non-persistent in-place toast policy. - The mutation-receipt GET category is background because only the service worker uses it on notification clicks. - Historic toast rows without category IDs stay intact; the migration does not infer categories from title/body text. - Screenshot theme selection uses the existing browser-seeded mode after the panel opens. The local server had 24 GB free, below its free-space reserve, so writing the test User's Appearance preference returned 507. Notification rows and actions still use the built server/API. ## Performance Expected clear/Undo work is O(n) in inbox rows: read and serialize each row and any reminder inverse, batch snapshot writes in groups of 80, then delete or restore rows in one transaction. The profile measured 64 rows for average use and 10,000 rows for the retention limit. Local production-build server; perf VM was unavailable. There is no matching baseline in `docs/perf/baseline.json`. Host load average was `33.25, 28.86, 28.47` before and `34.11, 29.71, 28.78` after. - 64 rows, 10 rounds: clear p50/p95 `37 / 56.8 ms`; Undo `27 / 47.8 ms`; mean/peak RSS `175.3 / 175.6 MB`; mean/peak CPU `19.54 / 176.8%`. - 10,000 rows: clear `2,275.1 ms`; Undo `2,085.9 ms`; mean/peak RSS `202.0 / 209.0 MB`; mean/peak CPU `64.52 / 394.3%`. - Four concurrent clears at 10,000 rows: wall `3,084.16 ms`; request p50/p95 `2,904.9 / 3,039.3 ms`; mean/peak RSS `212.8 / 213.3 MB`; mean/peak CPU `49.61 / 4,024.42%`; all four responses were 200 and one receipt cleared 10,000 rows. ## Gate output `cargo fmt --check` exited 0 with no output. `cargo clippy -p calternal-plugin-notifications --all-targets -- -D warnings` exited 0. `cargo test -p calternal-plugin-notifications`: ```text test result: ok. 42 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 6.99s Doc-tests calternal_plugin_notifications running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s ``` `cargo clippy -p calternal-server --all-targets -- -D warnings` exited 0 after the migration fixture update. The focused post-update `cargo test -p calternal-server production_` passed: ```text test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 268 filtered out; finished in 5.88s ``` The earlier full server test run was before that fixture update and reported: ```text test result: FAILED. 260 passed; 3 failed; 10 ignored; 0 measured; 0 filtered out; finished in 110.95s ``` Two failures were stale expectations for the new migrations and pass in the focused run after correction. The remaining failure was the load-sensitive `startup_serves_http_while_upgrade_backfills_wait` timeout (`16.38s`); the full server suite was not rerun. Merge round should rerun `cargo test -p calternal-server`. Web check output: ```text perf-lint: PASS; 0 violations; 22352 scoped exceptions svelte-check found 0 errors and 2 warnings in 2 files ``` The two warnings are existing empty CSS rulesets in `AttachmentDeck.svelte` and `AgendaList.svelte`. Full web test output: ```text Test Files 274 passed (274) Tests 1915 passed (1915) Duration 483.26s (transform 30%, environment 26%, import 23%, tests 15%, setup 5%) ``` Production builds succeeded. Screenshot E2E output: ```text PASS Notifications #1194: 24 screenshots at 390, 820 and 1440 px in light and dark ``` The initial profile fixture accidentally seeded 20,000 rows in its 10,000-row burst; it was corrected, and the final profile passed. `cargo clean` completed: ```text Removed 19545 files, 15.7GiB total ``` Web build output was removed. Screenshots remain ignored in `artifacts/notifications-1194/` and were not committed. ## For the merge round Run `tests/adversarial/run.sh` to prove the full cross-user, authorization, malformed-input and concurrency matrices, including the new toast category and clear/Undo probes. Run the full web E2E suite and `cargo test -p calternal-server`; the focused #1194 production screenshot runner already passed all 24 cases. Production screenshots (macOS emulation; 390/820/1440 px; light and dark): - [clear-inline-1440-paper.png](<https://git.kayg.org/attachments/605c8917-7009-4ed3-8374-7605e5b72ff9>) - [clear-inline-1440-tokyo-night.png](<https://git.kayg.org/attachments/fff559f0-6bcc-4c10-9099-55ba5aaab9aa>) - [clear-inline-390-paper.png](<https://git.kayg.org/attachments/a255625d-943d-4c24-b069-581dd6e2eff3>) - [clear-inline-390-tokyo-night.png](<https://git.kayg.org/attachments/3dddd8b0-0b5d-4fdd-8b3e-9cd184e3f53e>) - [clear-inline-820-paper.png](<https://git.kayg.org/attachments/adc875eb-2153-48fd-84e0-038928f50878>) - [clear-inline-820-tokyo-night.png](<https://git.kayg.org/attachments/0aeddb1b-526f-4b2d-aa1f-3754df1f2263>) - [clear-undo-1440-paper.png](<https://git.kayg.org/attachments/d316e98d-92f7-44c3-ae33-b17332fa8f33>) - [clear-undo-1440-tokyo-night.png](<https://git.kayg.org/attachments/5b469d10-3e26-45d6-b776-870663a49fa2>) - [clear-undo-390-paper.png](<https://git.kayg.org/attachments/942ff2d8-3810-46ca-8c3c-a385a8f0857d>) - [clear-undo-390-tokyo-night.png](<https://git.kayg.org/attachments/75c71fbb-e137-495e-9b1c-aab1c0a7aba4>) - [clear-undo-820-paper.png](<https://git.kayg.org/attachments/4276f1ff-f347-4294-bbcc-7f34e91413cc>) - [clear-undo-820-tokyo-night.png](<https://git.kayg.org/attachments/4da00635-ed3c-4e84-b41e-c9b9e02cfbfa>) - [delete-inline-1440-paper.png](<https://git.kayg.org/attachments/a0f8835e-dcd9-48e4-8673-b6b6168d055a>) - [delete-inline-1440-tokyo-night.png](<https://git.kayg.org/attachments/e1a75999-2a71-4784-b454-9768b4f01b18>) - [delete-inline-390-paper.png](<https://git.kayg.org/attachments/05478dd1-8bc8-4342-b488-59b6ed12f689>) - [delete-inline-390-tokyo-night.png](<https://git.kayg.org/attachments/29c6b074-3852-46cd-b333-fcd975331a18>) - [delete-inline-820-paper.png](<https://git.kayg.org/attachments/f21a9f1d-1c4a-4585-97b3-1113d794072b>) - [delete-inline-820-tokyo-night.png](<https://git.kayg.org/attachments/c874af28-4997-4b90-b342-a071e900cd52>) - [delete-undo-1440-paper.png](<https://git.kayg.org/attachments/5aa53956-b905-4639-9d55-e928c16a9fcb>) - [delete-undo-1440-tokyo-night.png](<https://git.kayg.org/attachments/6936505d-59ea-48c0-8180-a60013acdf50>) - [delete-undo-390-paper.png](<https://git.kayg.org/attachments/55e882cf-2811-405a-af49-aa6255b5ca6e>) - [delete-undo-390-tokyo-night.png](<https://git.kayg.org/attachments/17900d85-619b-46ff-8064-b469db247d94>) - [delete-undo-820-paper.png](<https://git.kayg.org/attachments/a47cfdc4-f465-4757-90a6-a1807a38c0cb>) - [delete-undo-820-tokyo-night.png](<https://git.kayg.org/attachments/59492fab-9846-47a0-9c1e-67dd56318d04>)
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/calternal#1194
No description provided.