wip_new_notifications #196
Open
anomalist
wants to merge 7 commits from wip_new_notifications
into master
33 changed files with 1234 additions and 201 deletions
@ -0,0 +1,96 @@ |
|||||
|
--- |
||||
|
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. |
@ -1,27 +1,56 @@ |
|||||
{ |
{ |
||||
"MD013": { |
"MD013": false, |
||||
"line_length": 80, |
"MD033": false, |
||||
"code_blocks": false, |
"MD041": false, |
||||
"tables": false, |
"MD024": { |
||||
"headings": false |
"siblings_only": true |
||||
|
}, |
||||
|
"MD029": { |
||||
|
"style": "ordered" |
||||
|
}, |
||||
|
"MD007": { |
||||
|
"indent": 2 |
||||
|
}, |
||||
|
"MD012": { |
||||
|
"maximum": 1 |
||||
}, |
}, |
||||
"MD012": true, |
|
||||
"MD022": true, |
"MD022": true, |
||||
|
"MD025": true, |
||||
|
"MD026": { |
||||
|
"punctuation": ".,;:!" |
||||
|
}, |
||||
|
"MD030": { |
||||
|
"ul_single": 1, |
||||
|
"ol_single": 1, |
||||
|
"ul_multi": 1, |
||||
|
"ol_multi": 1 |
||||
|
}, |
||||
"MD031": true, |
"MD031": true, |
||||
"MD032": true, |
"MD032": true, |
||||
"MD047": true, |
"MD034": true, |
||||
"MD009": true, |
"MD035": { |
||||
"MD010": true, |
"style": "---" |
||||
"MD004": { "style": "dash" }, |
}, |
||||
"MD029": { "style": "ordered" }, |
|
||||
"MD041": false, |
|
||||
"MD025": false, |
|
||||
"MD024": false, |
|
||||
"MD036": false, |
"MD036": false, |
||||
"MD003": false, |
"MD037": true, |
||||
"MD040": false, |
"MD038": true, |
||||
"MD055": false, |
"MD039": true, |
||||
"MD056": false, |
"MD040": true, |
||||
"MD034": false, |
"MD042": true, |
||||
"MD023": false |
"MD043": false, |
||||
|
"MD044": false, |
||||
|
"MD045": true, |
||||
|
"MD046": { |
||||
|
"style": "fenced" |
||||
|
}, |
||||
|
"MD047": true, |
||||
|
"MD048": { |
||||
|
"style": "backtick" |
||||
|
}, |
||||
|
"MD049": { |
||||
|
"style": "underscore" |
||||
|
}, |
||||
|
"MD050": { |
||||
|
"style": "asterisk" |
||||
|
} |
||||
} |
} |
@ -0,0 +1,21 @@ |
|||||
|
# Glossary |
||||
|
|
||||
|
**T (slot time)** — The local wall-clock time a notification should fire (e.g., 08:00). |
||||
|
|
||||
|
**T–lead** — The moment **`prefetchLeadMinutes`** before **T** when the system *attempts* a **single** background prefetch. T–lead **controls prefetch attempts, not arming**; locals are pre-armed earlier to guarantee closed-app delivery. |
||||
|
|
||||
|
**Rolling window** — Always keep **today's remaining** (and tomorrow if iOS pending caps allow) locals **armed** so the OS can deliver while the app is closed. |
||||
|
|
||||
|
**TTL (time-to-live)** — Maximum allowed payload age **at fire time**. If `T − fetchedAt > ttlSeconds`, we **skip** arming for that T. |
||||
|
|
||||
|
**Shared DB (default)** — The app and plugin open the **same SQLite file**; the app owns schema/migrations, the plugin performs short writes with WAL. |
||||
|
|
||||
|
**WAL (Write-Ahead Logging)** — SQLite journaling mode that permits concurrent reads during writes; recommended for foreground-read + background-write. |
||||
|
|
||||
|
**`PRAGMA user_version`** — An integer the app increments on each migration; the plugin **checks** (does not migrate) to ensure compatibility. |
||||
|
|
||||
|
**Exact alarm (Android)** — Minute-precise alarm via `AlarmManager.setExactAndAllowWhileIdle`, subject to policy and permission. |
||||
|
|
||||
|
**Windowed alarm (Android)** — Batched/inexact alarm via `setWindow(start,len)`; we target **±10 minutes** when exact alarms are unavailable. |
||||
|
|
||||
|
**Start-on-Login** — Electron feature that automatically launches the application when the user logs into their system, enabling background notification scheduling and delivery after system reboot. |
@ -1,77 +1,11 @@ |
|||||
# TimeSafari Docs |
# TimeSafari — Native-First Notification System (Clean Pack) — 2025-09-07 |
||||
|
|
||||
## Generating PDF from Markdown on OSx |
This pack contains a single-version **Native-First** documentation set with a clear definition of **T–lead** and aligned terminology. |
||||
|
|
||||
This uses Pandoc and BasicTex (LaTeX) Installed through Homebrew. |
**Native-First =** OS-scheduled **background prefetch at T–lead** + **pre-armed one-shot local notifications**. Web-push is retired. |
||||
|
|
||||
### Set Up |
**Included files** |
||||
|
|
||||
```bash |
- `notification-system.md` (merged comprehensive guide) |
||||
brew install pandoc |
- `web-push-cleanup-guide.md` (cleanup instructions) |
||||
|
- `GLOSSARY.md` (definitions incl. **T** and **T–lead**) |
||||
brew install basictex |
|
||||
|
|
||||
# Setting up LaTex packages |
|
||||
|
|
||||
# First update tlmgr |
|
||||
sudo tlmgr update --self |
|
||||
|
|
||||
# Then install LaTex packages |
|
||||
sudo tlmgr install bbding |
|
||||
sudo tlmgr install enumitem |
|
||||
sudo tlmgr install environ |
|
||||
sudo tlmgr install fancyhdr |
|
||||
sudo tlmgr install framed |
|
||||
sudo tlmgr install import |
|
||||
sudo tlmgr install lastpage # Enables Page X of Y |
|
||||
sudo tlmgr install mdframed |
|
||||
sudo tlmgr install multirow |
|
||||
sudo tlmgr install needspace |
|
||||
sudo tlmgr install ntheorem |
|
||||
sudo tlmgr install tabu |
|
||||
sudo tlmgr install tcolorbox |
|
||||
sudo tlmgr install textpos |
|
||||
sudo tlmgr install titlesec |
|
||||
sudo tlmgr install titling # Required for the fancy headers used |
|
||||
sudo tlmgr install threeparttable |
|
||||
sudo tlmgr install trimspaces |
|
||||
sudo tlmgr install tocloft # Required for \tableofcontents generation |
|
||||
sudo tlmgr install varwidth |
|
||||
sudo tlmgr install wrapfig |
|
||||
|
|
||||
# Install fonts |
|
||||
sudo tlmgr install cmbright |
|
||||
sudo tlmgr install collection-fontsrecommended # And set up fonts |
|
||||
sudo tlmgr install fira |
|
||||
sudo tlmgr install fontaxes |
|
||||
sudo tlmgr install libertine # The main font the doc uses |
|
||||
sudo tlmgr install opensans |
|
||||
sudo tlmgr install sourceserifpro |
|
||||
|
|
||||
``` |
|
||||
|
|
||||
#### References |
|
||||
|
|
||||
The following guide was adapted to this project except that we install with Brew and have a few more packages. |
|
||||
|
|
||||
Guide: <https://daniel.feldroy.com/posts/setting-up-latex-on-mac-os-x> |
|
||||
|
|
||||
### Usage |
|
||||
|
|
||||
Use the `pandoc` command to generate a PDF. |
|
||||
|
|
||||
```bash |
|
||||
pandoc usage-guide.md -o usage-guide.pdf |
|
||||
``` |
|
||||
|
|
||||
And you can open the PDF with the `open` command. |
|
||||
|
|
||||
```bash |
|
||||
open usage-guide.pdf |
|
||||
``` |
|
||||
|
|
||||
Or use this one-liner |
|
||||
|
|
||||
```bash |
|
||||
pandoc usage-guide.md -o usage-guide.pdf && open usage-guide.pdf |
|
||||
``` |
|
||||
|
@ -0,0 +1,231 @@ |
|||||
|
# TimeSafari — Native-First Notification System |
||||
|
|
||||
|
**Status:** Ready for implementation |
||||
|
**Date:** 2025-09-07 |
||||
|
**Author:** Matthew Raymer |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## Executive Summary |
||||
|
|
||||
|
Ship a **single, Native-First** notification system: OS-scheduled **background prefetch at T–lead** + **pre-armed** local notifications. Web-push is retired. |
||||
|
|
||||
|
### What we deliver |
||||
|
|
||||
|
- **Closed-app delivery:** Pre-armed locals fire even if the app is closed. |
||||
|
- **Freshness:** One prefetch attempt per slot at **T–lead**; ETag/TTL controls; skip when stale. |
||||
|
- **Android precision:** Exact alarms with permission; windowed fallback (±10m) otherwise. |
||||
|
- **Resilience:** Re-arm after reboot/time-change (Android receivers; iOS on next wake/silent push). |
||||
|
- **Cross-platform:** Same TS API (iOS/Android/Electron). Electron is best-effort while running. |
||||
|
|
||||
|
### Success signals |
||||
|
|
||||
|
- High delivery reliability, minute-precision on Android with permission. |
||||
|
- Prefetch budget hit rate at **T–lead**; zero stale deliveries beyond TTL. |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## Strategic Plan |
||||
|
|
||||
|
### Goal |
||||
|
|
||||
|
Deliver 1..M daily notifications with **OS background prefetch at T–lead** and **rolling-window safety** so messages display with fresh content even when the app is closed. |
||||
|
|
||||
|
### Tenets |
||||
|
|
||||
|
- **Reliability first:** OS delivers once scheduled; no JS at delivery time. |
||||
|
- **Freshness with guardrails:** Prefetch at **T–lead**; enforce **TTL-at-fire**; ETag-aware. |
||||
|
- **Single system:** One TS API; native adapters swap under the hood. |
||||
|
- **Platform honesty:** Android exactness via permission; iOS best-effort budget. |
||||
|
|
||||
|
### Architecture (high level) |
||||
|
|
||||
|
App (Vue/TS) → Orchestrator (policy) → Native Adapters: |
||||
|
|
||||
|
- **SchedulerNative** — AlarmManager (Android) / UNUserNotificationCenter (iOS) |
||||
|
- **BackgroundPrefetchNative** — WorkManager (Android) / BGTaskScheduler (+ silent push) (iOS) |
||||
|
- **DataStore** — SQLite |
||||
|
|
||||
|
**Storage (single shared DB):** The app and the native plugin will use **the same SQLite database file**. The app owns schema/migrations; the plugin opens the same file with WAL enabled and performs short, serialized writes. This keeps one source of truth for payloads, delivery logs, and config. |
||||
|
|
||||
|
### SQLite Ownership & Concurrency |
||||
|
|
||||
|
* **One DB file:** The plugin opens the **same path** the app uses (no second DB). |
||||
|
* **Migrations owned by app:** The app executes schema migrations and bumps `PRAGMA user_version`. The plugin **never** migrates; it **asserts** the expected version. |
||||
|
* **WAL mode:** Open DB with `journal_mode=WAL`, `synchronous=NORMAL`, `busy_timeout=5000`, `foreign_keys=ON`. WAL allows foreground reads while a background job commits quickly. |
||||
|
* **Single-writer discipline:** Background jobs write in **short transactions** (UPSERT per slot), then return. |
||||
|
* **Encryption (optional):** If using SQLCipher, the **same key** is used by both app and plugin. Do not mix encrypted and unencrypted openings. |
||||
|
|
||||
|
### Scheduling & T–lead |
||||
|
|
||||
|
- **Arm** a rolling window (today + tomorrow within iOS cap). |
||||
|
- **Attempt** a single **online-first** fetch per slot at **T–lead = T − prefetchLeadMinutes**. |
||||
|
- If prefetch is skipped, the armed local **still fires** using cached content. |
||||
|
|
||||
|
### Policies |
||||
|
|
||||
|
- **TTL-at-fire:** If (T − fetchedAt) > `ttlSeconds` → **skip** arming. |
||||
|
- **Android exactness:** Request `SCHEDULE_EXACT_ALARM`; fallback **±10m** window. |
||||
|
- **Reboot/time change:** Android receivers re-arm next 24h; iOS on next wake/silent push. |
||||
|
- **No delivery-time mutation:** iOS locals cannot be mutated by NSE; render before scheduling. |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## Implementation Guide |
||||
|
|
||||
|
### 1) Interfaces (TS stable) |
||||
|
|
||||
|
- **SchedulerNative**: `scheduleExact({slotId, whenMs, title, body, extra})`, `scheduleWindow(..., windowLenMs)`, `cancelBySlot`, `rescheduleAll`, `capabilities()` |
||||
|
- **BackgroundPrefetchNative**: `schedulePrefetch(slotId, atMs)`, `cancelPrefetch(slotId)` |
||||
|
- **DataStore**: SQLite adapters (notif_contents, notif_deliveries, notif_config) |
||||
|
- **Public API**: `configure`, `requestPermissions`, `runFullPipelineNow`, `reschedule`, `getState` |
||||
|
|
||||
|
### DB Path & Adapter Configuration |
||||
|
|
||||
|
* **Configure option:** `dbPath: string` (absolute path or platform alias) is passed from JS to the plugin during `configure()`. |
||||
|
* **Shared tables:** |
||||
|
|
||||
|
* `notif_contents(slot_id, payload_json, fetched_at, etag, …)` |
||||
|
* `notif_deliveries(slot_id, fire_at, delivered_at, status, error_code, …)` |
||||
|
* `notif_config(k, v)` |
||||
|
* **Open settings:** |
||||
|
|
||||
|
* `journal_mode=WAL` |
||||
|
* `synchronous=NORMAL` |
||||
|
* `busy_timeout=5000` |
||||
|
* `foreign_keys=ON` |
||||
|
|
||||
|
**Type (TS) extension** |
||||
|
|
||||
|
```ts |
||||
|
export type ConfigureOptions = { |
||||
|
// …existing fields… |
||||
|
dbPath: string; // shared DB file the plugin will open |
||||
|
storage: 'shared'; // canonical value; plugin-owned DB is not used |
||||
|
}; |
||||
|
``` |
||||
|
|
||||
|
**Plugin side (pseudo)** |
||||
|
|
||||
|
```kotlin |
||||
|
// Android open |
||||
|
val db = SQLiteDatabase.openDatabase(dbPath, null, SQLiteDatabase.OPEN_READWRITE) |
||||
|
db.execSQL("PRAGMA journal_mode=WAL") |
||||
|
db.execSQL("PRAGMA synchronous=NORMAL") |
||||
|
db.execSQL("PRAGMA foreign_keys=ON") |
||||
|
db.execSQL("PRAGMA busy_timeout=5000") |
||||
|
// Verify schema version |
||||
|
val uv = rawQuery("PRAGMA user_version").use { it.moveToFirst(); it.getInt(0) } |
||||
|
require(uv >= MIN_EXPECTED_VERSION) { "Schema version too old" } |
||||
|
``` |
||||
|
|
||||
|
```swift |
||||
|
// iOS open (FMDB / SQLite3) |
||||
|
// Set WAL via PRAGMA after open; check user_version the same way. |
||||
|
``` |
||||
|
|
||||
|
### 2) Templating & Arming |
||||
|
|
||||
|
- Render `title/body` **before** scheduling; pass via **SchedulerNative**. |
||||
|
- Route all arming through **SchedulerNative** to centralize Android exact/window semantics. |
||||
|
|
||||
|
### 3) T–lead (single attempt) |
||||
|
|
||||
|
**T–lead governs prefetch, not arming.** We **arm** one-shot locals as part of the rolling window so closed-app delivery is guaranteed. At **T–lead = T − prefetchLeadMinutes**, the **native background job** attempts **one** 12s ETag-aware fetch. If fresh content arrives and will not violate **TTL-at-fire**, we (re)arm the upcoming slot; if the OS skips the wake, the pre-armed local still fires with cached content. |
||||
|
|
||||
|
- Compute T–lead = `whenMs - prefetchLeadMinutes*60_000`. |
||||
|
- `BackgroundPrefetchNative.schedulePrefetch(slotId, atMs=T–lead)`. |
||||
|
- On wake: **ETag** fetch (timeout **12s**), persist, optionally cancel & re-arm if within TTL. |
||||
|
- Never fetch at delivery time. |
||||
|
|
||||
|
### 4) TTL-at-fire |
||||
|
|
||||
|
**TTL-at-fire:** Before arming for time **T**, compute `T − fetchedAt`. If that exceeds `ttlSeconds`, **do not arm** (skip). This prevents posting stale notifications when the app has been closed for a long time. |
||||
|
|
||||
|
`if (whenMs - fetchedAt) > ttlSeconds*1000 → skip` |
||||
|
|
||||
|
### 5) Android specifics |
||||
|
|
||||
|
- Request `SCHEDULE_EXACT_ALARM`; deep-link if denied; fallback to `setWindow(start,len)` (±10m). |
||||
|
- Receivers: `BOOT_COMPLETED`, `TIMEZONE_CHANGED`, `TIME_SET` → recompute & re-arm for next 24h and schedule T–lead prefetch. |
||||
|
|
||||
|
### 6) iOS specifics |
||||
|
|
||||
|
- `BGTaskScheduler` for T–lead prefetch (best-effort). Optional silent push nudge. |
||||
|
- Locals: `UNCalendarNotificationTrigger` (one-shots); no NSE mutation for locals. |
||||
|
|
||||
|
### 7) Network & Timeouts |
||||
|
|
||||
|
- Content fetch: **12s** timeout; single attempt at T–lead; ETag/304 respected. |
||||
|
- ACK/Error: **8s** timeout, fire-and-forget. |
||||
|
|
||||
|
### 8) Electron |
||||
|
|
||||
|
- Notifications while app is running; recommend **Start-on-Login**. No true background scheduling when fully closed. |
||||
|
|
||||
|
### 9) Telemetry |
||||
|
|
||||
|
- Record `scheduled|shown|error`; ACK deliveries (8s timeout); include slot/times/TZ/app version. |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## Capability Matrix |
||||
|
|
||||
|
| Capability | Android (Native) | iOS (Native) | Electron | Web | |
||||
|
|---|---|---|---|---| |
||||
|
| Multi-daily locals (closed app) | ✅ | ✅ | ✅ (app running) | — | |
||||
|
| Prefetch at T–lead (app closed) | ✅ WorkManager | ⚠️ BGTask (best-effort) | ✅ (app running) | — | |
||||
|
| Re-arm after reboot/time-change | ✅ Receivers | ⚠️ On next wake/silent push | ✅ Start-on-Login | — | |
||||
|
| Minute-precision alarms | ✅ with exact permission | ❌ not guaranteed | ✅ timer best-effort | — | |
||||
|
| Delivery-time mutation for locals | ❌ | ❌ | — | — | |
||||
|
| ETag/TTL enforcement | ✅ | ✅ | ✅ | — | |
||||
|
| Rolling-window safety | ✅ | ✅ | ✅ | — | |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## Acceptance Criteria |
||||
|
|
||||
|
### Core |
||||
|
|
||||
|
- **Closed-app delivery:** Armed locals fire at T with last rendered content. No delivery-time network. |
||||
|
- **T–lead prefetch:** Single background attempt at **T–lead**; if skipped, delivery still occurs from cache. |
||||
|
- **TTL-at-fire:** No armed local violates TTL at T. |
||||
|
|
||||
|
### Android |
||||
|
|
||||
|
- **Exact permission path:** With `SCHEDULE_EXACT_ALARM` → within ±1m; else **±10m** window. |
||||
|
- **Reboot recovery:** After reboot, receivers re-arm next 24h and schedule T–lead prefetch. |
||||
|
- **TZ/DST change:** Recompute & re-arm; future slots align to new wall-clock. |
||||
|
|
||||
|
### iOS |
||||
|
|
||||
|
- **BGTask budget respected:** Prefetch often runs but may be skipped; delivery still occurs via rolling window. |
||||
|
- **Force-quit caveat:** No background execution after user terminate; delivery still occurs if pre-armed. |
||||
|
|
||||
|
### Electron |
||||
|
|
||||
|
- **Running-app rule:** Delivery only while app runs; with Start-on-Login, after reboot the orchestrator re-arms and subsequent slots deliver. |
||||
|
|
||||
|
### Network |
||||
|
|
||||
|
- Content fetch timeout **12s**; ACK/Error **8s**; no retries inside lead; ETag honored. |
||||
|
|
||||
|
### Observability |
||||
|
|
||||
|
- Log/telemetry for `scheduled|shown|error`; ACK payload includes slot, times, device TZ, app version. |
||||
|
|
||||
|
### DB Sharing |
||||
|
|
||||
|
* **Shared DB visibility:** A background prefetch writes `notif_contents`; the foreground UI **immediately** reads the same row. |
||||
|
* **WAL overlap:** With the app reading while the plugin commits, no user-visible blocking occurs. |
||||
|
* **Version safety:** If `user_version` is behind, the plugin emits an error and does not write (protects against partial installs). |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## Web-Push Cleanup |
||||
|
|
||||
|
Web-push functionality has been retired due to unreliability. All web-push related code paths and documentation sections should be removed or marked as deprecated. See `web-push-cleanup-guide.md` for detailed cleanup steps. |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
_This document consolidates the Native-First notification system strategy, implementation details, capabilities, and acceptance criteria into a single comprehensive reference._ |
@ -0,0 +1,551 @@ |
|||||
|
# TimeSafari Web-Push Cleanup Guide |
||||
|
|
||||
|
**Status:** 🚀 Native-First Implementation |
||||
|
**Date:** 2025-01-27T14:30Z (UTC) |
||||
|
**Author:** Matthew Raymer |
||||
|
**Scope:** Web-push code cleanup and deprecation |
||||
|
**Goal:** Remove or quarantine all web-push code paths and mark as deprecated. |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## Executive Summary |
||||
|
|
||||
|
This document provides a comprehensive cleanup guide for removing web-push code |
||||
|
paths from TimeSafari. Web-push has been retired for unreliability, and the |
||||
|
system now focuses on native mobile reliability with Electron best-effort support. |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## Cleanup Strategy |
||||
|
|
||||
|
### Phase 1: Identify Web-Push Code Paths |
||||
|
|
||||
|
#### Service Worker Files |
||||
|
|
||||
|
- [ ] `sw_scripts/notification-click.js` - Mark as deprecated |
||||
|
- [ ] `sw_scripts/` directory - Review for web-push dependencies |
||||
|
- [ ] Service worker registration code - Remove or quarantine |
||||
|
|
||||
|
#### Web-Specific Code |
||||
|
|
||||
|
- [ ] Web push notification handlers |
||||
|
- [ ] Service worker event listeners |
||||
|
- [ ] Web notification API usage |
||||
|
- [ ] Push subscription management |
||||
|
|
||||
|
#### Configuration Files |
||||
|
|
||||
|
- [ ] VitePWA plugin configuration |
||||
|
- [ ] Service worker build configuration |
||||
|
- [ ] Web push manifest files |
||||
|
|
||||
|
### Phase 2: Mark as Deprecated |
||||
|
|
||||
|
#### Code Comments |
||||
|
|
||||
|
```javascript |
||||
|
// DEPRECATED: Web-push notification handling |
||||
|
// This code is kept for reference but not used in production |
||||
|
// Replaced by Native-First notification system |
||||
|
``` |
||||
|
|
||||
|
#### Documentation Updates |
||||
|
|
||||
|
- [ ] Mark web-push sections as deprecated |
||||
|
- [ ] Add deprecation notices |
||||
|
- [ ] Update README files |
||||
|
- [ ] Update API documentation |
||||
|
|
||||
|
### Phase 3: Remove or Quarantine |
||||
|
|
||||
|
#### Complete Removal |
||||
|
|
||||
|
- [ ] Web push subscription code |
||||
|
- [ ] Service worker notification handlers |
||||
|
- [ ] Web-specific notification APIs |
||||
|
- [ ] Push message handling |
||||
|
|
||||
|
#### Quarantine (Keep for Reference) |
||||
|
|
||||
|
- [ ] Service worker registration code |
||||
|
- [ ] Web push configuration |
||||
|
- [ ] Historical web-push tests |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## Detailed Cleanup Tasks |
||||
|
|
||||
|
### 1. Service Worker Cleanup |
||||
|
|
||||
|
#### Files to Deprecate |
||||
|
|
||||
|
**`sw_scripts/notification-click.js`** |
||||
|
|
||||
|
```javascript |
||||
|
// DEPRECATED: Service worker notification handling |
||||
|
// This code is kept for reference but not used in production |
||||
|
// Replaced by Native-First notification system |
||||
|
|
||||
|
// Original web-push notification click handler |
||||
|
self.addEventListener('notificationclick', (event) => { |
||||
|
// DEPRECATED: Web-push only |
||||
|
event.notification.close(); |
||||
|
|
||||
|
const slotId = event.notification.data?.slotId; |
||||
|
const route = slotId ? '/#/daily' : '/#/notifications'; |
||||
|
|
||||
|
event.waitUntil( |
||||
|
clients.openWindow(route).catch(() => { |
||||
|
return clients.openWindow('/'); |
||||
|
}) |
||||
|
); |
||||
|
}); |
||||
|
``` |
||||
|
|
||||
|
**Service Worker Registration** |
||||
|
|
||||
|
```javascript |
||||
|
// DEPRECATED: Service worker registration |
||||
|
// This code is kept for reference but not used in production |
||||
|
// Replaced by Native-First notification system |
||||
|
|
||||
|
if ('serviceWorker' in navigator && process.env.VITE_PLATFORM === 'web') { |
||||
|
// DEPRECATED: Web-push only |
||||
|
navigator.serviceWorker.register('/sw.js') |
||||
|
.then(registration => { |
||||
|
console.log('Service Worker registered:', registration); |
||||
|
}) |
||||
|
.catch(error => { |
||||
|
console.error('Service Worker registration failed:', error); |
||||
|
}); |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
### 2. Web Push API Cleanup |
||||
|
|
||||
|
#### Push Subscription Management |
||||
|
|
||||
|
```javascript |
||||
|
// DEPRECATED: Web push subscription management |
||||
|
// This code is kept for reference but not used in production |
||||
|
// Replaced by Native-First notification system |
||||
|
|
||||
|
class WebPushManager { |
||||
|
// DEPRECATED: Web-push only |
||||
|
async subscribeToPush() { |
||||
|
// Implementation kept for reference |
||||
|
} |
||||
|
|
||||
|
// DEPRECATED: Web-push only |
||||
|
async unsubscribeFromPush() { |
||||
|
// Implementation kept for reference |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
#### Push Message Handling |
||||
|
|
||||
|
```javascript |
||||
|
// DEPRECATED: Push message handling |
||||
|
// This code is kept for reference but not used in production |
||||
|
// Replaced by Native-First notification system |
||||
|
|
||||
|
self.addEventListener('push', (event) => { |
||||
|
// DEPRECATED: Web-push only |
||||
|
const data = event.data ? event.data.json() : {}; |
||||
|
|
||||
|
const options = { |
||||
|
body: data.body, |
||||
|
icon: '/icon-192x192.png', |
||||
|
badge: '/badge-72x72.png', |
||||
|
data: data |
||||
|
}; |
||||
|
|
||||
|
event.waitUntil( |
||||
|
self.registration.showNotification(data.title, options) |
||||
|
); |
||||
|
}); |
||||
|
``` |
||||
|
|
||||
|
### 3. Configuration Cleanup |
||||
|
|
||||
|
#### VitePWA Plugin Configuration |
||||
|
|
||||
|
```javascript |
||||
|
// DEPRECATED: VitePWA plugin configuration |
||||
|
// This configuration is kept for reference but not used in production |
||||
|
// Replaced by Native-First notification system |
||||
|
|
||||
|
import { VitePWA } from 'vite-plugin-pwa' |
||||
|
|
||||
|
export default defineConfig({ |
||||
|
plugins: [ |
||||
|
VitePWA({ |
||||
|
// DEPRECATED: Web-push only |
||||
|
registerType: 'autoUpdate', |
||||
|
workbox: { |
||||
|
globPatterns: ['**/*.{js,css,html,ico,png,svg}'] |
||||
|
}, |
||||
|
includeAssets: ['favicon.ico', 'apple-touch-icon.png', 'masked-icon.svg'], |
||||
|
manifest: { |
||||
|
name: 'TimeSafari', |
||||
|
short_name: 'TimeSafari', |
||||
|
description: 'TimeSafari App', |
||||
|
theme_color: '#ffffff', |
||||
|
icons: [ |
||||
|
{ |
||||
|
src: 'pwa-192x192.png', |
||||
|
sizes: '192x192', |
||||
|
type: 'image/png' |
||||
|
} |
||||
|
] |
||||
|
} |
||||
|
}) |
||||
|
] |
||||
|
}) |
||||
|
``` |
||||
|
|
||||
|
#### Service Worker Build Configuration |
||||
|
|
||||
|
```javascript |
||||
|
// DEPRECATED: Service worker build configuration |
||||
|
// This configuration is kept for reference but not used in production |
||||
|
// Replaced by Native-First notification system |
||||
|
|
||||
|
export default defineConfig({ |
||||
|
build: { |
||||
|
rollupOptions: { |
||||
|
input: { |
||||
|
// DEPRECATED: Web-push only |
||||
|
sw: 'sw_scripts/notification-click.js' |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}) |
||||
|
``` |
||||
|
|
||||
|
### 4. Test Cleanup |
||||
|
|
||||
|
#### Web Push Tests |
||||
|
|
||||
|
```javascript |
||||
|
// DEPRECATED: Web push tests |
||||
|
// These tests are kept for reference but not used in production |
||||
|
// Replaced by Native-First notification system |
||||
|
|
||||
|
describe('Web Push Notifications (DEPRECATED)', () => { |
||||
|
// DEPRECATED: Web-push only |
||||
|
it('should handle push notifications', async () => { |
||||
|
// Test implementation kept for reference |
||||
|
}); |
||||
|
|
||||
|
// DEPRECATED: Web-push only |
||||
|
it('should handle notification clicks', async () => { |
||||
|
// Test implementation kept for reference |
||||
|
}); |
||||
|
}); |
||||
|
``` |
||||
|
|
||||
|
#### Service Worker Tests |
||||
|
|
||||
|
```javascript |
||||
|
// DEPRECATED: Service worker tests |
||||
|
// These tests are kept for reference but not used in production |
||||
|
// Replaced by Native-First notification system |
||||
|
|
||||
|
describe('Service Worker (DEPRECATED)', () => { |
||||
|
// DEPRECATED: Web-push only |
||||
|
it('should register service worker', async () => { |
||||
|
// Test implementation kept for reference |
||||
|
}); |
||||
|
|
||||
|
// DEPRECATED: Web-push only |
||||
|
it('should handle push events', async () => { |
||||
|
// Test implementation kept for reference |
||||
|
}); |
||||
|
}); |
||||
|
``` |
||||
|
|
||||
|
### 5. Documentation Cleanup |
||||
|
|
||||
|
#### README Updates |
||||
|
|
||||
|
```markdown |
||||
|
# TimeSafari Native-First Notification System |
||||
|
|
||||
|
## Web-Push Status: DEPRECATED |
||||
|
|
||||
|
Web-push has been retired for unreliability. The system now focuses on native mobile reliability with Electron best-effort support. |
||||
|
|
||||
|
### Deprecated Features |
||||
|
- ❌ Web push notifications |
||||
|
- ❌ Service worker notification handling |
||||
|
- ❌ Web notification API |
||||
|
|
||||
|
### Active Features |
||||
|
- ✅ Native mobile notifications (Android/iOS) |
||||
|
- ✅ Electron notifications (best-effort) |
||||
|
- ✅ OS-scheduled background prefetch |
||||
|
- ✅ Rolling window safety |
||||
|
``` |
||||
|
|
||||
|
#### API Documentation Updates |
||||
|
|
||||
|
```markdown |
||||
|
## Notification API (Native-First) |
||||
|
|
||||
|
### Deprecated Methods |
||||
|
- `subscribeToPush()` - DEPRECATED: Web-push only |
||||
|
- `unsubscribeFromPush()` - DEPRECATED: Web-push only |
||||
|
- `handlePushMessage()` - DEPRECATED: Web-push only |
||||
|
|
||||
|
### Active Methods |
||||
|
- `scheduleExact()` - Native exact scheduling |
||||
|
- `scheduleWindow()` - Native windowed scheduling |
||||
|
- `schedulePrefetch()` - Native background prefetch |
||||
|
``` |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## File-by-File Cleanup Checklist |
||||
|
|
||||
|
### Service Worker Files |
||||
|
|
||||
|
- [ ] `sw_scripts/notification-click.js` - Mark as deprecated |
||||
|
- [ ] `sw_scripts/` directory - Review for web-push dependencies |
||||
|
- [ ] Service worker build configuration - Remove or quarantine |
||||
|
|
||||
|
### Web-Specific Code |
||||
|
|
||||
|
- [ ] `src/main.web.ts` - Remove service worker registration |
||||
|
- [ ] `src/services/webPush.ts` - Mark as deprecated |
||||
|
- [ ] `src/utils/serviceWorker.ts` - Mark as deprecated |
||||
|
- [ ] Web notification API usage - Remove or quarantine |
||||
|
|
||||
|
### Configuration Files |
||||
|
|
||||
|
- [ ] `vite.config.web.mts` - Remove VitePWA plugin |
||||
|
- [ ] `package.json` - Remove web-push dependencies |
||||
|
- [ ] `public/manifest.json` - Mark as deprecated |
||||
|
- [ ] Service worker build scripts - Remove or quarantine |
||||
|
|
||||
|
### Test Files |
||||
|
|
||||
|
- [ ] `test-playwright/web-push.spec.ts` - Mark as deprecated |
||||
|
- [ ] `test/services/webPush.test.ts` - Mark as deprecated |
||||
|
- [ ] Service worker tests - Mark as deprecated |
||||
|
|
||||
|
### Documentation Files |
||||
|
|
||||
|
- [ ] `README.md` - Update to reflect native-first approach |
||||
|
- [ ] `doc/web-push.md` - Mark as deprecated |
||||
|
- [ ] API documentation - Remove web-push references |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## Dependencies to Remove |
||||
|
|
||||
|
### NPM Packages |
||||
|
|
||||
|
```json |
||||
|
{ |
||||
|
"dependencies": { |
||||
|
// DEPRECATED: Web-push only |
||||
|
"web-push": "^7.4.0", |
||||
|
"vite-plugin-pwa": "^0.17.0" |
||||
|
}, |
||||
|
"devDependencies": { |
||||
|
// DEPRECATED: Web-push only |
||||
|
"workbox-webpack-plugin": "^6.5.0" |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
### Build Scripts |
||||
|
|
||||
|
```json |
||||
|
{ |
||||
|
"scripts": { |
||||
|
// DEPRECATED: Web-push only |
||||
|
"build:sw": "workbox generateSW", |
||||
|
"test:sw": "jest --testPathPattern=serviceWorker" |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## Migration Guide |
||||
|
|
||||
|
### From Web-Push to Native-First |
||||
|
|
||||
|
#### Step 1: Remove Web-Push Dependencies |
||||
|
|
||||
|
```bash |
||||
|
# Remove web-push packages |
||||
|
npm uninstall web-push vite-plugin-pwa workbox-webpack-plugin |
||||
|
|
||||
|
# Remove service worker files |
||||
|
rm -rf sw_scripts/ |
||||
|
rm -f public/sw.js |
||||
|
rm -f public/workbox-*.js |
||||
|
``` |
||||
|
|
||||
|
#### Step 2: Update Configuration |
||||
|
|
||||
|
```javascript |
||||
|
// Remove VitePWA plugin from vite.config.web.mts |
||||
|
export default defineConfig({ |
||||
|
plugins: [ |
||||
|
// Remove VitePWA plugin |
||||
|
// VitePWA({ ... }) |
||||
|
] |
||||
|
}) |
||||
|
``` |
||||
|
|
||||
|
#### Step 3: Update Service Registration |
||||
|
|
||||
|
```javascript |
||||
|
// Remove service worker registration from main.web.ts |
||||
|
// if ('serviceWorker' in navigator) { |
||||
|
// navigator.serviceWorker.register('/sw.js') |
||||
|
// } |
||||
|
``` |
||||
|
|
||||
|
#### Step 4: Update Tests |
||||
|
|
||||
|
```javascript |
||||
|
// Remove web-push tests |
||||
|
// describe('Web Push Notifications', () => { ... }) |
||||
|
``` |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## Verification Checklist |
||||
|
|
||||
|
### Code Removal Verification |
||||
|
|
||||
|
- [ ] No web-push imports remain |
||||
|
- [ ] No service worker registration code |
||||
|
- [ ] No push subscription management |
||||
|
- [ ] No web notification API usage |
||||
|
- [ ] No VitePWA plugin configuration |
||||
|
|
||||
|
### Documentation Verification |
||||
|
|
||||
|
- [ ] All web-push references marked as deprecated |
||||
|
- [ ] README updated to reflect native-first approach |
||||
|
- [ ] API documentation updated |
||||
|
- [ ] Test documentation updated |
||||
|
|
||||
|
### Build Verification |
||||
|
|
||||
|
- [ ] Web build succeeds without service worker |
||||
|
- [ ] No service worker files generated |
||||
|
- [ ] No web-push dependencies in bundle |
||||
|
- [ ] Native builds work correctly |
||||
|
|
||||
|
### Test Verification |
||||
|
|
||||
|
- [ ] Web-push tests are marked as deprecated |
||||
|
- [ ] Native notification tests pass |
||||
|
- [ ] No web-push test failures |
||||
|
- [ ] Test suite runs successfully |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## Rollback Plan |
||||
|
|
||||
|
### Emergency Rollback |
||||
|
|
||||
|
If native-first implementation fails, web-push code can be restored: |
||||
|
|
||||
|
#### 1. **Restore Dependencies** |
||||
|
|
||||
|
```bash |
||||
|
npm install web-push vite-plugin-pwa workbox-webpack-plugin |
||||
|
``` |
||||
|
|
||||
|
#### 2. **Restore Service Worker Files** |
||||
|
|
||||
|
```bash |
||||
|
git checkout HEAD~1 -- sw_scripts/ |
||||
|
git checkout HEAD~1 -- public/sw.js |
||||
|
``` |
||||
|
|
||||
|
#### 3. **Restore Configuration** |
||||
|
|
||||
|
```bash |
||||
|
git checkout HEAD~1 -- vite.config.web.mts |
||||
|
git checkout HEAD~1 -- package.json |
||||
|
``` |
||||
|
|
||||
|
#### 4. **Restore Tests** |
||||
|
|
||||
|
```bash |
||||
|
git checkout HEAD~1 -- test-playwright/web-push.spec.ts |
||||
|
git checkout HEAD~1 -- test/services/webPush.test.ts |
||||
|
``` |
||||
|
|
||||
|
### Rollback Verification |
||||
|
|
||||
|
- [ ] Web-push functionality restored |
||||
|
- [ ] Service worker registration works |
||||
|
- [ ] Push notifications work |
||||
|
- [ ] Tests pass |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## Post-Cleanup Tasks |
||||
|
|
||||
|
### Code Review |
||||
|
|
||||
|
- [ ] Review all changes for completeness |
||||
|
- [ ] Verify no web-push code remains |
||||
|
- [ ] Check for orphaned references |
||||
|
- [ ] Validate native-first implementation |
||||
|
|
||||
|
### Testing |
||||
|
|
||||
|
- [ ] Run full test suite |
||||
|
- [ ] Verify native notifications work |
||||
|
- [ ] Check Electron functionality |
||||
|
- [ ] Validate mobile builds |
||||
|
|
||||
|
### Documentation |
||||
|
|
||||
|
- [ ] Update all documentation |
||||
|
- [ ] Remove web-push references |
||||
|
- [ ] Update API documentation |
||||
|
- [ ] Update user guides |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## Success Criteria |
||||
|
|
||||
|
### Complete Web-Push Removal |
||||
|
|
||||
|
- [ ] All web-push code marked as deprecated |
||||
|
- [ ] Service worker files quarantined |
||||
|
- [ ] Dependencies removed |
||||
|
- [ ] Configuration updated |
||||
|
|
||||
|
### Native-First Implementation |
||||
|
|
||||
|
- [ ] Native notifications work on Android |
||||
|
- [ ] Native notifications work on iOS |
||||
|
- [ ] Electron notifications work |
||||
|
- [ ] Background prefetch works |
||||
|
|
||||
|
### Documentation Updated |
||||
|
|
||||
|
- [ ] All docs reflect native-first approach |
||||
|
- [ ] Web-push marked as deprecated |
||||
|
- [ ] Migration guide provided |
||||
|
- [ ] Rollback plan documented |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
_This cleanup guide provides comprehensive instructions for removing web-push |
||||
|
code paths from TimeSafari. Web-push has been retired for unreliability, and the |
||||
|
system now focuses on native mobile reliability with Electron best-effort support._ |
Loading…
Reference in new issue