diff --git a/.cursor/rules/docs/documentation_references_model_agents.mdc b/.cursor/rules/docs/documentation_references_model_agents.mdc new file mode 100644 index 00000000..8ea812b3 --- /dev/null +++ b/.cursor/rules/docs/documentation_references_model_agents.mdc @@ -0,0 +1,87 @@ +--- +title: Documentation, References, and Model Agent Use +version: 1.1 +alwaysApply: true +scope: code, project-plans +--- + +# Directive on Documentation, References, and Model Agent Use in Code and Project Plans + +To ensure clarity, efficiency, and high-value documentation within code and project plans—and to leverage **model agents** (AI- or automation-based assistants) effectively—contributors must follow these rules: + +--- + +## 1. Documentation and References Must Add Clear Value +- Only include documentation, comments, or reference links when they provide *new, meaningful information* that assists understanding or decision-making. +- Avoid duplicating content already obvious in the codebase, version history, or linked project documents. + +--- + +## 2. Eliminate Redundant or Noisy References +- Remove references that serve no purpose beyond filling space. +- Model agents may automatically flag and suggest removal of trivial references (e.g., links to unchanged boilerplate or self-evident context). + +--- + +## 3. Explicit Role of Model Agents +Model agents are **active participants** in documentation quality control. Their tasks include: +- **Relevance Evaluation**: Automatically analyze references for their substantive contribution before inclusion. +- **Redundancy Detection**: Flag duplicate or trivial references across commits, files, or tasks. +- **Context Linking**: Suggest appropriate higher-level docs (designs, ADRs, meeting notes) when a code change touches multi-stage or cross-team items. +- **Placement Optimization**: Recommend centralization of references (e.g., in plan overviews, ADRs, or merge commit messages) rather than scattered low-value inline references. +- **Consistency Monitoring**: Ensure references align with team standards (e.g., ADR template, architecture repo, or external policy documents). + +Contributors must treat agent recommendations as **first-pass reviews** but remain accountable for final human judgment. + +--- + +## 4. Contextual References for Complex Items +- Use **centralized references** for multi-stage features (e.g., architectural docs, research threads). +- Keep inline code comments light; push broader context into centralized documents. +- Model agents may auto-summarize complex chains of discussion and attach them as a single reference point. + +--- + +## 5. Centralization of Broader Context +- Store overarching context (design docs, proposals, workflows) in accessible, well-indexed places. +- Model agents should assist by **generating reference maps** that track where docs are cited across the codebase. + +--- + +## 6. Focused Documentation +- Documentation should explain **why** and **how** decisions are made, not just what was changed. +- Model agents can auto-generate first-pass explanations from commit metadata, diffs, and linked issues—but humans must refine them for accuracy and intent. + +--- + +## 7. Review and Accountability +- Reviewers and team leads must reject submissions containing unnecessary or low-quality documentation. +- Model agent outputs are aids, not replacements—contributors remain responsible for **final clarity and relevance**. + +--- + +## 8. Continuous Improvement and Agent Feedback Loops +- Encourage iterative development of model agents so their evaluations become more precise over time. +- Contributions should include **feedback on agent suggestions** (e.g., accepted, rejected, or corrected) to train better future outputs. +- Agents should log patterns of “rejected” suggestions for refinement. + +--- + +## 9. Workflow Overview (Mermaid Diagram) + +```mermaid +flowchart TD + A[Contributor] -->|Writes Code & Draft Docs| B[Model Agent] + B -->|Evaluates References| C{Relevant?} + C -->|Yes| D[Suggest Placement & Context Links] + C -->|No| E[Flag Redundancy / Noise] + D --> F[Contributor Refines Docs] + E --> F + F --> G[Reviewer] + G -->|Approves / Requests Revisions| H[Final Documentation] + G -->|Feedback on Agent Suggestions| B +``` + +--- + +✅ **Outcome:** By integrating disciplined contributor standards with **model agent augmentation**, the team achieves documentation that is consistently *relevant, concise, centralized, and decision-focused*. AI ensures coverage and noise reduction, while humans ensure precision and judgment. diff --git a/doc/notification-system-executive-summary.md b/doc/notification-system-executive-summary.md index 9d7a7864..bb309760 100644 --- a/doc/notification-system-executive-summary.md +++ b/doc/notification-system-executive-summary.md @@ -1,24 +1,20 @@ # TimeSafari Notification System — Executive Summary -**Status:** 🚀 Ready for Implementation -**Date:** 2025-01-27T14:30Z (UTC) -**Author:** Matthew Raymer +**Status:** 🚀 Ready for Implementation +**Date:** 2025-01-27T14:30Z (UTC) +**Author:** Matthew Raymer **Audience:** Executive Leadership, Product Management, Engineering Leadership --- ## Executive Overview -TimeSafari will implement a **multi-platform notification system** that -delivers **1-3 daily notifications** to keep users connected to gratitude, gifts, - and collaborative projects. The system will work across **iOS, Android, Web, - and Electron** platforms with **offline-first reliability** and - **privacy-preserving architecture**. +TimeSafari will implement a **multi-platform notification system** that delivers **1-3 daily notifications** to keep users connected to gratitude, gifts, and collaborative projects. The system will work across **iOS, Android, Web, and Electron** platforms with **offline-first reliability** and **privacy-preserving architecture**. ### Business Value - **User Engagement:** Daily touchpoints to maintain community connections -- **Platform Coverage:** Unified experience across all TimeSafari platforms +- **Platform Coverage:** Unified experience across all TimeSafari platforms - **Privacy-First:** User-controlled data with no external tracking - **Reliability:** Offline notifications that work even when app is closed @@ -31,7 +27,6 @@ delivers **1-3 daily notifications** to keep users connected to gratitude, gifts **Scope:** Multi-daily local notifications with online/offline flows **Key Capabilities:** - - **Local Notifications:** OS-level delivery on mobile/desktop - **Web Push:** Service Worker-based notifications for web - **Offline Reliability:** Notifications fire even when app is closed @@ -40,11 +35,10 @@ delivers **1-3 daily notifications** to keep users connected to gratitude, gifts ### Phase 2 (v2): Native Plugin -**Timeline:** Future enhancement +**Timeline:** Future enhancement **Scope:** Native background scheduling and enhanced capabilities **Key Capabilities:** - - **Native Background Work:** OS-level background tasks - **Enhanced Scheduling:** More precise timing and reliability - **Advanced Features:** Rich media and complex actions @@ -118,14 +112,9 @@ delivers **1-3 daily notifications** to keep users connected to gratitude, gifts ## Document References -- **Strategic Plan:** `notification-system-plan.md` - Goals, tenets, platform -behaviors, acceptance criteria -- **Implementation Guide:** `notification-system-implementation.md` - Complete -code, database schemas, integration specifics -- **This Summary:** High-level overview for executive decision-making +- **Strategic Plan:** `notification-system-plan.md` - Goals, tenets, platform behaviors, acceptance criteria +- **Implementation Guide:** `notification-system-implementation.md` - Complete code, database schemas, integration specifics --- -*This executive summary provides the essential business context and strategic -direction for TimeSafari's notification system. For detailed technical -specifications and implementation guidance, refer to the referenced documents.* +*This executive summary provides the essential business context and strategic direction for TimeSafari's notification system. For detailed technical specifications and implementation guidance, refer to the referenced documents.* \ No newline at end of file diff --git a/doc/notification-system-implementation.md b/doc/notification-system-implementation.md index 3ace2fa1..d7cf55cb 100644 --- a/doc/notification-system-implementation.md +++ b/doc/notification-system-implementation.md @@ -1,16 +1,13 @@ # TimeSafari Notification System — Implementation Guide -**Status:** 🚀 Active implementation -**Date:** 2025-09-05T05:09Z (UTC) -**Author:** Matthew Raymer -**Scope:** Detailed implementation for v1 (in‑app orchestrator) -**Goal:** Complete implementation guide with code, database schemas, and -integration specifics. - -> **Strategic Overview:** See `notification-system-plan.md` for high-level -> strategy, architecture, and planning details. -> **Canonical Ownership:** This document owns API definitions, Database -> schemas, Adapter implementations, and Code examples. +**Status:** 🚀 Active implementation +**Date:** 2025-09-05T05:09Z (UTC) +**Author:** Matthew Raymer +**Scope:** Detailed implementation for v1 (in‑app orchestrator) +**Goal:** Complete implementation guide with code, database schemas, and integration specifics. + +> **Strategic Overview:** See `notification-system-plan.md` for high-level strategy, architecture, and planning details. +> **Canonical Ownership:** This document owns API definitions, Database schemas, Adapter implementations, and Code examples. --- diff --git a/doc/notification-system-plan.md b/doc/notification-system-plan.md index 5d5bf3b9..d1743331 100644 --- a/doc/notification-system-plan.md +++ b/doc/notification-system-plan.md @@ -1,55 +1,34 @@ # TimeSafari Notification System — Strategic Plan -**Status:** 🚀 Active plan -**Date:** 2025-09-05T05:09Z (UTC) -**Author:** Matthew Raymer -**Scope:** v1 (in‑app orchestrator) now; path to v2 (native plugin) next -**Goal:** We **will deliver** 1..M local notifications/day with content -**prefetched** so messages **will display offline**. We **will support** -online‑first (API→DB→Schedule) with offline‑first fallback. The system -**will enhance** TimeSafari's community-building mission by keeping users -connected to gratitude, gifts, and collaborative projects through timely, -relevant notifications. - -> **Implementation Details:** See `notification-system-implementation.md` for - detailed code, database schemas, and integration specifics. -> **Canonical Ownership:** This document owns Goals, Tenets, Platform -> behaviors, Acceptance criteria, and Test cases. +**Status:** 🚀 Active plan +**Date:** 2025-09-05T05:09Z (UTC) +**Author:** Matthew Raymer +**Scope:** v1 (in‑app orchestrator) now; path to v2 (native plugin) next +**Goal:** We **will deliver** 1..M local notifications/day with content **prefetched** so messages **will display offline**. We **will support** online‑first (API→DB→Schedule) with offline‑first fallback. The system **will enhance** TimeSafari's community-building mission by keeping users connected to gratitude, gifts, and collaborative projects through timely, relevant notifications. + +> **Implementation Details:** See `notification-system-implementation.md` for detailed code, database schemas, and integration specifics. +> **Canonical Ownership:** This document owns Goals, Tenets, Platform behaviors, Acceptance criteria, and Test cases. --- ## 1) Versioning & Intent -- **v1 (In‑App Orchestrator):** We **will implement** multi‑daily local - notifications, online/offline flows, templating, SQLite persistence, and - eventing **inside the app** using Capacitor Local Notifications. -- **v2 (Plugin):** We **will extract** adapters to a Capacitor/Native plugin - to gain native schedulers (WorkManager/AlarmManager; - BGTask+UNUserNotificationCenter), native HTTP, and native SQLite **with the - same TypeScript API**. +- **v1 (In‑App Orchestrator):** We **will implement** multi‑daily local notifications, online/offline flows, templating, SQLite persistence, and eventing **inside the app** using Capacitor Local Notifications. +- **v2 (Plugin):** We **will extract** adapters to a Capacitor/Native plugin to gain native schedulers (WorkManager/AlarmManager; BGTask+UNUserNotificationCenter), native HTTP, and native SQLite **with the same TypeScript API**. -> We **will retain** the existing web push + Service Worker foundation; the -> system **will add** reliable local scheduling on mobile and a unified API -> across platforms. +> We **will retain** the existing web push + Service Worker foundation; the system **will add** reliable local scheduling on mobile and a unified API across platforms. --- ## 2) Design Tenets -- **Reliability:** OS‑level delivery once scheduled; no reliance on JS being alive -at fire time. -- **Freshness:** Prefer online‑first within a short prefetch window; degrade -gracefully to cached content with TTL. -- **Extractable:** Clean interfaces (Scheduler, DataStore, Callbacks) so v2 -**will swap** adapters without API changes. -- **Simplicity:** One‑shot notifications per slot; rolling window scheduling to -respect platform caps. -- **Observability:** Persist deliveries and errors; surface minimal metrics; -enable ACKs. -- **Privacy-First:** Follow TimeSafari's privacy-preserving architecture; -user-controlled visibility and data sovereignty. -- **Community-Focused:** Enhance TimeSafari's mission of connecting people through -gratitude, gifts, and collaborative projects. +- **Reliability:** OS‑level delivery once scheduled; no reliance on JS being alive at fire time. +- **Freshness:** Prefer online‑first within a short prefetch window; degrade gracefully to cached content with TTL. +- **Extractable:** Clean interfaces (Scheduler, DataStore, Callbacks) so v2 **will swap** adapters without API changes. +- **Simplicity:** One‑shot notifications per slot; rolling window scheduling to respect platform caps. +- **Observability:** Persist deliveries and errors; surface minimal metrics; enable ACKs. +- **Privacy-First:** Follow TimeSafari's privacy-preserving architecture; user-controlled visibility and data sovereignty. +- **Community-Focused:** Enhance TimeSafari's mission of connecting people through gratitude, gifts, and collaborative projects. --- @@ -77,90 +56,58 @@ Platform **Execution modes (concise):** -- **Online‑First:** wake near slot → fetch (ETag, timeout) → persist → schedule; -on failure → Offline‑First. -- **Offline‑First:** read last good payload from SQLite; if beyond TTL → skip -notification (no retry). +- **Online‑First:** wake near slot → fetch (ETag, timeout) → persist → schedule; on failure → Offline‑First. +- **Offline‑First:** read last good payload from SQLite; if beyond TTL → skip notification (no retry). --- ## 4) Public API (Shared by v1 & v2) -**Core Types & Interface:** See Implementation document for complete API -definitions, type interfaces, and design decisions. - -> **Storage semantics:** `'shared'` = app DB; `'private'` = plugin-owned/native -> DB (v2). (No functional difference in v1.) -> **Slot Identity & Scheduling Policy** -> • **SlotId** uses canonical `HHmm` and remains stable across timezone -> changes. -> • **Lead window:** default `prefetchLeadMinutes = 20`; no retries once inside - the lead. -> • **TTL policy:** When offline and content is beyond TTL, **we will skip** the -notification (no "(cached)" suffix). -> • **Idempotency:** Duplicate "scheduled" deliveries are prevented by a unique -index on `(slot_id, fire_at, status='scheduled')`. -> • **Time handling:** Slots will follow **local wall-clock** time across TZ/DST; -`slotId=HHmm` stays constant and we will **recompute fire times** on offset change. +**Core Types & Interface:** See Implementation document for complete API definitions, type interfaces, and design decisions. + +> **Storage semantics:** `'shared'` = app DB; `'private'` = plugin-owned/native DB (v2). (No functional difference in v1.) +> **Slot Identity & Scheduling Policy** +> • **SlotId** uses canonical `HHmm` and remains stable across timezone changes. +> • **Lead window:** default `prefetchLeadMinutes = 20`; no retries once inside the lead. +> • **TTL policy:** When offline and content is beyond TTL, **we will skip** the notification (no "(cached)" suffix). +> • **Idempotency:** Duplicate "scheduled" deliveries are prevented by a unique index on `(slot_id, fire_at, status='scheduled')`. +> • **Time handling:** Slots will follow **local wall-clock** time across TZ/DST; `slotId=HHmm` stays constant and we will **recompute fire times** on offset change. --- ## 5) Data Model & Retention (SQLite) -**Tables:** `notif_contents`, `notif_deliveries`, `notif_config` (see -Implementation document for complete schema) +**Tables:** `notif_contents`, `notif_deliveries`, `notif_config` (see Implementation document for complete schema) -**Retention:** We **will keep** ~14 days of contents/deliveries (configurable) -and **will prune** via a simple daily job that runs on app start/resume. We -**will prune** daily but **will not** VACUUM by default on mobile; disk -compaction is deferred. +**Retention:** We **will keep** ~14 days of contents/deliveries (configurable) and **will prune** via a simple daily job that runs on app start/resume. We **will prune** daily but **will not** VACUUM by default on mobile; disk compaction is deferred. -**Payload handling:** We **will template** `{title, body}` **before** scheduling; -we **will not** mutate at delivery time. +**Payload handling:** We **will template** `{title, body}` **before** scheduling; we **will not** mutate at delivery time. --- ## 6) Scheduling Policy & Slot Math - **One‑shot per slot** per day (non‑repeating). -- **Rolling window:** today's remaining slots; seed tomorrow where platform -limits allow. -- **TZ/DST safe:** We **will recompute** local wall‑times on app resume and -whenever timezone/offset changes; then **reschedule**. -- **Android exactness:** If exact alarms are unavailable or denied, we -**will use** `setWindow` semantics via the scheduler adapter. -- **iOS pending cap:** We **will keep** pending locals within typical caps (~64) -by limiting the window and canceling/re‑arming as needed. -- **Electron rolling window:** On Electron we **will schedule** the **next -occurrence per slot** by default; depth (today+tomorrow) **will be** enabled only -when auto-launch is on, to avoid drift while the app is closed. +- **Rolling window:** today's remaining slots; seed tomorrow where platform limits allow. +- **TZ/DST safe:** We **will recompute** local wall‑times on app resume and whenever timezone/offset changes; then **reschedule**. +- **Android exactness:** If exact alarms are unavailable or denied, we **will use** `setWindow` semantics via the scheduler adapter. +- **iOS pending cap:** We **will keep** pending locals within typical caps (~64) by limiting the window and canceling/re‑arming as needed. +- **Electron rolling window:** On Electron we **will schedule** the **next occurrence per slot** by default; depth (today+tomorrow) **will be** enabled only when auto-launch is on, to avoid drift while the app is closed. --- ## 7) Timing & Network Requirements -**Summary:** The notification system uses lightweight, ETag-aware content -fetching with single attempts inside lead windows. All timing constants and -detailed network policies are defined in the Implementation document. +**Summary:** The notification system uses lightweight, ETag-aware content fetching with single attempts inside lead windows. All timing constants and detailed network policies are defined in the Implementation document. **Key Policies:** -- **Lead policy:** The lead window governs **online-first fetch attempts**, not -arming. We **will arm** locals **whenever the app runs**, using the freshest -available payload. -- **TTL policy:** If offline and content is beyond TTL, we will **skip** the -notification (no "cached" suffix). -- **Idempotency:** Duplicate "scheduled" rows are prevented by a unique index on -`(slot_id, fire_at, status='scheduled')`. -- **Wall-clock rule:** Slots will follow **local wall-clock** across TZ/DST; -`slotId=HHmm` stays constant and we will **recompute fire times** on offset change. -- **Resume debounce:** On app resume/open we will **debounce** pipeline -entry points by **30s** per app session to avoid burst fetches. -- **No scheduled background network in v1 (mobile):** Local notifications -**will deliver offline once armed**, but **we will not** run timed network jobs -when the app is terminated. **Network prefetch will occur only while the app is -running** (launch/resume/inside lead). Server-driven push (Web SW) and OS -background schedulers are a **v2** capability. +- **Lead policy:** The lead window governs **online-first fetch attempts**, not arming. We **will arm** locals **whenever the app runs**, using the freshest available payload. +- **TTL policy:** If offline and content is beyond TTL, we will **skip** the notification (no "cached" suffix). +- **Idempotency:** Duplicate "scheduled" rows are prevented by a unique index on `(slot_id, fire_at, status='scheduled')`. +- **Wall-clock rule:** Slots will follow **local wall-clock** across TZ/DST; `slotId=HHmm` stays constant and we will **recompute fire times** on offset change. +- **Resume debounce:** On app resume/open we will **debounce** pipeline entry points by **30s** per app session to avoid burst fetches. +- **No scheduled background network in v1 (mobile):** Local notifications **will deliver offline once armed**, but **we will not** run timed network jobs when the app is terminated. **Network prefetch will occur only while the app is running** (launch/resume/inside lead). Server-driven push (Web SW) and OS background schedulers are a **v2** capability. **Platform-Specific Network Access:** @@ -171,18 +118,9 @@ background schedulers are a **v2** capability. **Optional Background Prefetch (v1):** -- **Background Runner (optional, v1):** We **will integrate** Capacitor's -Background Runner to **opportunistically prefetch** content on iOS/Android when -the OS grants background time. This **will not** provide clock-precise execution -and **will not** run after user-terminate on iOS. It **will not** be treated as -a scheduler. We **will continue** to *arm* local notifications via our rolling -window regardless of Runner availability. When Runner fires near a slot (inside -`prefetchLeadMinutes`), it **will** refresh content (ETag, 12s timeout) and, -behind a flag, **may** cancel & re-arm that slot with the fresh template if within -TTL. If no budget or failure, the previously armed local **will** still deliver. +- **Background Runner (optional, v1):** We **will integrate** Capacitor's Background Runner to **opportunistically prefetch** content on iOS/Android when the OS grants background time. This **will not** provide clock-precise execution and **will not** run after user-terminate on iOS. It **will not** be treated as a scheduler. We **will continue** to *arm* local notifications via our rolling window regardless of Runner availability. When Runner fires near a slot (inside `prefetchLeadMinutes`), it **will** refresh content (ETag, 12s timeout) and, behind a flag, **may** cancel & re-arm that slot with the fresh template if within TTL. If no budget or failure, the previously armed local **will** still deliver. -**Implementation Details:** See Implementation document for complete timing -constants table, network request profiles, and platform-specific enforcement. +**Implementation Details:** See Implementation document for complete timing constants table, network request profiles, and platform-specific enforcement. --- @@ -190,113 +128,70 @@ constants table, network request profiles, and platform-specific enforcement. **iOS** -- Local notifications **will** fire without background runtime once scheduled. -NSE **will not** mutate locals; delivery-time enrichment requires remote push (future). +- Local notifications **will** fire without background runtime once scheduled. NSE **will not** mutate locals; delivery-time enrichment requires remote push (future). - **Category ID**: `TS_DAILY` with default `OPEN` action -- **Background budget** is short and OS‑managed; any prefetch work -**will complete** promptly. +- **Background budget** is short and OS‑managed; any prefetch work **will complete** promptly. - **Mobile local notifications will route via action listeners (not the service worker)**. -- Background Runner **will** offer **opportunistic** network wake (no guarantees; -short runtime; iOS will not run after force-quit). Locals **will** still deliver -offline once armed. +- Background Runner **will** offer **opportunistic** network wake (no guarantees; short runtime; iOS will not run after force-quit). Locals **will** still deliver offline once armed. **Android** -- Exact alarms on **API 31+** may require `SCHEDULE_EXACT_ALARM`. If exact access -is missing on API 31+, we will use a **windowed trigger (default ±10m)** and -surface a settings deep-link. +- Exact alarms on **API 31+** may require `SCHEDULE_EXACT_ALARM`. If exact access is missing on API 31+, we will use a **windowed trigger (default ±10m)** and surface a settings deep-link. - **We will deep-link users to the exact-alarm settings when we detect denials.** -- **Channel defaults**: ID `timesafari.daily`, name "TimeSafari Daily", -importance=high (IDs never change) -- Receivers for reboot/time change **will be handled** by v2 (plugin); in v1, -re‑arming **will occur** on app start/resume. +- **Channel defaults**: ID `timesafari.daily`, name "TimeSafari Daily", importance=high (IDs never change) +- Receivers for reboot/time change **will be handled** by v2 (plugin); in v1, re‑arming **will occur** on app start/resume. - **Mobile local notifications will route via action listeners (not the service worker)**. -- Background Runner **will** offer **opportunistic** network wake (no guarantees; -short runtime; iOS will not run after force-quit). Locals **will** still deliver -offline once armed. +- Background Runner **will** offer **opportunistic** network wake (no guarantees; short runtime; iOS will not run after force-quit). Locals **will** still deliver offline once armed. **Web** -- Requires registered Service Worker + permission; can deliver with browser closed. -**Web will not offline-schedule**. -- Service Worker click handlers are for **web push only**; -**mobile locals bypass the SW**. -- SW examples use `/sw.js` as a placeholder; **wire this to your actual build -output path** (e.g., `sw_scripts/notification-click.js` or your combined bundle). -- **Note**: Service workers are **intentionally disabled** in Electron -(`src/main.electron.ts`) and web uses VitePWA plugin for minimal implementation. +- Requires registered Service Worker + permission; can deliver with browser closed. **Web will not offline-schedule**. +- Service Worker click handlers are for **web push only**; **mobile locals bypass the SW**. +- SW examples use `/sw.js` as a placeholder; **wire this to your actual build output path** (e.g., `sw_scripts/notification-click.js` or your combined bundle). +- **Note**: Service workers are **intentionally disabled** in Electron (`src/main.electron.ts`) and web uses VitePWA plugin for minimal implementation. **Electron** -- We **will use** native OS notifications with **best-effort scheduling while the -app is running**; true background scheduling will be addressed in v2 (native bridges). +- We **will use** native OS notifications with **best-effort scheduling while the app is running**; true background scheduling will be addressed in v2 (native bridges). + +**Electron delivery strategy (v1 reality + v2 path)** +We **will deliver** desktop notifications while the Electron app is running. True **background scheduling when the app is closed** is **out of scope for v1** and **will be addressed** in v2 via native bridges. We **will adopt** one of the following options (in order of fit to our codebase): + +**In-app scheduler + auto-launch (recommended now):** Keep the orchestrator in the main process, **start on login** (tray app, hidden window), and use the **Electron `Notification` API** for delivery. This requires no new OS services and aligns with our PlatformServiceFactory/mixin patterns. -**Electron delivery strategy (v1 reality + v2 path)** -We **will deliver** desktop notifications while the Electron app is running. True -**background scheduling when the app is closed** is **out of scope for v1** and -**will be addressed** in v2 via native bridges. We **will adopt** one of the -following options (in order of fit to our codebase): +**Policy (v1):** If the app is **not running**, Electron will **not** deliver scheduled locals. With **auto-launch enabled**, we **will achieve** near-mobile parity while respecting OS sleep/idle behavior. -**In-app scheduler + auto-launch (recommended now):** Keep the orchestrator in -the main process, **start on login** (tray app, hidden window), and use the -**Electron `Notification` API** for delivery. This requires no new OS services -and aligns with our PlatformServiceFactory/mixin patterns. +**UX notes:** On Windows we **will set** `appUserModelId` so toasts are attributed correctly; on macOS we **will request** notification permission on first use. -**Policy (v1):** If the app is **not running**, Electron will **not** deliver -scheduled locals. With **auto-launch enabled**, we **will achieve** near-mobile -parity while respecting OS sleep/idle behavior. +**Prerequisites:** We **will require** Node 18+ (global `fetch`) or we **will polyfill** via `undici` for content fetching in the main process. -**UX notes:** On Windows we **will set** `appUserModelId` so toasts are -attributed correctly; on macOS we **will request** notification permission on -first use. +--- + +## 9) Template Engine Contract -**Prerequisites:** We **will require** Node 18+ (global `fetch`) or we -**will polyfill** via `undici` for content fetching in the main process. +**Supported tokens:** `{{headline}}`, `{{summary}}`, `{{date}}` (YYYY-MM-DD), `{{time}}` (HH:MM). +**Escaping:** HTML-escape all injected values. +**Limits:** Title ≤ 50 chars; Body ≤ 200 chars; truncate with ellipsis. +**Fallback:** Missing token → `"[Content]"`. +**Mutation:** We **will** render templates **before** scheduling; no mutation at delivery time on iOS locals. --- -## 8) Template Engine Contract - -**Supported tokens:** `{{headline}}`, `{{summary}}`, `{{date}}` (YYYY-MM-DD), -`{{time}}` (HH:MM). -**Escaping:** HTML-escape all injected values. -**Limits:** Title ≤ 50 chars; Body ≤ 200 chars; truncate with ellipsis. -**Fallback:** Missing token → `"[Content]"`. -**Mutation:** We **will** render templates **before** scheduling; no mutation at -delivery time on iOS locals. - -## 9) Integration with Existing TimeSafari Infrastructure - -**Database:** We **will integrate** with existing migration system in -`src/db-sql/migration.ts` following the established `MIGRATIONS` array pattern -**Settings:** We **will extend** existing Settings type in -`src/db/tables/settings.ts` following the established type extension pattern -**Platform Service:** We **will leverage** existing PlatformServiceMixin database -utilities following the established mixin pattern -**Service Factory:** We **will follow** the existing `PlatformServiceFactory` -singleton pattern for notification service creation -**Capacitor:** We **will integrate** with existing deep link system in -`src/main.capacitor.ts` following the established initialization pattern -**Service Worker:** We **will extend** existing service worker infrastructure -following the established `sw_scripts/` pattern (Note: Service workers are -intentionally disabled in Electron and have minimal web implementation via -VitePWA plugin) -**API:** We **will use** existing error handling from `src/services/api.ts` -following the established `handleApiError` pattern -**Logging:** We **will use** existing logger from `src/utils/logger` following -the established logging patterns -**Platform Detection:** We **will use** existing `process.env.VITE_PLATFORM` -patterns (`web`, `capacitor`, `electron`) -**Vue Architecture:** We **will follow** Vue 3 + vue-facing-decorator patterns -for component integration (Note: The existing `useNotifications` composable in -`src/composables/useNotifications.ts` is currently stub functions with -eslint-disable comments and needs implementation) -**State Management:** We **will integrate** with existing settings system via -`PlatformServiceMixin.$saveSettings()` for notification preferences (Note: -TimeSafari uses PlatformServiceMixin for all state management, not Pinia stores) -**Identity System:** We **will integrate** with existing `did:ethr:` -(Ethereum-based DID) system for user context -**Testing:** We **will follow** Playwright E2E testing patterns established in TimeSafari +## 10) Integration with Existing TimeSafari Infrastructure + +**Database:** We **will integrate** with existing migration system in `src/db-sql/migration.ts` following the established `MIGRATIONS` array pattern +**Settings:** We **will extend** existing Settings type in `src/db/tables/settings.ts` following the established type extension pattern +**Platform Service:** We **will leverage** existing PlatformServiceMixin database utilities following the established mixin pattern +**Service Factory:** We **will follow** the existing `PlatformServiceFactory` singleton pattern for notification service creation +**Capacitor:** We **will integrate** with existing deep link system in `src/main.capacitor.ts` following the established initialization pattern +**Service Worker:** We **will extend** existing service worker infrastructure following the established `sw_scripts/` pattern (Note: Service workers are intentionally disabled in Electron and have minimal web implementation via VitePWA plugin) +**API:** We **will use** existing error handling from `src/services/api.ts` following the established `handleApiError` pattern +**Logging:** We **will use** existing logger from `src/utils/logger` following the established logging patterns +**Platform Detection:** We **will use** existing `process.env.VITE_PLATFORM` patterns (`web`, `capacitor`, `electron`) +**Vue Architecture:** We **will follow** Vue 3 + vue-facing-decorator patterns for component integration (Note: The existing `useNotifications` composable in `src/composables/useNotifications.ts` is currently stub functions with eslint-disable comments and needs implementation) +**State Management:** We **will integrate** with existing settings system via `PlatformServiceMixin.$saveSettings()` for notification preferences (Note: TimeSafari uses PlatformServiceMixin for all state management, not Pinia stores) +**Identity System:** We **will integrate** with existing `did:ethr:` (Ethereum-based DID) system for user context +**Testing:** We **will follow** Playwright E2E testing patterns established in TimeSafari **Database Architecture:** We **will support** platform-specific database backends: - **Web**: Absurd SQL (SQLite via IndexedDB) via `WebPlatformService` with worker pattern @@ -305,87 +200,64 @@ TimeSafari uses PlatformServiceMixin for all state management, not Pinia stores) --- -## 10) Error Taxonomy & Telemetry +## 11) Error Taxonomy & Telemetry -**Error Codes:** `FETCH_TIMEOUT`, `ETAG_NOT_MODIFIED`, `SCHEDULE_DENIED`, -`EXACT_ALARM_MISSING`, `STORAGE_BUSY`, `TEMPLATE_MISSING_TOKEN`, `PERMISSION_DENIED`. +**Error Codes:** `FETCH_TIMEOUT`, `ETAG_NOT_MODIFIED`, `SCHEDULE_DENIED`, `EXACT_ALARM_MISSING`, `STORAGE_BUSY`, `TEMPLATE_MISSING_TOKEN`, `PERMISSION_DENIED`. -**Event Envelope:** `code, slotId, whenMs, attempt, networkState, tzOffset, -appState, timestamp`. +**Event Envelope:** `code, slotId, whenMs, attempt, networkState, tzOffset, appState, timestamp`. --- -## 11) Permission UX & Channels/Categories +## 12) Permission UX & Channels/Categories -- We **will request** notification permission **after** user intent (e.g., -settings screen), not on first render. -- **Android:** We **will create** a stable channel ID (e.g., `timesafari.daily`) -and **will set** importance appropriately. -- **iOS:** We **will register** categories for optional actions; grouping may -use `threadIdentifier` per slot/day. +- We **will request** notification permission **after** user intent (e.g., settings screen), not on first render. +- **Android:** We **will create** a stable channel ID (e.g., `timesafari.daily`) and **will set** importance appropriately. +- **iOS:** We **will register** categories for optional actions; grouping may use `threadIdentifier` per slot/day. --- -## 12) Eventing & Telemetry +## 13) Eventing & Telemetry -**Error Codes:** `FETCH_TIMEOUT`, `ETAG_NOT_MODIFIED`, `SCHEDULE_DENIED`, -`EXACT_ALARM_MISSING`, `STORAGE_BUSY`, `TEMPLATE_MISSING_TOKEN`, `PERMISSION_DENIED`. +**Error Codes:** `FETCH_TIMEOUT`, `ETAG_NOT_MODIFIED`, `SCHEDULE_DENIED`, `EXACT_ALARM_MISSING`, `STORAGE_BUSY`, `TEMPLATE_MISSING_TOKEN`, `PERMISSION_DENIED`. -**Event Envelope:** `code, slotId, whenMs, attempt, networkState, tzOffset, -appState, timestamp`. +**Event Envelope:** `code, slotId, whenMs, attempt, networkState, tzOffset, appState, timestamp`. -**Implementation:** See Implementation document for complete error taxonomy, -event logging envelope, ACK payload format, and telemetry events. +**Implementation:** See Implementation document for complete error taxonomy, event logging envelope, ACK payload format, and telemetry events. --- -## 13) Feature Flags & Config +## 14) Feature Flags & Config -**Key Flags:** `scheduler`, `mode`, `prefetchLeadMinutes`, `ttlSeconds`, -`iosCategoryIdentifier`, `androidChannelId`, `prefetchRunner`, `runnerRearm`. +**Key Flags:** `scheduler`, `mode`, `prefetchLeadMinutes`, `ttlSeconds`, `iosCategoryIdentifier`, `androidChannelId`, `prefetchRunner`, `runnerRearm`. -**Storage:** Feature flags **will reside** in `notif_config` table as key-value -pairs, separate from user settings. +**Storage:** Feature flags **will reside** in `notif_config` table as key-value pairs, separate from user settings. -**Implementation:** See Implementation document for complete feature flags table - with defaults and descriptions. +**Implementation:** See Implementation document for complete feature flags table with defaults and descriptions. --- -## 14) Acceptance (Definition of Done) → Test Cases +## 15) Acceptance (Definition of Done) → Test Cases ### Explicit Test Checks -- **App killed → locals fire**: Configure slots at 8:00, 12:00, 18:00; kill -app; verify notifications fire at each slot on iOS/Android -- **ETag 304 path**: Server returns 304 → keep previous content; locals fire -with cached payload -- **ETag 200 path**: Server returns 200 → update content and re-arm locals with -fresh payload -- **Offline + beyond TTL**: When offline and content > 24h old → skip notification -(no "(cached)" suffix) -- **iOS pending cap**: Respect ~64 pending limit; cancel/re-arm as needed within -rolling window -- **Exact-alarm denied**: Android permission absent → windowed schedule (±10m) -activates; UI shows fallback hint -- **Permissions disabled** → we will record `SCHEDULE_DENIED` and refrain from -queuing locals. -- **Window fallback** → when exact alarm is absent on Android, verify target -fires within **±10m** of slot time (document as an E2E expectation). -- **Timezone change**: On TZ/DST change → recompute wall-clock times; cancel & -re-arm all slots +- **App killed → locals fire**: Configure slots at 8:00, 12:00, 18:00; kill app; verify notifications fire at each slot on iOS/Android +- **ETag 304 path**: Server returns 304 → keep previous content; locals fire with cached payload +- **ETag 200 path**: Server returns 200 → update content and re-arm locals with fresh payload +- **Offline + beyond TTL**: When offline and content > 24h old → skip notification (no "(cached)" suffix) +- **iOS pending cap**: Respect ~64 pending limit; cancel/re-arm as needed within rolling window +- **Exact-alarm denied**: Android permission absent → windowed schedule (±10m) activates; UI shows fallback hint +- **Permissions disabled** → we will record `SCHEDULE_DENIED` and refrain from queuing locals. +- **Window fallback** → when exact alarm is absent on Android, verify target fires within **±10m** of slot time (document as an E2E expectation). +- **Timezone change**: On TZ/DST change → recompute wall-clock times; cancel & re-arm all slots - **Lead window respect**: No retries attempted once inside 20min lead window -- **Idempotency**: Multiple `runFullPipelineNow()` calls don't create duplicate -scheduled deliveries +- **Idempotency**: Multiple `runFullPipelineNow()` calls don't create duplicate scheduled deliveries - **Cooldown guard**: `deliverStoredNow()` has 60s cooldown to prevent double-firing ### Electron-Specific Test Checks -- **Electron running (tray or window) → notifications fire** at configured slots -using Electron `Notification` +- **Electron running (tray or window) → notifications fire** at configured slots using Electron `Notification` - **Electron not running →** no delivery (documented limitation for v1) -- **Start on Login enabled →** after reboot + login, orchestrator **will re-arm** -slots and deliver +- **Start on Login enabled →** after reboot + login, orchestrator **will re-arm** slots and deliver - **Template limits honored** (Title ≤ 50, Body ≤ 200) on Electron notifications - **SW scope** not used for Electron (click handlers are **web only**) - **Windows appUserModelId** set correctly for toast attribution @@ -393,76 +265,51 @@ slots and deliver ### Timing-Verifiable Test Checks -- **iOS/Android (app killed):** locals will fire at their slots; no network -activity at delivery time. -- **iOS/Android (resume inside lead):** exactly **one** online-first attempt -occurs; if fetch completes within **12s** → content updated; otherwise offline -policy applies. -- **Android (no exact access):** observed delivery is within **±10 min** of slot -time. -- **Web push:** SW push event fetch runs once with **12s** timeout; if it times -out, the push still displays (from payload). -- **Electron (app running):** timer-based locals fire on time; on reboot with -**Start on Login**, orchestrator re-arms on first run. +- **iOS/Android (app killed):** locals will fire at their slots; no network activity at delivery time. +- **iOS/Android (resume inside lead):** exactly **one** online-first attempt occurs; if fetch completes within **12s** → content updated; otherwise offline policy applies. +- **Android (no exact access):** observed delivery is within **±10 min** of slot time. +- **Web push:** SW push event fetch runs once with **12s** timeout; if it times out, the push still displays (from payload). +- **Electron (app running):** timer-based locals fire on time; on reboot with **Start on Login**, orchestrator re-arms on first run. - **TTL behavior:** offline & stale → **skip** (no notification posted). -- **ETag path:** with `304`, last payload remains; no duplicate scheduling rows -(unique index enforced). -- **Cooldown:** calling `deliverStoredNow` twice within **60s** for same slot -doesn't produce two notifications. -- **Closed app, armed earlier** → locals fire at slot; title/body match last -rendered content (proves "render at schedule time" + adapter API). -- **Closed app, timezone change before slot** → on next resume, app recomputes -and re-arms; already armed notifications will still fire on original wall-time -- **Mobile closed-app, no background network:** Arm at T–hours; kill app; verify -locals fire with last rendered text; confirm **no** network egress at delivery. -- **Web push as network scheduler:** Send push with empty payload → SW fetches -within 12s timeout → shows correct text; confirm behavior with browser closed. -- **Electron app not running:** No delivery; with **Start on Login**, after -reboot first run fetches and re-arms; subsequent slots fire. -- **Runner fires in background (Android/iOS):** With Runner enabled and app -backgrounded for ≥30 min, at least one prefetch **will** occur; content cache -**will** update; already-armed locals **will** still fire on time. -- **Runner re-arm (flagged):** If `runnerRearm=true` and Runner fires inside lead -with fresh content + within TTL, the system **will** cancel & re-arm the next -slot; delivered text **will** match fresh template. +- **ETag path:** with `304`, last payload remains; no duplicate scheduling rows (unique index enforced). +- **Cooldown:** calling `deliverStoredNow` twice within **60s** for same slot doesn't produce two notifications. +- **Closed app, armed earlier** → locals fire at slot; title/body match last rendered content (proves "render at schedule time" + adapter API). +- **Closed app, timezone change before slot** → on next resume, app recomputes and re-arms; already armed notifications will still fire on original wall-time +- **Mobile closed-app, no background network:** Arm at T–hours; kill app; verify locals fire with last rendered text; confirm **no** network egress at delivery. +- **Web push as network scheduler:** Send push with empty payload → SW fetches within 12s timeout → shows correct text; confirm behavior with browser closed. +- **Electron app not running:** No delivery; with **Start on Login**, after reboot first run fetches and re-arms; subsequent slots fire. +- **Runner fires in background (Android/iOS):** With Runner enabled and app backgrounded for ≥30 min, at least one prefetch **will** occur; content cache **will** update; already-armed locals **will** still fire on time. +- **Runner re-arm (flagged):** If `runnerRearm=true` and Runner fires inside lead with fresh content + within TTL, the system **will** cancel & re-arm the next slot; delivered text **will** match fresh template. --- -## 15) Test Matrix (Essentials) +## 16) Test Matrix (Essentials) -- **Android:** exact vs inexact branch, Doze/App Standby behavior, reboot/time -change, permission denial path, deep‑link to exact‑alarm settings. -- **iOS:** BG fetch budget limits, pending cap windowing, local notification -delivery with app terminated, category actions. -- **Web:** SW lifecycle, push delivery with app closed, click handling, no -offline scheduling. -- **Cross‑cutting:** ETag/304 behavior, TTL policy, templating correctness, event - queue drain, SQLite retention job. +- **Android:** exact vs inexact branch, Doze/App Standby behavior, reboot/time change, permission denial path, deep‑link to exact‑alarm settings. +- **iOS:** BG fetch budget limits, pending cap windowing, local notification delivery with app terminated, category actions. +- **Web:** SW lifecycle, push delivery with app closed, click handling, no offline scheduling. +- **Cross‑cutting:** ETag/304 behavior, TTL policy, templating correctness, event queue drain, SQLite retention job. --- -## 16) Migration & Rollout Notes +## 17) Migration & Rollout Notes - We **will keep** existing web push flows intact. -- We **will introduce** the orchestrator behind a feature flag, initially with -a small number of slots. +- We **will introduce** the orchestrator behind a feature flag, initially with a small number of slots. - We **will migrate** settings to accept multiple times per day. -- We **will document** platform caveats inside user‑visible settings (e.g., -Android exact alarms, iOS cap). +- We **will document** platform caveats inside user‑visible settings (e.g., Android exact alarms, iOS cap). --- -## 17) Security & Privacy +## 18) Security & Privacy -- Tokens **will reside** in Keystore/Keychain (mobile) and **will be injected** -at request time; they **will not** be stored in SQLite. -- Optionally, SQLCipher at rest for mobile; redaction of PII in logs; payload -size caps. +- Tokens **will reside** in Keystore/Keychain (mobile) and **will be injected** at request time; they **will not** be stored in SQLite. +- Optionally, SQLCipher at rest for mobile; redaction of PII in logs; payload size caps. - Content **will be** minimal (title/body); sensitive data **will not be** embedded. --- -## 18) Non‑Goals (Now) +## 19) Non‑Goals (Now) - Complex action sets and rich media on locals (kept minimal). - Delivery‑time mutation of local notifications on iOS (NSE is for remote). @@ -470,69 +317,19 @@ size caps. --- -## 19) Cross-Doc Sync Hygiene - -### Canonical Ownership - -- **This document (Plan)**: Canonical for Goals, Tenets, Platform behaviors, -Acceptance criteria, Test cases -- **Implementation document**: Canonical for API definitions, Database schemas, -Adapter implementations, Code examples - -### PR Checklist - -When changing notification system behavior, update both documents: - -- [ ] **API changes**: Update types/interfaces in both Plan §4 and Implementation §3 -- [ ] **Schema changes**: Update Plan §5 and Implementation §2 -- [ ] **Slot/TTL changes**: Update Plan §4 semantics and Implementation §6 logic -- [ ] **Template changes**: Update Plan §9 contract and Implementation examples -- [ ] **Error codes**: Update Plan §11 taxonomy and Implementation error handling - -- **API code blocks**: Must be identical between Plan §4 and Implementation §3 -(Public API (Shared)) -- **Feature flags**: Must match between Plan §12 table and Implementation defaults -- **Test cases**: Plan §13 acceptance criteria must align with Implementation -test examples -- **Slot/TTL/Lead policies**: Must be identical between Plan §4 policy and -Implementation §3 policy - ---- - -## Sync Checklist - -| Sync item | Plan | Impl | Status | -| ------------------------------ | --------------------- | --------------------- | ------ | -| Public API block identical | §4 | §3 | ✅ | -| `getState()` fields present | §4 | §8 Orchestrator | ✅ | -| Capacitor action handlers | §7 (iOS/Android note) | §9 Bootstrap | ✅ | -| Electron fetch prereq/polyfill | §7 | §9 Electron | ✅ | -| Android ±10m fallback | §7 | §7 SchedulerCapacitor | ✅ | -| Retention (no VACUUM v1) | §5 | `$pruneNotifData` | ✅ | -| Runner described as **opportunistic prefetch**, not scheduler | §7 | §9 | ✅ | -| Feature flag `prefetchRunner` (default `'none'`) | §13 | §15 | ✅ | -| Capabilities `networkWake: 'opportunistic' | 'none'` | §7 | Scheduler.capabilities | ✅ | -| Runner tick handler bounded to ≤12s | §7 | BackgroundRunnerPrefetch | ✅ | -| Optional `runnerRearm` flag & behavior | §7 | Orchestrator + Runner | ✅ | +## 20) Cross-Document Synchronization ---- +**Canonical Ownership:** +- **This document (Plan):** Goals, Tenets, Platform behaviors, Acceptance criteria, Test cases +- **Implementation document:** API definitions, Database schemas, Adapter implementations, Code examples -## Sync Checklist - -| Sync item | Plan | Impl | Status | -| ------------------------------ | --------------------- | --------------------- | ------ | -| Public API block identical | §4 | §3 | ✅ | -| `getState()` fields present | §4 | §8 Orchestrator | ✅ | -| Capacitor action handlers | §7 (iOS/Android note) | §9 Bootstrap | ✅ | -| Electron fetch prereq/polyfill | §7 | §9 Electron | ✅ | -| Android ±10m fallback | §7 | §7 SchedulerCapacitor | ✅ | -| Retention (no VACUUM v1) | §5 | `$pruneNotifData` | ✅ | -| Runner described as **opportunistic prefetch**, not scheduler | §7 | §9 | ✅ | -| Feature flag `prefetchRunner` (default `'none'`) | §13 | §15 | ✅ | -| Capabilities `networkWake: 'opportunistic' | 'none'` | §7 | Scheduler.capabilities | ✅ | -| Runner tick handler bounded to ≤12s | §7 | BackgroundRunnerPrefetch | ✅ | -| Optional `runnerRearm` flag & behavior | §7 | Orchestrator + Runner | ✅ | +**Synchronization Requirements:** +- API code blocks must be identical between Plan §4 and Implementation §3 +- Feature flags must match between Plan §13 and Implementation §15 defaults +- Test cases must align between Plan §14 acceptance criteria and Implementation examples +- Error codes must match between Plan §11 taxonomy and Implementation error handling +- Slot/TTL/Lead policies must be identical between Plan §4 semantics and Implementation §9 logic --- -*This strategic plan focuses on features and future‑tense deliverables, avoids implementation details, and preserves a clear path from the in‑app orchestrator (v1) to native plugin (v2). For executive overview, see `notification-system-executive-summary.md`. For complete implementation details, see `notification-system-implementation.md`.* +*This strategic plan focuses on features and future‑tense deliverables, avoids implementation details, and preserves a clear path from the in‑app orchestrator (v1) to native plugin (v2).* \ No newline at end of file