From f695cc014dbe0836b9d657212a1b6c0b8a59ee8c Mon Sep 17 00:00:00 2001 From: Lucas Berger Date: Sun, 7 Jun 2026 21:53:23 -0400 Subject: [PATCH] =?UTF-8?q?wip:=20Phase=204=20paused=20=E2=80=94=20discuss?= =?UTF-8?q?=20complete,=20entry=20gate=20cleared,=20ready=20to=20plan?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .planning/HANDOFF.json | 35 ++++++++ .../.continue-here.md | 89 +++++++++++++++++++ 2 files changed, 124 insertions(+) create mode 100644 .planning/HANDOFF.json create mode 100644 .planning/phases/04-shared-lists-live-sync/.continue-here.md diff --git a/.planning/HANDOFF.json b/.planning/HANDOFF.json new file mode 100644 index 0000000..14f85d1 --- /dev/null +++ b/.planning/HANDOFF.json @@ -0,0 +1,35 @@ +{ + "version": "1.0", + "timestamp": "2026-06-08T01:52:24.365Z", + "phase": "4", + "phase_name": "Shared Lists + Live Sync", + "phase_dir": ".planning/phases/04-shared-lists-live-sync", + "plan": null, + "task": null, + "total_tasks": null, + "status": "paused", + "stage": "discuss-complete; entry-gate-cleared; ready-to-plan", + "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": 2, "name": "Ran SSE-over-Pangolin smoke test live — PASS (~6 min, 35 heartbeats, buffering off, no cut)", "status": "done"}, + {"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": [ + {"id": 5, "name": "Plan Phase 4: /gsd-plan-phase 4 (reads 04-CONTEXT.md)", "status": "not_started"}, + {"id": 6, "name": "Optionally /gsd-ui-phase 4 for the lists UI design contract (UI hint: yes)", "status": "not_started"}, + {"id": 7, "name": "Execute Phase 4 plans", "status": "not_started"} + ], + "blockers": [], + "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} + ], + "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": "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": "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"} + ], + "uncommitted_files": [], + "next_action": "/clear then /gsd-plan-phase 4 (entry gate is cleared; no infra precondition remains)", + "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." +} diff --git a/.planning/phases/04-shared-lists-live-sync/.continue-here.md b/.planning/phases/04-shared-lists-live-sync/.continue-here.md new file mode 100644 index 0000000..2efa104 --- /dev/null +++ b/.planning/phases/04-shared-lists-live-sync/.continue-here.md @@ -0,0 +1,89 @@ +--- +context: phase +phase: 04-shared-lists-live-sync +task: null +total_tasks: null +status: ready_to_plan +last_updated: 2026-06-08T01:52:24.365Z +--- + + +Phase 4 (Shared Lists + Live Sync) — **discussion complete, entry gate cleared, ready to plan.** +Nothing is mid-edit. The working tree is clean and this is a deliberate stopping point between +discuss-phase and plan-phase. + +- `04-CONTEXT.md` is written and committed (18 decisions, D-01..D-18). +- The Phase 4 **entry gate** (SSE-over-Pangolin smoke test, D-14 / issue #1034) is **CLEARED** — + verified live this session and recorded in the gate docs. No infra precondition remains. +- No PLAN.md exists yet for Phase 4. + + + + +This session: +- Ran `/gsd-discuss-phase 4` → `04-CONTEXT.md` + `04-DISCUSSION-LOG.md` (commit 05e1c9e). +- Executed the SSE-over-Pangolin smoke test live over `familysync-dev.bergerhouse.net`: + ~6 min hold, 35 heartbeats (id 0→34) at ~10s, incremental delivery (buffering off), no cut → PASS. +- Recorded the PASS via quick task 260607-u8o: updated `01-HUMAN-UAT.md` item 4 and + `03-GATE2-RESULTS.md` Part C to PASS; struck the entry-gate blocker in STATE.md (commit 9ee5906). +- Saved project memory: design for N family members (not hard-coded two). + + + + +- **Next:** `/gsd-plan-phase 4` (consumes `04-CONTEXT.md`). +- Optional before/after planning: `/gsd-ui-phase 4` — lists UI design contract (ROADMAP UI hint: yes). +- Then execute Phase 4 plans. + + + + +All locked in `04-CONTEXT.md` (read it before planning). Highlights for the planner: +- **Sharing:** default-shared lists with a per-list private toggle; `list_shares` join table + (member-count-agnostic, N-member-ready); SSE fan-out **scoped to who can see a list** (private + lists must NOT broadcast to everyone). +- **Items:** checked items sink to a completed section; confirm-on-delete for lists only + (reuse `DeleteConfirmationDialog`). +- **Live feel/conflicts:** optimistic UI; per-field PATCH + per-field last-write-wins (bounded — + NO CRDT); delete-wins. +- **Reconnect:** full refetch on reconnect; capped-backoff then a "updates paused" indicator; + React Query `refetchInterval` polling fallback. +- **Ordering:** string-based fractional index (NOT raw floats, NOT integer-renumber); animate + remote reorders; last-write-wins settle. +- **Nav:** bottom tab bar + react-router (real URLs, for Phase 5 push deep-links). No router today. +- **Project principle:** design for N family members, not hard-coded two. +- **Deferred (out of scope):** anonymous public-URL list sharing; per-recipient picker UI. + + + +- None. The entry gate that previously blocked the build is cleared. + + +## Required Reading (in order) +1. `.planning/phases/04-shared-lists-live-sync/04-CONTEXT.md` — locked implementation decisions; the contract for planning. +2. `apps/api/src/db/schema.ts` — Drizzle table conventions for the new `lists` / `list_items` / `list_shares` tables. +3. `apps/api/src/routes/sse.ts` — existing Hono `streamSSE` heartbeat pattern; the live-list stream extends it. +4. `.planning/phases/03-event-write-back-pwa-install/03-GATE2-RESULTS.md` Part C — recorded SSE smoke PASS evidence. + +## Open Decisions for the Planner (intentionally NOT pre-decided) +- **Fan-out mechanism:** in-memory EventEmitter vs Redis pub/sub. API runs as a single Node process + today (no replicas); `ioredis` is NOT installed; `redis` IS in docker-compose. In-memory is the + YAGNI default — planner must justify the choice against the N-member future (D-18). +- Position-rank column type, SSE auth/middleware wiring, React Query cache-key structure. + +## Infrastructure State +- Pangolin route already configured (buffering off, idle/read timeout ≥120s) and verified for SSE. +- `redis` service present in docker-compose; `ioredis` not yet a dependency. +- New DB tables MUST use `drizzle-kit generate` + `migrate` — **never `push`** (unsafe on populated MariaDB). +- No background processes were left running. + + +Clean handoff. The hard part (verifying SSE survives the tunnel) is done and recorded, so Phase 4 +can be planned and built without an infra gate hanging over it. The planner should treat +04-CONTEXT.md as authoritative and focus its remaining judgment on the fan-out mechanism and the +new schema (lists, list_items, list_shares) using generate+migrate. + + + +Start with: `/clear` then `/gsd-plan-phase 4`. +