Files
headquarter/openspec/changes/notification-center/apply-progress.md
T
alex 6085859874 feat: notification center backend integration (PR-2)
- Wire lifecycle_hooks.py to NotificationService after event bus publish
- Wire health_monitor.py to NotificationService after state changes
- Category/severity mapping: instance.* → info, error → error, unhealthy → warning
- Extend UserConfig API with notification_mute_categories and notification_toast_level
- 6 integration tests for event-to-notification flow
- All producer calls wrapped in try/except — failures logged, pipeline continues

Quality gates: pytest 41 passed (monitoring + lifecycle), ruff clean
2026-05-29 12:40:15 +02:00

8.9 KiB

Apply Progress: Notification Center

TDD Cycle Evidence (PR-1)

Cycle Task Test File RED GREEN Evidence
1 NC-PR1-004 (basic CRUD) tests/unit/test_notification_service.py 13 tests written against missing service All 13 pass pytest tests/unit/test_notification_service.py → 13 passed
2 NC-PR1-005 (edge cases) tests/unit/test_notification_service.py Already included in cycle 1 Added wrong-owner, mute-categories, cross-user isolation Same 13 tests pass
3 NC-PR1-007 (basic endpoints) tests/integration/test_notifications_api.py 10 tests written against missing router All 10 pass pytest tests/integration/test_notifications_api.py → 10 passed
4 NC-PR1-008 (API edge cases) tests/integration/test_notifications_api.py Already included in cycle 3 Pagination, 404 ownership, mute categories at API layer Same 10 tests pass
5 NC-PR1-010 (REFACTOR) All files ruff clean, no regressions ruff check passes on all new files; existing unit tests 223 passed (4 pre-existing failures unrelated)

TDD Cycle Evidence (PR-2)

Cycle Task Test File RED GREEN Evidence
1 NC-PR2-004 (producer flow) tests/integration/test_notifications_lifecycle.py 4 tests written against unwired producers Wired lifecycle_hooks.py and health_monitor.py 4 passed
2 NC-PR2-004 (severity mapping) tests/integration/test_notifications_lifecycle.py Added error + warning severity tests Already green from implementation 6 passed
3 NC-PR2-003 (UserConfig schema) src/api/user_config.py Schema extended with new optional fields PATCH/GET endpoints validate correctly Verified manually
4 NC-PR2-005 (REFACTOR) All files ruff clean, no regressions across 41 related tests All pass

Completed Tasks

PR-1: Backend Core

  • NC-PR1-001: Alembic migration for notifications table
  • NC-PR1-002: SQLAlchemy Notification model (apps/api/src/models/notification.py)
  • NC-PR1-003: Export Notification in models/__init__.py
  • NC-PR1-004: NotificationService unit tests — basic CRUD (RED)
  • NC-PR1-005: Implement NotificationService singleton (GREEN)
  • NC-PR1-006: Service edge-case and isolation tests (TRIANGULATE)
  • NC-PR1-007: API integration tests — basic endpoints (RED)
  • NC-PR1-008: Implement FastAPI router + Pydantic schemas (GREEN)
  • NC-PR1-009: API edge-case and ownership tests (TRIANGULATE)
  • NC-PR1-010: Register router in main.py + import Notification for Alembic
  • NC-PR1-011: Code quality pass — ruff, test regressions, smoke tests (REFACTOR)

PR-2: Backend Integration

  • NC-PR2-001: Wire lifecycle_hooks.py to NotificationService
  • NC-PR2-002: Wire health_monitor.py to NotificationService
  • NC-PR2-003: Extend UserConfig schema for notification preferences
  • NC-PR2-004: Event producer integration tests (RED)
  • NC-PR2-005: Verify producer tests pass and clean up (GREEN / REFACTOR)

Files Changed

PR-1 Files

  1. apps/api/alembic/versions/2026_05_29_add_notifications_table.py (new) — Alembic migration
  2. apps/api/src/models/notification.py (new) — SQLAlchemy model
  3. apps/api/src/models/__init__.py — Export Notification
  4. apps/api/src/services/notification_service.py (new)NotificationService singleton
  5. apps/api/src/api/notifications.py (new) — FastAPI router + Pydantic schemas
  6. apps/api/src/api/__init__.py — Export notifications_router
  7. apps/api/src/main.py — Register router, import Notification for Alembic
  8. apps/api/tests/unit/test_notification_service.py (new) — 13 unit tests
  9. apps/api/tests/integration/test_notifications_api.py (new) — 10 integration tests
  10. apps/api/tests/integration/test_models.py — Updated expected tables list

