**Status**: 🚢 ACTIVE — General ruleset extending *Base Context — Human Competence First*
**Status**: 🚢 ACTIVE — General ruleset extending *Base Context — Human Competence First*
> **Alignment with Base Context**
> **Alignment with Base Context**
>
> - **Purpose fit**: Prioritizes human competence and collaboration while delivering reproducible artifacts.
> - **Purpose fit**: Prioritizes human competence and collaboration while delivering reproducible artifacts.
> - **Output Contract**: This directive **adds universal constraints** for any technical topic while **inheriting** the Base Context contract sections.
> - **Output Contract**: This directive **adds universal constraints** for any technical topic while **inheriting** the Base Context contract sections.
> - **Toggles honored**: Uses the same toggle semantics; defaults above can be overridden by the caller.
> - **Toggles honored**: Uses the same toggle semantics; defaults above can be overridden by the caller.
@ -26,9 +27,11 @@ inherits: base_context.mdc
---
---
## Objective
## Objective
Produce a **developer-grade, reproducible guide** for any technical topic that onboards a competent practitioner **without meta narration** and **with evidence-backed steps**.
Produce a **developer-grade, reproducible guide** for any technical topic that onboards a competent practitioner **without meta narration** and **with evidence-backed steps**.
## Scope & Constraints
## Scope & Constraints
- **One Markdown document** as the deliverable.
- **One Markdown document** as the deliverable.
- Use **absolute dates** in **UTC** (e.g., `2025-08-21T14:22Z`) — avoid “today/yesterday”.
- Use **absolute dates** in **UTC** (e.g., `2025-08-21T14:22Z`) — avoid “today/yesterday”.
- Include at least **one diagram** (Mermaid preferred). Choose the most fitting type:
- Include at least **one diagram** (Mermaid preferred). Choose the most fitting type:
@ -41,6 +44,7 @@ Produce a **developer-grade, reproducible guide** for any technical topic that o
- If something is unknown, output `TODO:<missing>` — **never invent**.
- If something is unknown, output `TODO:<missing>` — **never invent**.
## Required Sections (extends Base Output Contract)
## Required Sections (extends Base Output Contract)
Follow this exact order **after** the Base Contract’s **Objective → Result → Use/Run** headers:
Follow this exact order **after** the Base Contract’s **Objective → Result → Use/Run** headers:
1. **Context & Scope**
1. **Context & Scope**
@ -69,16 +73,19 @@ Follow this exact order **after** the Base Contract’s **Objective → Result
- Canonical docs, specs, tickets, prior analyses.
- Canonical docs, specs, tickets, prior analyses.
> **Competence Hooks (per Base Context; keep lightweight):**
> **Competence Hooks (per Base Context; keep lightweight):**
>
> - *Why this works* (≤3 bullets) — core invariants or guarantees.
> - *Why this works* (≤3 bullets) — core invariants or guarantees.
> - *Common pitfalls* (≤3 bullets) — the traps we saw in evidence.
> - *Common pitfalls* (≤3 bullets) — the traps we saw in evidence.
> - *Next skill unlock* (1 line) — the next capability to implement/learn.
> - *Next skill unlock* (1 line) — the next capability to implement/learn.
> - *Teach-back* (1 line) — prompt the reader to restate the flow/architecture.
> - *Teach-back* (1 line) — prompt the reader to restate the flow/architecture.
> **Collaboration Hooks (per Base Context):**
> **Collaboration Hooks (per Base Context):**
>
> - Name reviewers for **Interfaces & Contracts** and the **diagram**.
> - Name reviewers for **Interfaces & Contracts** and the **diagram**.
> - Short **sign-off checklist** before merging/publishing the guide.
> - Short **sign-off checklist** before merging/publishing the guide.
## Do / Don’t (Base-aligned)
## Do / Don’t (Base-aligned)
- **Do** quantify progress only against a defined scope with acceptance criteria.
- **Do** quantify progress only against a defined scope with acceptance criteria.
- **Do** include minimal sample payloads/headers or I/O schemas; redact sensitive values.
- **Do** include minimal sample payloads/headers or I/O schemas; redact sensitive values.
- **Do** keep commentary lean; if timeboxed, move depth to **Deferred for depth**.
- **Do** keep commentary lean; if timeboxed, move depth to **Deferred for depth**.
@ -86,6 +93,7 @@ Follow this exact order **after** the Base Contract’s **Objective → Result
- **Don’t** include IDE-specific chatter or internal rules unrelated to the task.
- **Don’t** include IDE-specific chatter or internal rules unrelated to the task.
## Validation Checklist (self-check before returning)
## Validation Checklist (self-check before returning)
- [ ] All Required Sections present and ordered.
- [ ] All Required Sections present and ordered.
- [ ] Diagram compiles (basic Mermaid syntax) and fits the problem.
- [ ] Diagram compiles (basic Mermaid syntax) and fits the problem.
- [ ] If API-based, **Auth** and **Key Headers/Params** are listed for each endpoint.
- [ ] If API-based, **Auth** and **Key Headers/Params** are listed for each endpoint.
@ -96,6 +104,7 @@ Follow this exact order **after** the Base Contract’s **Objective → Result
- [ ] Base **Output Contract** sections satisfied (Objective/Result/Use/Run/Competence/Collaboration/Assumptions/References).
- [ ] Base **Output Contract** sections satisfied (Objective/Result/Use/Run/Competence/Collaboration/Assumptions/References).
## Universal Template (fill-in)
## Universal Template (fill-in)
```markdown
```markdown
# <Title> — Working Notes (As of YYYY-MM-DDTHH:MMZ)
# <Title> — Working Notes (As of YYYY-MM-DDTHH:MMZ)
@ -132,37 +141,46 @@ Follow this exact order **after** the Base Contract’s **Objective → Result
@ -11,7 +11,7 @@ See [ClickUp](https://sharing.clickup.com/9014278710/l/h/8cmnyhp-174/10573fec74e
Quick start:
Quick start:
* For setup, we recommend [pkgx](https://pkgx.dev), which installs what you need (either automatically or with the `dev` command). Core dependencies are typescript & npm; when building for other platforms, you'll need other things such as those in the pkgx.yaml & BUILDING.md files.
- For setup, we recommend [pkgx](https://pkgx.dev), which installs what you need (either automatically or with the `dev` command). Core dependencies are typescript & npm; when building for other platforms, you'll need other things such as those in the pkgx.yaml & BUILDING.md files.
```bash
```bash
npm install
npm install
@ -90,6 +90,7 @@ VITE_LOG_LEVEL=debug npm run dev
See [Logging Configuration Guide](doc/logging-configuration.md) for complete details.
See [Logging Configuration Guide](doc/logging-configuration.md) for complete details.
### Quick Usage
### Quick Usage
```bash
```bash
# Run the database clearing script
# Run the database clearing script
./scripts/clear-database.sh
./scripts/clear-database.sh
@ -102,16 +103,19 @@ npm run build:web:dev # For Web
### What It Does
### What It Does
#### **Electron (Desktop App)**
#### **Electron (Desktop App)**
- Automatically finds and clears the SQLite database files
- Automatically finds and clears the SQLite database files
- Works on Linux, macOS, and Windows
- Works on Linux, macOS, and Windows
- Clears all data and forces fresh migrations on next startup
- Clears all data and forces fresh migrations on next startup
#### **Web Browser**
#### **Web Browser**
- Provides instructions for using custom browser data directories
- Provides instructions for using custom browser data directories
- Shows manual clearing via browser DevTools
- Shows manual clearing via browser DevTools
- Ensures reliable database clearing without browser complications
- Ensures reliable database clearing without browser complications
### Safety Features
### Safety Features
- ✅ **Interactive Script**: Guides you through the process
- ✅ **Interactive Script**: Guides you through the process
- ✅ **Platform Detection**: Automatically detects your OS
- ✅ **Platform Detection**: Automatically detects your OS
- ✅ **Clear Instructions**: Step-by-step guidance for each platform
- ✅ **Clear Instructions**: Step-by-step guidance for each platform
@ -120,6 +124,7 @@ npm run build:web:dev # For Web
*`src/utils/PlatformServiceMixin.ts` - Vue mixin for database access with caching
-`src/utils/PlatformServiceMixin.ts` - Vue mixin for database access with caching
*`src/db/` - Legacy Dexie database (migration in progress)
-`src/db/` - Legacy Dexie database (migration in progress)
**Development Guidelines**:
**Development Guidelines**:
@ -316,11 +325,11 @@ timesafari/
Gifts make the world go 'round!
Gifts make the world go 'round!
* [WebStorm by JetBrains](https://www.jetbrains.com/webstorm/) for the free open-source license
- [WebStorm by JetBrains](https://www.jetbrains.com/webstorm/) for the free open-source license
* [Máximo Fernández](https://medium.com/@maxfarenas) for the 3D [code](https://github.com/maxfer03/vue-three-ns) and [explanatory post](https://medium.com/nicasource/building-an-interactive-web-portfolio-with-vue-three-js-part-three-implementing-three-js-452cb375ef80)
- [Máximo Fernández](https://medium.com/@maxfarenas) for the 3D [code](https://github.com/maxfer03/vue-three-ns) and [explanatory post](https://medium.com/nicasource/building-an-interactive-web-portfolio-with-vue-three-js-part-three-implementing-three-js-452cb375ef80)
* [Many tools & libraries](https://gitea.anomalistdesign.com/trent_larson/crowd-funder-for-time-pwa/src/branch/master/package.json#L10) such as Nodejs.org, IntelliJ Idea, Veramo.io, Vuejs.org, threejs.org
- [Many tools & libraries](https://gitea.anomalistdesign.com/trent_larson/crowd-funder-for-time-pwa/src/branch/master/package.json#L10) such as Nodejs.org, IntelliJ Idea, Veramo.io, Vuejs.org, threejs.org
* [Bush 3D model](https://sketchfab.com/3d-models/lupine-plant-bf30f1110c174d4baedda0ed63778439)
- [Bush 3D model](https://sketchfab.com/3d-models/lupine-plant-bf30f1110c174d4baedda0ed63778439)
We can't trust iOS IndexedDB to persist. I want to start delivering an app to people now, in preparation for presentations mid-June: Rotary on June 12 and Porcfest on June 17.
We can't trust iOS IndexedDB to persist. I want to start delivering an app to people now, in preparation for presentations mid-June: Rotary on June 12 and Porcfest on June 17.
* Apple WebKit puts a [7-day cap on IndexedDB](https://webkit.org/blog/10218/full-third-party-cookie-blocking-and-more/).
- Apple WebKit puts a [7-day cap on IndexedDB](https://webkit.org/blog/10218/full-third-party-cookie-blocking-and-more/).
* The web standards expose a `persist` method to mark memory as persistent, and [supposedly WebView supports it](https://developer.mozilla.org/en-US/docs/Web/API/StorageManager/persisted), but too many other things indicate it's not reliable. I've talked with [ChatGPT](https://chatgpt.com/share/68322f40-84c8-8007-b213-855f7962989a) & Venice & Claude (in Cursor); [this answer from Perplexity](https://www.perplexity.ai/search/which-platforms-prompt-the-use-HUQLqy4qQD2cRbkmO4CgHg) says that most platforms don't prompt and Safari doesn't support it; I don't know if that means WebKit as well.
- The web standards expose a `persist` method to mark memory as persistent, and [supposedly WebView supports it](https://developer.mozilla.org/en-US/docs/Web/API/StorageManager/persisted), but too many other things indicate it's not reliable. I've talked with [ChatGPT](https://chatgpt.com/share/68322f40-84c8-8007-b213-855f7962989a) & Venice & Claude (in Cursor); [this answer from Perplexity](https://www.perplexity.ai/search/which-platforms-prompt-the-use-HUQLqy4qQD2cRbkmO4CgHg) says that most platforms don't prompt and Safari doesn't support it; I don't know if that means WebKit as well.
* Capacitor says [not to trust it on iOS](https://capacitorjs.com/docs/v6/guides/storage).
- Capacitor says [not to trust it on iOS](https://capacitorjs.com/docs/v6/guides/storage).
Also, with sensitive data, the accounts info should be encrypted.
Also, with sensitive data, the accounts info should be encrypted.
# Options
# Options
* There is a community [SQLite plugin for Capacitor](https://github.com/capacitor-community/sqlite) with encryption by [SQLCipher](https://github.com/sqlcipher/sqlcipher).
- There is a community [SQLite plugin for Capacitor](https://github.com/capacitor-community/sqlite) with encryption by [SQLCipher](https://github.com/sqlcipher/sqlcipher).
* [This tutorial](https://jepiqueau.github.io/2023/09/05/Ionic7Vue-SQLite-CRUD-App.html#part-1---web---table-of-contents) shows how that plugin works for web as well as native.
- [This tutorial](https://jepiqueau.github.io/2023/09/05/Ionic7Vue-SQLite-CRUD-App.html#part-1---web---table-of-contents) shows how that plugin works for web as well as native.
* Capacitor abstracts [user preferences in an API](https://capacitorjs.com/docs/apis/preferences), which uses different underlying libraries on iOS & Android. Unfortunately, it won't do any filtering or searching, and is only meant for small amounts of data. (It could be used for settings and for identifiers, but contacts will grow and image blobs won't work.)
- Capacitor abstracts [user preferences in an API](https://capacitorjs.com/docs/apis/preferences), which uses different underlying libraries on iOS & Android. Unfortunately, it won't do any filtering or searching, and is only meant for small amounts of data. (It could be used for settings and for identifiers, but contacts will grow and image blobs won't work.)
* There are hints that Capacitor offers another custom storage API but all I could find was that Preferences API.
- There are hints that Capacitor offers another custom storage API but all I could find was that Preferences API.
* [Ionic Storage](https://ionic.io/docs/secure-storage) is an enterprise solution, which also supports encryption.
- [Ionic Storage](https://ionic.io/docs/secure-storage) is an enterprise solution, which also supports encryption.
* Not an option yet: Dexie may support SQLite in [a future version](https://dexie.org/roadmap/dexie5.0).
- Not an option yet: Dexie may support SQLite in [a future version](https://dexie.org/roadmap/dexie5.0).
# Current Plan
# Current Plan
* Implement SQLite for Capacitor & web, with encryption. That will allow us to test quickly and keep the same interface for native & web, but we don't deal with migrations for current web users.
- Implement SQLite for Capacitor & web, with encryption. That will allow us to test quickly and keep the same interface for native & web, but we don't deal with migrations for current web users.
* After that is delivered, write a migration for current web users from IndexedDB to SQLite.
- After that is delivered, write a migration for current web users from IndexedDB to SQLite.
**Scope:** v1 (in‑app orchestrator) now; path to v2 (native plugin) next
**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.
**Goal:** We **will deliver** 1..M local notifications/day with content
**prefetched** so messages **will display offline**. We **will support**
> **Implementation Details:** See `notification-system-implementation.md` for detailed code, database schemas, and integration specifics.
online‑first (API→DB→Schedule) with offline‑first fallback. The system
> **Canonical Ownership:** This document owns Goals, Tenets, Platform behaviors, Acceptance criteria, and Test cases.
**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
## 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.
- **v1 (In‑App Orchestrator):** We **will implement** multi‑daily local
- **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**.
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
## 2) Design Tenets
- **Reliability:** OS‑level delivery once scheduled; no reliance on JS being alive at fire time.
- **Reliability:** OS‑level delivery once scheduled; no reliance on JS being alive
- **Freshness:** Prefer online‑first within a short prefetch window; degrade gracefully to cached content with TTL.
at fire time.
- **Extractable:** Clean interfaces (Scheduler, DataStore, Callbacks) so v2 **will swap** adapters without API changes.
- **Freshness:** Prefer online‑first within a short prefetch window; degrade
- **Simplicity:** One‑shot notifications per slot; rolling window scheduling to respect platform caps.
**Scheduler Adapter:** All notification arming must go through the Scheduler adapter to honor platform timing semantics (exact alarms vs. windowed fallback).
> • **SlotId** uses canonical `HHmm` and remains stable across timezone changes.
> • **SlotId** uses canonical `HHmm` and remains stable across timezone
> • **Lead window:** default `prefetchLeadMinutes = 20`; no retries once inside the lead.
> changes.
> • **TTL policy:** When offline and content is beyond TTL, **we will skip** the notification (no "(cached)" suffix).
> • **Lead window:** default `prefetchLeadMinutes = 20`; no retries once inside
> • **Idempotency:** Duplicate "scheduled" deliveries are prevented by a unique index on `(slot_id, fire_at, status='scheduled')`.
the lead.
> • **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.
> • **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.
**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
## 6) Scheduling Policy & Slot Math
- **One‑shot per slot** per day (non‑repeating).
- **One‑shot per slot** per day (non‑repeating).
- **Rolling window:** today's remaining slots; seed tomorrow where platform limits allow. v1 will schedule **the next occurrence per slot** by default; a **configurable depth** (0=today, 1=today+tomorrow) may be enabled as long as the iOS pending cap is respected.
- **Rolling window:** today's remaining slots; seed tomorrow where platform
- **TZ/DST safe:** We **will recompute** local wall‑times on app resume and whenever timezone/offset changes; then **reschedule**.
limits allow.
- **Android exactness:** If exact alarms are unavailable or denied, we **will use**`setWindow` semantics via the scheduler adapter.
- **TZ/DST safe:** We **will recompute** local wall‑times on app resume and
- **iOS pending cap:** We **will keep** pending locals within typical caps (~64) by limiting the window and canceling/re‑arming as needed.
whenever timezone/offset changes; then **reschedule**.
- **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.
- **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.
**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.
**Platform-Specific Network Access:**
- **iOS:** Foreground/recently backgrounded only; no JS wake when app is killed
- **Android:** Exact alarms vs. windowed triggers based on permissions
- **Web:** Service Worker for push notifications only
- **Electron:** App-running only; no background network access
**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.
**Implementation Details:** See Implementation document for complete timing
constants table, network request profiles, and platform-specific enforcement.
---
---
## 7) Platform Essentials
## 8) Platform Essentials
**iOS**
**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.
- **Category ID**: `TS_DAILY` with default `OPEN` action
- **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)**.
- **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.
**Android**
**Android**
- Exact alarms on **API 31+** may require `SCHEDULE_EXACT_ALARM`. If exact access
- 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.
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.**
- **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)
- **Channel defaults**: ID `timesafari.daily`, name "TimeSafari Daily",
- Receivers for reboot/time change **will be handled** by v2 (plugin); in v1, re‑arming **will occur** on app start/resume.
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)**.
- **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.
**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.
**Web**
**Electron**
- Requires registered Service Worker + permission; can deliver with browser closed. **Web will not offline-schedule**.
- Service worker click handlers apply to **web push only**; local notifications on mobile do **not** pass through 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
- 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).
app is running**; true background scheduling will be addressed in v2 (native bridges).
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):
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.
**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.
**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.
**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.
**UX notes:** On Windows we **will set**`appUserModelId` so toasts are attributed correctly; on macOS we **will request** notification permission on first use.
**UX notes:** On Windows we **will set**`appUserModelId` so toasts are
attributed correctly; on macOS we **will request** notification permission on
first use.
**Prerequisites:** We **will require** Node 18+ (global `fetch`) or we **will polyfill** via `undici` for content fetching in the main process.
**Prerequisites:** We **will require** Node 18+ (global `fetch`) or we
**will polyfill** via `undici` for content fetching in the main process.
**Limits:** Title ≤ 50 chars; Body ≤ 200 chars; truncate with ellipsis.
**Escaping:** HTML-escape all injected values.
**Fallback:** Missing token → `"[Content]"`.
**Limits:** Title ≤ 50 chars; Body ≤ 200 chars; truncate with ellipsis.
**Mutation:** We **will** render templates **before** scheduling; no mutation at delivery time on iOS locals.
**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
## 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
**Database:** We **will integrate** with existing migration system in
**Settings:** We **will extend** existing Settings type in `src/db/tables/settings.ts` following the established type extension pattern
`src/db-sql/migration.ts` following the established `MIGRATIONS` array pattern
**Platform Service:** We **will leverage** existing PlatformServiceMixin database utilities following the established mixin pattern
**Settings:** We **will extend** existing Settings type in
**Service Factory:** We **will follow** the existing `PlatformServiceFactory` singleton pattern for notification service creation
`src/db/tables/settings.ts` following the established type extension pattern
**Capacitor:** We **will integrate** with existing deep link system in `src/main.capacitor.ts` following the established initialization pattern
**Platform Service:** We **will leverage** existing PlatformServiceMixin database
**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)
utilities following the established mixin pattern
**API:** We **will use** existing error handling from `src/services/api.ts` following the established `handleApiError` pattern
**Service Factory:** We **will follow** the existing `PlatformServiceFactory`
**Logging:** We **will use** existing logger from `src/utils/logger` following the established logging patterns
singleton pattern for notification service creation
**Capacitor:** We **will integrate** with existing deep link system in
**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)
`src/main.capacitor.ts` following the established initialization pattern
**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)
**Service Worker:** We **will extend** existing service worker infrastructure
**Identity System:** We **will integrate** with existing `did:ethr:` (Ethereum-based DID) system for user context
following the established `sw_scripts/` pattern (Note: Service workers are
**Testing:** We **will follow** Playwright E2E testing patterns established in TimeSafari
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:
**Database Architecture:** We **will support** platform-specific database backends:
- **Web**: Absurd SQL (SQLite via IndexedDB) via `WebPlatformService` with worker pattern
- **Web**: Absurd SQL (SQLite via IndexedDB) via `WebPlatformService` with worker pattern
- **Capacitor**: Native SQLite via `CapacitorPlatformService`
- **Capacitor**: Native SQLite via `CapacitorPlatformService`
- **Electron**: Native SQLite via `ElectronPlatformService` (extends CapacitorPlatformService)
- **Electron**: Native SQLite via `ElectronPlatformService` (extends CapacitorPlatformService)
- **Event queue (v1):** In-memory queue for `delivery`, `error`, `heartbeat` events. Background/native work **will enqueue**; foreground **will drain** and publish to the UI. **v2 will migrate** to SQLite-backed queue for persistence.
**Implementation:** See Implementation document for complete error taxonomy,
- **Callbacks (optional):**`ackDelivery`, `reportError`, `heartbeat`**will post** to server endpoints when configured.
event logging envelope, ACK payload format, and telemetry events.
- **Minimal metrics:** pending count, last fetch, last delivery, next occurrences.
*This strategic plan focuses on features and future‑tense deliverables, avoids implementation details, and preserves a clear path from the in‑app implementation (v1) to the native plugin (v2). For detailed implementation specifics, 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). For executive overview, see `notification-system-executive-summary.md`. For complete implementation details, see `notification-system-implementation.md`.*