wip: phase 5 web-push paused after discuss (ready to plan)
This commit is contained in:
+17
-18
@@ -1,35 +1,34 @@
|
|||||||
{
|
{
|
||||||
"version": "1.0",
|
"version": "1.0",
|
||||||
"timestamp": "2026-06-08T01:52:24.365Z",
|
"timestamp": "2026-06-09T19:14:03.727Z",
|
||||||
"phase": "4",
|
"phase": "5",
|
||||||
"phase_name": "Shared Lists + Live Sync",
|
"phase_name": "Web Push Notifications",
|
||||||
"phase_dir": ".planning/phases/04-shared-lists-live-sync",
|
"phase_dir": ".planning/phases/05-web-push-notifications",
|
||||||
"plan": null,
|
"plan": null,
|
||||||
"task": null,
|
"task": null,
|
||||||
"total_tasks": null,
|
"total_tasks": null,
|
||||||
"status": "paused",
|
"status": "paused",
|
||||||
"stage": "discuss-complete; entry-gate-cleared; ready-to-plan",
|
"stage": "discuss-complete; ready-to-plan",
|
||||||
"completed_tasks": [
|
"completed_tasks": [
|
||||||
{"id": 1, "name": "discuss-phase 4 → 04-CONTEXT.md + 04-DISCUSSION-LOG.md (18 decisions D-01..D-18)", "status": "done", "commit": "05e1c9e"},
|
{"id": 1, "name": "discuss-phase 5 → 05-CONTEXT.md + 05-DISCUSSION-LOG.md (D-01..D-14, 3 areas discussed)", "status": "done", "commit": "e74f24d"},
|
||||||
{"id": 2, "name": "Ran SSE-over-Pangolin smoke test live — PASS (~6 min, 35 heartbeats, buffering off, no cut)", "status": "done"},
|
{"id": 2, "name": "Recorded STATE.md session (phase 5 context gathered)", "status": "done", "commit": "3bbfbbc"}
|
||||||
{"id": 3, "name": "Recorded gate PASS via quick task 260607-u8o (01-HUMAN-UAT item 4 + 03-GATE2-RESULTS Part C)", "status": "done", "commit": "9ee5906"},
|
|
||||||
{"id": 4, "name": "Saved project memory: N-member expansion principle", "status": "done"}
|
|
||||||
],
|
],
|
||||||
"remaining_tasks": [
|
"remaining_tasks": [
|
||||||
{"id": 5, "name": "Plan Phase 4: /gsd-plan-phase 4 (reads 04-CONTEXT.md)", "status": "not_started"},
|
{"id": 3, "name": "Optional: /gsd-ui-phase 5 — UI design contract (permission prompt, settings toggle, notification flow)", "status": "not_started"},
|
||||||
{"id": 6, "name": "Optionally /gsd-ui-phase 4 for the lists UI design contract (UI hint: yes)", "status": "not_started"},
|
{"id": 4, "name": "/gsd-plan-phase 5 — research + plan (reads 05-CONTEXT.md → RESEARCH.md, PLAN.md files)", "status": "not_started"},
|
||||||
{"id": 7, "name": "Execute Phase 4 plans", "status": "not_started"}
|
{"id": 5, "name": "Execute Phase 5 plans", "status": "not_started"}
|
||||||
],
|
],
|
||||||
"blockers": [],
|
"blockers": [],
|
||||||
"human_actions_pending": [
|
"human_actions_pending": [
|
||||||
{"action": "Log out / invalidate the Authelia session whose oidc-auth cookie was pasted into the transcript during the SSE test", "context": "Cookie embeds a refresh token valid until ~2026-06-08; it is now in conversation history. Non-blocking but a security hygiene follow-up.", "blocking": false}
|
{"action": "Create + share the shared Family calendar and mark is_shared=1 on the primary Fastmail account", "context": "Phase 2 D-16 deferred. Reminders (NOTIF-01) fire ONLY on shared-calendar events (D-05), so live verification of reminders needs a real shared calendar with events. Implementation/planning is not blocked — the reminder path handles the empty-shared-calendar case correctly.", "blocking": false}
|
||||||
],
|
],
|
||||||
"decisions": [
|
"decisions": [
|
||||||
{"decision": "Phase 4 entry gate (SSE-over-Pangolin, D-14/#1034) CLEARED", "rationale": "Live smoke test held a heartbeat stream open ~6 min through Pangolin with incremental delivery and no cut; buffering confirmed off", "phase": "4"},
|
{"decision": "Reminders fire for SHARED Family-calendar events ONLY (not personal)", "rationale": "Native device calendar apps already remind for personal events; FamilySync must not duplicate. Deliberately narrows literal NOTIF-01 — single most consequential decision; revisit if the household expects all-event reminders.", "phase": "5"},
|
||||||
{"decision": "All Phase 4 implementation decisions captured in 04-CONTEXT.md (D-01..D-18)", "rationale": "Discuss-phase complete; downstream planner/researcher consume CONTEXT.md", "phase": "4"},
|
{"decision": "Coalesce list-change pushes per list; events specific / list pings generic; name the actor; meaningful event-changes only (description-only edits silent)", "rationale": "Anti-spam for the chatty list source; clear attribution for a two-person household.", "phase": "5"},
|
||||||
{"decision": "Design member-count-agnostic (N family members, not hard-coded 2)", "rationale": "User stated future expansion; drives list_shares join table + per-list-scoped SSE fan-out", "phase": "4"}
|
{"decision": "Contextual permission prompt after install; single master toggle (v1); silent auto re-subscribe on dead subscription", "rationale": "Low-friction for the non-technical member; per-category opt-out deferred to v1.x.", "phase": "5"},
|
||||||
|
{"decision": "Fixed 15-min lead time; no all-day reminders", "rationale": "MVP simplicity; custom/per-event lead time is v1.x.", "phase": "5"}
|
||||||
],
|
],
|
||||||
"uncommitted_files": [],
|
"uncommitted_files": [],
|
||||||
"next_action": "/clear then /gsd-plan-phase 4 (entry gate is cleared; no infra precondition remains)",
|
"next_action": "/clear then /gsd-plan-phase 5 (CONTEXT.md is the source of truth; entry gates from Ph3/Ph4 already cleared).",
|
||||||
"context_notes": "Phase 4 = app-native shared lists (MariaDB, NOT CalDAV) with SSE live co-edit sync. Discussion is done and committed; the SSE transport gate that was blocking the build is now verified and recorded. Nothing is mid-edit — clean stopping point between discuss and plan. Key locked choices for the planner: default-shared lists + private toggle via list_shares join table; checked items sink to completed section; optimistic UI + per-field PATCH last-write-wins (no CRDT); delete-wins; full-refetch-on-reconnect + capped-backoff + polling fallback; string fractional-index ordering; bottom tab bar + react-router. Open for planner: in-memory EventEmitter vs Redis pub/sub (single Node process today, ioredis not installed); new tables must use drizzle generate+migrate, NOT push."
|
"context_notes": "Just finished discuss-phase for Phase 5 (Web Push, MVP mode, depends on Ph3+Ph4). 05-CONTEXT.md + 05-DISCUSSION-LOG.md written and committed; clean stopping point between discuss and plan, nothing mid-edit. Most consequential locked choice: D-05 shared-only reminder scope (deliberate NOTIF-01 narrowing — flagged for planner + verification). For the planner: the new push-subscription table MUST use drizzle generate+migrate (db:push unsafe on populated MariaDB); push dispatch reuses the existing in-memory listEmitter publish points (apps/api/src/lib/listEmitter.ts) + poller/outboxWorker change-detection; web-push and ioredis are NOT installed (VAPID keys need generating, no Redis — single Node process); SW is vite-plugin-pwa generateSW + autoUpdate so the push/notificationclick handler likely needs an injectManifest switch; iOS subscription health-check + event.waitUntil() + visible-notification-on-every-push are mandatory from day one (STATE.md)."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
---
|
||||||
|
context: phase
|
||||||
|
phase: 05-web-push-notifications
|
||||||
|
task: null
|
||||||
|
total_tasks: null
|
||||||
|
status: paused
|
||||||
|
last_updated: 2026-06-09T19:14:03.727Z
|
||||||
|
---
|
||||||
|
|
||||||
|
<current_state>
|
||||||
|
Phase 5 (Web Push Notifications, MVP mode, depends on Phase 3 + Phase 4) just
|
||||||
|
completed **discuss-phase**. `05-CONTEXT.md` and `05-DISCUSSION-LOG.md` are written
|
||||||
|
and committed (e74f24d); STATE.md session recorded (3bbfbbc). Working tree clean.
|
||||||
|
|
||||||
|
This is a clean stop **between discuss and plan** — no plans/tasks exist yet,
|
||||||
|
nothing is mid-edit. Resume by planning the phase.
|
||||||
|
</current_state>
|
||||||
|
|
||||||
|
<completed_work>
|
||||||
|
|
||||||
|
- Task 1: discuss-phase 5 → `05-CONTEXT.md` + `05-DISCUSSION-LOG.md` (decisions D-01..D-14). Done (e74f24d).
|
||||||
|
- Task 2: STATE.md session recorded. Done (3bbfbbc).
|
||||||
|
</completed_work>
|
||||||
|
|
||||||
|
<remaining_work>
|
||||||
|
|
||||||
|
- Task 3 (optional): `/gsd-ui-phase 5` — UI design contract for the permission prompt, settings master toggle, and notification flow. This phase has real frontend surface.
|
||||||
|
- Task 4: `/gsd-plan-phase 5` — research + plan (reads `05-CONTEXT.md`).
|
||||||
|
- Task 5: execute the plans.
|
||||||
|
</remaining_work>
|
||||||
|
|
||||||
|
<decisions_made>
|
||||||
|
|
||||||
|
- **D-05 (most consequential): reminders fire for the SHARED Family calendar ONLY** — not personal events. Deliberate, because each member's native device calendar app already reminds for personal events and FamilySync must not duplicate. This narrows a literal reading of NOTIF-01; flagged for planner + verification.
|
||||||
|
- Coalesce list-change pushes per list; events show specifics, list pings stay generic (actor + list + count, no item text); name the actor; only meaningful event-changes push (description-only edits stay silent).
|
||||||
|
- Contextual permission prompt right after install; single master on/off toggle (v1); silent auto re-subscribe when a subscription dies but OS permission is still granted.
|
||||||
|
- Fixed 15-min lead; no all-day reminders.
|
||||||
|
</decisions_made>
|
||||||
|
|
||||||
|
<blockers>
|
||||||
|
- None blocking planning/implementation. (Human action — non-blocking: the shared "Family" calendar must be created + shared + `is_shared=1` per Phase 2 D-16 before reminders have real events to fire on for live verification.)
|
||||||
|
</blockers>
|
||||||
|
|
||||||
|
## Required Reading (in order)
|
||||||
|
1. `.planning/phases/05-web-push-notifications/05-CONTEXT.md` — the source of truth; decisions, canonical refs, code-context, deferred items.
|
||||||
|
2. `CLAUDE.md` §"React PWA Stack" — iOS push constraints (16.4 min, install required, gesture subscribe, visible-notification mandatory) + web-push/VAPID stack entry.
|
||||||
|
3. `.planning/STATE.md` (Phase 5 note) — iOS revokes after ~3 silent pushes; health-check + `event.waitUntil()` mandatory.
|
||||||
|
4. `apps/api/src/lib/listEmitter.ts`, `apps/api/src/broker/poller.ts`, `apps/api/src/broker/outboxWorker.ts` — push-dispatch hook points + change-detection sources.
|
||||||
|
|
||||||
|
## Critical Anti-Patterns (do NOT repeat these)
|
||||||
|
| Pattern | Description | Severity | Prevention Mechanism |
|
||||||
|
|---------|-------------|----------|---------------------|
|
||||||
|
| `db:push` on populated MariaDB | The new push-subscription table must NOT be created via `drizzle-kit push` — it emits a false destructive diff and can truncate tables on populated MariaDB | advisory | Use `drizzle-kit generate` + `migrate` for all new tables this phase |
|
||||||
|
| Silent pushes on iOS | Any push that does not display a visible notification counts toward iOS's ~3-strike silent-revocation; subscriptions die silently | advisory | Every push MUST show a visible notification; SW uses `event.waitUntil()`; implement the subscription health-check from day one |
|
||||||
|
| Introducing Redis for fan-out | The API is a single Node process; `ioredis` is not installed | advisory | Push dispatch reuses the in-memory `listEmitter` publish points — do not add Redis (matches Ph4 decision) |
|
||||||
|
|
||||||
|
## Infrastructure State
|
||||||
|
- `web-push` and `ioredis`: NOT installed. VAPID keys not yet generated. No Redis (single Node process).
|
||||||
|
- Service worker: vite-plugin-pwa `generateSW` + `autoUpdate` — adding a `push`/`notificationclick` handler likely requires switching to `injectManifest` (planner's call; preserve Workbox precache + autoupdate).
|
||||||
|
- react-router installed (Ph4 D-17) — enables tap-to-open deep links (`/lists/:id`, event URLs).
|
||||||
|
- No running background services from this session.
|
||||||
|
|
||||||
|
<context>
|
||||||
|
Discussion is done and committed; nothing is in flight. Three gray areas were
|
||||||
|
discussed (Copy & anti-spam, Reminder scope & timing, Onboarding & opt-out);
|
||||||
|
Quiet-hours/DND was left to discretion (v1 = none). The shared-only reminder
|
||||||
|
scope (D-05) is the decision most worth re-examining before committing the plan
|
||||||
|
if the household's mental model is "remind me about everything."
|
||||||
|
</context>
|
||||||
|
|
||||||
|
<next_action>
|
||||||
|
Start with: `/clear` then `/gsd-plan-phase 5` (optionally `/gsd-ui-phase 5` first
|
||||||
|
for the notification UI design contract). Read `05-CONTEXT.md` before acting.
|
||||||
|
</next_action>
|
||||||
Reference in New Issue
Block a user