Channel deletion cascades away sent_alerts history that the DB was designed to keep #48

Closed
opened 2026-07-18 05:31:15 +00:00 by claude-bot · 1 comment
Contributor

NotificationChannel.sent_alerts declares cascade="all, delete-orphan"
(app/models/channel.py:41-43) while the FK is ondelete="SET NULL"
(app/models/alert.py:20-22, migration 0001:72) — the documented design is that
channel deletion leaves history as dashboard-only rows. The ORM cascade wins on
await db.delete(ch) (app/api/channels.py:143) and hard-deletes every
associated SentAlert. Remove the delete cascade and add
passive_deletes=True so the DB behavior applies; add a regression test that
deletes a channel and asserts its SentAlert rows persist with
channel_id IS NULL. Note the SQLite test double doesn't enforce FK behavior
identically — assert via ORM state, or cover in the Postgres tier (F-29).

Acceptance criteria:

  • Deleting a channel preserves its sent_alerts rows with channel_id NULL
  • Regression test added

Filed from the 2026-07-17 codebase audit (docs/.internal/report-2026-07-17.md), finding F-09.

`NotificationChannel.sent_alerts` declares `cascade="all, delete-orphan"` (app/models/channel.py:41-43) while the FK is `ondelete="SET NULL"` (app/models/alert.py:20-22, migration 0001:72) — the documented design is that channel deletion leaves history as dashboard-only rows. The ORM cascade wins on `await db.delete(ch)` (app/api/channels.py:143) and hard-deletes every associated `SentAlert`. Remove the delete cascade and add `passive_deletes=True` so the DB behavior applies; add a regression test that deletes a channel and asserts its `SentAlert` rows persist with `channel_id IS NULL`. Note the SQLite test double doesn't enforce FK behavior identically — assert via ORM state, or cover in the Postgres tier (F-29). **Acceptance criteria:** - [ ] Deleting a channel preserves its sent_alerts rows with channel_id NULL - [ ] Regression test added --- _Filed from the 2026-07-17 codebase audit (`docs/.internal/report-2026-07-17.md`), finding F-09._
Author
Contributor

Fixed in #92 (merged to main). Dropped cascade="all, delete-orphan" on NotificationChannel.sent_alerts and set passive_deletes=True, so channel deletion defers to the DB's ON DELETE SET NULL and preserves history as dashboard-only rows. Regression test asserts the rows are not cascade-deleted (full channel_id IS NULL semantics are authoritative on the Postgres tier — SQLite doesn't enforce FKs here).

Fixed in #92 (merged to `main`). Dropped `cascade="all, delete-orphan"` on `NotificationChannel.sent_alerts` and set `passive_deletes=True`, so channel deletion defers to the DB's `ON DELETE SET NULL` and preserves history as dashboard-only rows. Regression test asserts the rows are not cascade-deleted (full `channel_id IS NULL` semantics are authoritative on the Postgres tier — SQLite doesn't enforce FKs here).
Sign in to join this conversation.
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
rbrooks/WeatherBot#48
No description provided.