diff --git a/Open-Questions.md b/Open-Questions.md new file mode 100644 index 0000000..632e06a --- /dev/null +++ b/Open-Questions.md @@ -0,0 +1,266 @@ +# Open Questions + +**Status:** Living document +**Owner:** Patrick +**Last updated:** 2026-06-24 + +> This page captures Sparkboard-specific decisions that are **not yet locked**. Each question has a recommended **leaning** based on the Sprint 1 walking-skeleton constraints. Patrick has the final call; the leaning is what we'll build unless Patrick overrides. +> +> Auth and multi-tenancy questions live in plate-auth's own docs (Sparkboard consumes those decisions, not makes them). + +--- + +## Question Map + +| ID | Topic | Blocks Sprint | Decision needed by | +|----|-------|--------------|-------------------| +| Q01 | Single-org name and ID strategy | Sprint 1 (W2) | Sprint 1 kickoff | +| Q02 | Allowlist management UX | Sprint 1 (W1) — light; Sprint 4 — heavy | Sprint 1 kickoff | +| Q03 | Admin promotion model | Sprint 1 (W2) | Sprint 1 kickoff | +| Q04 | Idea status workflow | Sprint 2 (Kindling) | Sprint 2 kickoff | +| Q05 | Reaction model | Sprint 2 (Kindling) | Sprint 2 kickoff | +| Q06 | Security customisation: do we need any beyond plate-auth? | Sprint 1 (W1) — confirm "no"; Sprint 2 — re-check | Sprint 1 kickoff | +| Q07 | PWA assets pipeline (icons, splash) | Sprint 1 (W4) | Sprint 1 mid-sprint | +| Q08 | Mobile strategy: PWA-only vs native APK | Sprint 5 (Wildfire) | Sprint 4 retro | +| Q09 | Real-time updates (live list refresh) | Sprint 4 (Ember) | Sprint 3 retro | +| Q10 | Notifications: email, push, or none | Sprint 4 (Ember) | Sprint 3 retro | + +--- + +## Q01 — Single-org name and ID strategy + +**Question:** +The whole of Sparkboard runs against one org. How do we identify it? +- (A) Hardcode a magic UUID `00000000-0000-0000-0000-000000000001` everywhere. +- (B) Look up by `org_type = 'SPARK_ORG'` on every request (assume only one row). +- (C) Bind via `@ConfigurationProperties("sparkboard.org.id")` from `application.yml`. + +**Why it matters:** +This decision shows up in `IdeaController` (the `FAMILY_SPARK_ID` constant), the `SparkboardOnboardingHook`, the `V2__seed_family_spark_org.sql` migration, and any future entity that needs `org_id`. + +**Leaning: A** (hardcode the all-zeros-with-trailing-1 magic UUID). + +**Why:** +- Sparkboard is single-org by definition. The constant is honest. +- One value in one place (`IdeaController.FAMILY_SPARK_ID = UUID.fromString("00000000-0000-0000-0000-000000000001")`). +- Trivial to refactor to option C the moment a second org appears (which is "never" for Sparkboard, but might be relevant if the pattern is lifted into a different app). +- Option B costs a DB roundtrip per request for no value. Option C costs configuration ceremony for no value. + +**Decision:** _Pending._ + +--- + +## Q02 — Allowlist management UX + +**Question:** +Who can sign in is controlled by plate-auth's allowlist. How does Patrick add/remove the 4 family humans? +- (A) Edit `application.yml` → `plate.auth.allowlist[]` → redeploy via Gitea Actions. +- (B) Edit a DB table → no redeploy. +- (C) Build an admin UI (form + endpoint). + +**Why it matters:** +Affects W1 config shape, Sprint 4 admin-panel scope, and how forgiving the system is when a 5th human needs adding ("guest"). + +**Leaning: A** (YAML + redeploy) for v1, **B** if Patrick says manual edits feel painful, **C** is Sprint 4+. + +**Why:** +- 4 humans, very low churn. Editing YAML and pushing to `main` triggers a deploy and is honestly fine. +- DB-backed allowlist (B) adds an entire CRUD surface; not worth it in Sprint 1. +- Admin UI (C) is multi-week work; explicitly scheduled in Sprint 4 (Ember) on the [Roadmap](Roadmap.md). + +**Decision:** _Pending._ + +--- + +## Q03 — Admin promotion model + +**Question:** +Patrick wants to bootstrap himself (and probably the friend) as ADMIN, with the kids as MEMBER. How is that decided? +- (A) `sparkboard.admins[]` list in `application.yml`; `SparkboardOnboardingHook` checks the user's email against it and writes ADMIN or MEMBER accordingly. +- (B) Manual DB UPDATE after first login. +- (C) First-user-is-admin convention. +- (D) All users are ADMIN; no MEMBER role in v1. + +**Why it matters:** +Affects W2 (`SparkboardOnboardingHook` impl) and the bootstrap story for new family-member onboarding. + +**Leaning: A** (`sparkboard.admins[]` in YAML). + +**Why:** +- Self-documenting (admin list is in source control). +- No DB-edit ritual. +- Idempotent (re-running the hook against the same user with same admin list = no diff). +- Option D (all ADMIN) is tempting for simplicity, but Sprint 2+ will care about MEMBER vs ADMIN (e.g., admin-only delete), so introducing the distinction now costs almost nothing. + +**Decision:** _Pending._ + +--- + +## Q04 — Idea status workflow + +**Question:** +In Sprint 1, the `IdeaStatus` enum exists with values `RAW`, `EXPLORING`, `BUILDING`, `SHIPPED`, `DEAD`. Default is `RAW`. **Sprint 2 (Kindling)** adds UI to change status. What's the rule? +- (A) Free transitions: any user can move any idea from any state to any state. +- (B) Author-only: only `Idea.authorId == currentUser.id` can change status. +- (C) Admin-only. +- (D) Stage-gate: only RAW→EXPLORING→BUILDING→SHIPPED, with DEAD reachable from any. + +**Leaning: A** for v1 (free transitions), revisit if it gets messy. + +**Why:** +- 4 humans, low conflict probability. Free transitions = least code, most agency. +- Author-only (B) prevents Patrick from cleaning up a kid's neglected idea, which is the wrong tradeoff for this user group. +- Admin-only (C) makes the kids feel infantilised. +- Stage-gate (D) is over-engineered for v1; revisit if data shows people skipping states or "stuck in BUILDING for a year" patterns. + +**Decision:** _Pending. Not blocking Sprint 1._ + +--- + +## Q05 — Reaction model + +**Question:** +Sprint 2 (Kindling) introduces reactions. What does a reaction look like? +- (A) Likes only (one-bit, per user, per idea). +- (B) Emoji set (fixed list: 👍 ❤️ 🔥 💡 🤔). +- (C) Custom emoji per user. +- (D) None — skip reactions, jump to comments in Sprint 3. + +**Leaning: B** (fixed emoji set of 4–6). + +**Why:** +- Likes only (A) is too cold for a family idea board. +- Fixed emoji set (B) gives expressiveness without UX complexity (one row per `(user, idea, emoji)`). +- Custom emoji (C) is over-engineering for 4 humans. +- Skipping reactions (D) is plausible if comments-first feels more valuable. Decision deferred to Sprint 2 retro. + +**Decision:** _Pending. Not blocking Sprint 1._ + +--- + +## Q06 — Security customisation: do we need any beyond plate-auth? + +**Question:** +plate-auth ships a default Spring Security config that secures everything under `/api/**` and exposes `/api/auth/**` for the auth flow. Does Sparkboard need to: +- (A) Use plate-auth's defaults as-is. +- (B) Customise the `SecurityFilterChain` (e.g., add CORS rules, custom headers, rate limiting). +- (C) Replace plate-auth's security config entirely. + +**Why it matters:** +If C is the answer, plate-auth's promise of "consumers don't touch auth code" breaks. + +**Leaning: A** (use defaults). + +**Why:** +- 4-user app, no rate-limiting threat. +- Same origin (frontend + backend behind Caddy) → no CORS surface. +- If plate-auth's defaults are wrong for Sparkboard, that's a plate-auth bug, not a Sparkboard customisation. File it back upstream. + +**Decision:** _Pending. Sprint 1 verification: confirm A works end-to-end. Re-check in Sprint 2 if reactions/comments introduce new endpoint patterns._ + +--- + +## Q07 — PWA assets pipeline + +**Question:** +Sprint 1 W4 produces `manifest.json` referencing icon files. How are those assets produced? +- (A) Hand-crafted SVG + 1–2 PNG sizes (192 + 512) committed to `frontend/public/icons/`. +- (B) Generated from a single source SVG via a build-time tool (e.g., `pwa-asset-generator`). +- (C) Skip icons; rely on default browser icon (PWA install will still work, just ugly). + +**Why it matters:** +Affects A5 acceptance ("installable PWA with correct icon"). Default browser icon (C) technically installs but doesn't feel finished. + +**Leaning: A** (hand-crafted, 2 sizes). + +**Why:** +- 4 humans, no app-store. Two PNGs are enough. +- Asset-generator tooling (B) is Sprint 4+ when we care about iOS splash screens, maskable icons, dark-mode variants. +- C fails the spirit of A5. + +**Decision:** _Pending. Sprint 1 blocker if no icon → A5 fails MT-01/MT-02._ + +--- + +## Q08 — Mobile strategy: PWA-only vs native APK + +**Question:** +Sprint 5 (Wildfire) optionally introduces Capacitor to package the PWA as a native APK (and iOS app). Do we go native? +- (A) PWA only, forever. iOS share-target via PWA shortcuts; Android install via Chrome. +- (B) PWA primary, plus a Capacitor APK for Android (cheap; native iOS deferred indefinitely). +- (C) Full native (Capacitor APK + iOS via TestFlight + signing certs). + +**Leaning: A** for v1; reconsider B at end of Sprint 4. + +**Why:** +- PWA install on iOS Safari does work (MT-01). +- Capacitor for Android is genuinely cheap once the PWA exists. +- iOS native is expensive (Apple developer account, signing, App Store review). Not worth it for 4 humans. + +**Decision:** _Pending. Not blocking Sprint 1._ + +--- + +## Q09 — Real-time updates + +**Question:** +When User A creates an idea, does User B's open `/ideas` page refresh automatically? +- (A) No real-time; rely on Next.js revalidation + manual refresh. +- (B) Server-Sent Events (one-way push from backend to frontend). +- (C) WebSockets (two-way, overkill for this). +- (D) Polling every 10 seconds. + +**Leaning: A** for v1/v2; **B** in Sprint 4 if it feels stale. + +**Why:** +- 4 humans, low concurrent-write rate. "Pull to refresh" is fine for v1. +- SSE (B) is the cheapest real-time option (single endpoint, browser native). +- WebSockets (C) costs more infrastructure for no real benefit at 4 users. +- Polling (D) wastes bandwidth and battery. + +**Decision:** _Pending. Not blocking Sprint 1._ + +--- + +## Q10 — Notifications + +**Question:** +Sprint 4 (Ember) lands notifications. What channels? +- (A) Email digest (daily summary of new ideas). +- (B) Web push (browser-side push notifications). +- (C) Both. +- (D) None — Sparkboard is pull-only. + +**Why it matters:** +Affects Sprint 4 architecture (SMTP integration vs Web Push API + VAPID keys). + +**Leaning: A** (email digest first, then reassess for B). + +**Why:** +- Email is dead-simple: SMTP via SendGrid or postfix on TrueNAS; daily cron. +- Web push requires VAPID keys, service-worker logic (sw.js becomes real, not a stub), and per-user permission grants. Higher effort, less reliable on iOS. +- A daily email is genuinely useful for a family idea board (low rate of new items). +- D ("none") is plausible if Sprint 3 reveals that the 4 humans naturally check the app regularly without prompting. + +**Decision:** _Pending. Not blocking Sprint 1._ + +--- + +## Resolved Questions (Archive) + +> When a question is decided, move it here with the resolution and date. Keeps Q01–Q10 numbering stable. + +_None yet._ + +--- + +## Cross-references + +- [Sprint-1-Plan](Sprint-1-Plan.md) and [Sprint-1-Plan-Part-4](Sprint-1-Plan-Part-4.md) — Sprint-1 impact of Q01, Q02, Q03, Q07 +- [Roadmap](Roadmap.md) — when each unresolved question is scheduled to bite +- [Architecture](Architecture.md) — context for Q01 (single-org), Q06 (security) +- plate-auth wiki — for auth-side decisions (NOT in this list) + +--- + +_End of Open Questions. Status: **10 open, 0 resolved.**_