docs(open-questions): 10 Sparkboard Qs (Q01-Q10) with leanings

Patrick Plate
2026-06-24 14:58:14 +02:00
parent ec5cc6c151
commit 8e72174188
+266
@@ -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 46).
**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 + 12 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 Q01Q10 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.**_