Skip to content

[2C-02]: Add scheduled_date column to notification_queue and populate on creation - #209

Merged
WilliM233 merged 1 commit into
developfrom
feat/2C-02-scheduled-date-column
Apr 27, 2026
Merged

WilliM233 merged 1 commit into
developfrom
feat/2C-02-scheduled-date-column

Conversation

@WilliM233

Copy link
Copy Markdown
Owner

Verification

  • ruff check . — ✅ Pass (All checks passed!)
  • pytest -v — ✅ Pass (1358 passed)
  • Migration applied locally on brain3-dev: ✅ Yes
  • Postgres-backed test confirmed: ✅ Yes (tests/test_notifications_schema_022.py — 3 passed against migrated brain3-dev DB)

Ran locally against develop HEAD at 5cd3b80 immediately before opening this PR.

Summary

Implements [2C-02]. Adds scheduled_date (calendar-day anchor) to notification_queue so the companion app's recent view can filter by device-local date without the server needing to know client TZ — resolves Stream C v0.9 Q8 / E5. scheduled_at (tz-aware DateTime) remains the source of truth for when the notification fires; scheduled_date records the calendar day the notification is for.

Changes

  • alembic/versions/022_add_scheduled_date_to_notifications.py — new migration. Adds the column nullable, backfills existing rows from scheduled_at::date (Postgres casts a timestamptz to date using the session's TimeZone setting, which BRAIN runs in UTC — matches the in-app default), then alters NOT NULL and creates ix_nq_scheduled_date. Two-step nullable→backfill→NOT NULL pattern keeps the upgrade safe whether the table is populated at upgrade time or not.
  • app/models.py — NotificationQueue.scheduled_date: Mapped[date] (NOT NULL) + matching Index("ix_nq_scheduled_date", "scheduled_date") in __table_args__.
  • app/schemas/notifications.py — NotificationCreate.scheduled_date: date (required) and NotificationResponse.scheduled_date: date. NotificationUpdate left untouched per spec (no allowance for changing the calendar-day anchor after creation).
  • app/routers/notification.py — list endpoint accepts scheduled_date: date query param and filters with exact equality.
  • app/services/rule_evaluation.py — _create_notification now populates scheduled_date=now.date() with the server-TZ assumption documented inline. A future SERVER_TZ (introduced by [2C-04]) will swap that default if BRAIN ever runs in a non-UTC timezone.
  • Tests — six existing test fixtures that build NotificationQueue(...) directly updated to provide scheduled_date. Three API helpers (test_notification_crud, test_notification_defaults, test_notification_respond, test_notification_expiry) now post scheduled_date in the create payload. New cases in test_notification_crud.py: missing-field 422, malformed-date 422, response payload echo, ?scheduled_date= filter. New file tests/test_notifications_schema_022.py runs the migration against a real Postgres (brain3-dev pattern from test_rules_postgres.py) and asserts the scheduled_at::date backfill, the post-migration NOT NULL constraint, and the index presence.

How to Verify

  1. git checkout feat/2C-02-scheduled-date-column
  2. With brain3-dev Postgres running: pytest -v — 1358 pass; the three Postgres-backed migration tests in tests/test_notifications_schema_022.py exercise the backfill end-to-end.
  3. Without Postgres reachable: pytest -v — schema-022 tests skip with the same reason as the existing Postgres-backed suite, the rest still pass.
  4. ruff check . — clean.
  5. Manual contract check: POST /api/notifications/ without scheduled_date → 422; with valid scheduled_date → 201 and the response body includes the field; GET /api/notifications/?scheduled_date=2026-04-15 returns only same-day rows.

Deviations

  • Migration number renamed from 021 to 022. Spec says the new migration is 021_add_scheduled_date_to_notifications.py because it was authored against a tree where 020 was the head. Between spec freeze and this dispatch, [2C-26] shipped 021_add_routine_completions_unique_constraint.py (revision 21a1b2c3d4e5). I bumped this ticket's migration to 022_add_scheduled_date_to_notifications.py (revision 22a1b2c3d4e5, down_revision='21a1b2c3d4e5') and the new test file to tests/test_notifications_schema_022.py. Spec intent (next sequential after current head) is preserved.
  • Org CLAUDE.md drift. The repo working copy at D:\_DEVELOPMENT\CLAUDE.md is older than BRAIN org CLAUDE.md v5 (April 27, 2026) — it lacks the inheritance-and-source-of-truth section. BRAIN won for this session per directive 4ea28266 (CLAUDE.md is PO-controlled). Flagging only — not modifying the file.
  • CHANGELOG.md not updated. Per org/repo CLAUDE.md, every ticket should update CHANGELOG. The CHANGELOG has not been touched since the v1.2.0 docs sync; neither [2C-26] nor [2C-01] added entries (the working pattern appears to be batched per-release, not per-ticket). Following the de-facto pattern here. If L wants per-ticket CHANGELOG updates resumed, that's a separate observation worth a follow-up issue rather than a unilateral fix in this PR.

Test Results

ruff check .
All checks passed!

pytest -v
============================ 1358 passed in 22.17s ============================

Highlights of new coverage:

  • tests/test_notification_crud.py::TestCreateNotification::test_create_missing_scheduled_date_rejected — 422
  • tests/test_notification_crud.py::TestCreateNotification::test_create_response_includes_scheduled_date — payload echo
  • tests/test_notification_crud.py::TestCreateNotification::test_create_invalid_scheduled_date_format_rejected — 422 on malformed date
  • tests/test_notification_crud.py::TestListNotifications::test_filter_by_scheduled_date — exact-day filter
  • tests/test_notifications_schema_022.py::TestMigration022Backfill::test_backfill_populates_scheduled_date_from_scheduled_at — Postgres backfill
  • tests/test_notifications_schema_022.py::TestMigration022Backfill::test_post_migration_insert_without_scheduled_date_fails — NOT NULL enforced
  • tests/test_notifications_schema_022.py::TestMigration022Backfill::test_index_on_scheduled_date_exists — index present

Acceptance Checklist

  • Migration applies cleanly on a populated test DB (covered by test_backfill_populates_scheduled_date_from_scheduled_at against brain3-dev).
  • POST /api/notifications/ without scheduled_date → 422 with a clear validation error.
  • POST /api/notifications/ with a valid scheduled_date → 201, response body includes the field.
  • Backfill query yields expected values for existing rows (Postgres-backed migration test seeds two rows with morning/evening UTC timestamps and asserts the resulting dates).
  • All existing notification tests still pass (fixture payloads carry scheduled_date in the same commit).
  • GET /api/notifications/?scheduled_date=YYYY-MM-DD returns only notifications with that calendar-date match.

Closes #201

…ar filtering (#201)

Implements [2C-02]. ``scheduled_date`` is a calendar-day anchor on
``notification_queue`` denormalized from ``scheduled_at``: the companion
app's recent view filters by device-local date without the server
needing to know client TZ (Stream C v0.9 Q8 / E5).

Migration 022 adds the column nullable, backfills existing rows from
``scheduled_at::date`` (Postgres uses session ``TimeZone``, BRAIN runs
UTC), then alters NOT NULL and adds index ``ix_nq_scheduled_date``.
``NotificationCreate`` requires ``scheduled_date``; ``NotificationResponse``
echoes it; the list endpoint accepts ``?scheduled_date=YYYY-MM-DD`` for
exact-day filtering. Internal callers in ``rule_evaluation`` derive the
default from ``now.date()`` with the server-TZ assumption documented
inline (a future SERVER_TZ from [2C-04] will swap that default if
BRAIN ever runs non-UTC).

Test coverage: schema-level 422 on missing/malformed ``scheduled_date``,
response payload echo, ``?scheduled_date`` filter on the list endpoint,
and Postgres-backed migration tests in
``tests/test_notifications_schema_022.py`` covering the
``scheduled_at::date`` backfill, the post-migration NOT NULL constraint,
and ``ix_nq_scheduled_date`` presence.
@WilliM233
WilliM233 merged commit 2ec8521 into develop Apr 27, 2026
2 checks passed
@WilliM233
WilliM233 deleted the feat/2C-02-scheduled-date-column branch April 27, 2026 17:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[2C-02] Add scheduled_date column to notification_queue and populate on creation

1 participant