PR-2 Files

  1. apps/api/src/services/lifecycle_hooks.py — Wired NotificationService after event bus publish
  2. apps/api/src/services/health_monitor.py — Wired NotificationService after state change event publish
  3. apps/api/src/api/user_config.py — Added notification_mute_categories and notification_toast_level to Pydantic schemas
  4. apps/api/tests/integration/test_notifications_lifecycle.py (new) — 6 integration tests for event-to-notification flow

Test Commands & Exit Codes

PR-1

# Unit tests for NotificationService (13 tests)
cd apps/api && python -m pytest tests/unit/test_notification_service.py -v
# Exit: 0 — 13 passed

# Integration tests for notifications API (10 tests)
cd apps/api && python -m pytest tests/integration/test_notifications_api.py -v
# Exit: 0 — 10 passed

# Existing unit tests (no regressions in our code)
cd apps/api && python -m pytest tests/unit/ -v
# Exit: 1 — 223 passed, 4 failed (pre-existing failures in test_config.py and test_git_repository_clone_preflight.py)

PR-2

# New integration tests for event producers (6 tests)
cd apps/api && python -m pytest tests/integration/test_notifications_lifecycle.py -v
# Exit: 0 — 6 passed

# NotificationService unit tests (no regressions)
cd apps/api && python -m pytest tests/unit/test_notification_service.py -v
# Exit: 0 — 13 passed

# Notifications API integration tests (no regressions)
cd apps/api && python -m pytest tests/integration/test_notifications_api.py -v
# Exit: 0 — 10 passed

# Health monitor unit tests (no regressions)
cd apps/api && python -m pytest tests/unit/test_health_monitor.py -v
# Exit: 0 — 6 passed

# Event integration tests (no regressions)
cd apps/api && python -m pytest tests/integration/test_events.py -v
# Exit: 0 — 6 passed

# Combined relevant test suite (41 tests)
cd apps/api && python -m pytest \
  tests/unit/test_notification_service.py \
  tests/integration/test_notifications_api.py \
  tests/integration/test_notifications_lifecycle.py \
  tests/unit/test_health_monitor.py \
  tests/integration/test_events.py \
  -v
# Exit: 0 — 41 passed

# Ruff linting on all PR-2 modified files
cd apps/api && python -m ruff check \
  src/services/lifecycle_hooks.py \
  src/services/health_monitor.py \
  src/api/user_config.py \
  tests/integration/test_notifications_lifecycle.py
# Exit: 0 — All checks passed

Deviations from Design

PR-1

  • SQLAlchemy metadata column name conflict: Base.metadata is reserved by SQLAlchemy DeclarativeBase. Used notification_metadata as the Python attribute name with DB column name "metadata". In the Pydantic response model, used Field(serialization_alias="metadata") so the JSON API still exposes metadata as specified.
  • created_at type in Pydantic: Used datetime instead of str to leverage FastAPI's automatic ISO serialization.

PR-2

  • None. All mappings and behaviors match the design spec (section 1.3) and task requirements exactly.

Surprises / Decisions

PR-1

  1. SQLite func.now() resolution: test_list_notifications_orders_by_created_at_desc failed because multiple rapid INSERTs got identical timestamps. Fixed by explicitly setting created_at offsets in the test after creation.
  2. Pre-existing integration test failures: ~40 integration tests fail due to missing asyncpg module and direct PostgreSQL connection attempts in their custom setup code. These are unrelated to our changes.
  3. Pre-existing test_models.py outdated: The test_expected_tables_are_registered assertion had a hardcoded set missing many newer tables. Updated it to include all current tables (including notifications).

PR-2

  1. Health monitor test_health_monitor_unhealthy_creates_warning_notification required public_url: The health monitor only checks tunnel health when instance.public_url is truthy. Without setting it on the test fixture instance, _derive_status returned "running" instead of "unhealthy", which created an "info" notification. Fixed by setting test_instance.public_url in the test before calling _check_instance.
  2. Patch target for failure test: The test_notification_failure_does_not_block_event_pipeline patches src.services.lifecycle_hooks.notification_service.create_notification. This only works because lifecycle_hooks.py imports notification_service at module level, making the attribute resolvable by unittest.mock.patch.
  3. No schema migration needed for UserConfig: Preferences are stored in the existing JSON config blob, consistent with the existing pattern (theme, editor, git identity). No Alembic migration required.

Remaining Tasks

  • PR-3: Frontend Core (NC-PR3-001 through NC-PR3-012)
  • PR-4: Toast Coordination (NC-PR4-001 through NC-PR4-006)

PR Boundary

This progress covers PR-1 and PR-2. PR-3 (frontend core — NotificationProvider, useNotifications, NotificationCenter, NotificationItem, styles, AppShell integration) and PR-4 (toast coordination — EventToastBridge preferences, settings UI) are out of scope.