Table of Contents
- Open Questions
- Question Map
- Q01 — Single-org name and ID strategy
- Q02 — Allowlist management UX
- Q03 — Admin promotion model
- Q04 — Idea status workflow
- Q05 — Reaction model
- Q06 — Security customisation: do we need any beyond plate-auth?
- Q07 — PWA assets pipeline
- Q08 — Mobile strategy: PWA-only vs native APK
- Q09 — Real-time updates
- Q10 — Notifications
- Resolved Questions (Archive)
- Cross-references
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 | Status | Blocks Sprint | Decision needed by |
|---|---|---|---|---|
| Q01 | Single-org name and ID strategy | ✅ Decided 2026-06-24 — A | Sprint 1 (W2) | Sprint 1 kickoff |
| Q02 | Allowlist management UX | ✅ Decided 2026-06-24 — A | Sprint 1 (W1) — light; Sprint 4 — heavy | Sprint 1 kickoff |
| Q03 | Admin promotion model | ✅ Decided 2026-06-24 — A | Sprint 1 (W2) | Sprint 1 kickoff |
| Q04 | Idea status workflow | ⏳ Open | Sprint 2 (Kindling) | Sprint 2 kickoff |
| Q05 | Reaction model | ⏳ Open | Sprint 2 (Kindling) | Sprint 2 kickoff |
| Q06 | Security customisation: do we need any beyond plate-auth? | ⏳ Open | Sprint 1 (W1) — confirm "no"; Sprint 2 — re-check | Sprint 1 kickoff |
| Q07 | PWA assets pipeline (icons, splash) | ✅ Decided 2026-06-24 — A | Sprint 1 (W4) | Sprint 1 mid-sprint |
| Q08 | Mobile strategy: PWA-only vs native APK | ⏳ Open | Sprint 5 (Wildfire) | Sprint 4 retro |
| Q09 | Real-time updates (live list refresh) | ⏳ Open | Sprint 4 (Ember) | Sprint 3 retro |
| Q10 | Notifications: email, push, or none | ⏳ Open | 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-000000000001everywhere. - (B) Look up by
org_type = 'SPARK_ORG'on every request (assume only one row). - (C) Bind via
@ConfigurationProperties("sparkboard.org.id")fromapplication.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: ✅ Decided 2026-06-24 — Option A. Hardcode FAMILY_SPARK_ID = UUID.fromString("00000000-0000-0000-0000-000000000001") as a public static final on SparkboardOnboardingHook. Used by IdeaController, the hook, and the V2__seed_family_spark_org.sql migration. Refactor to option C only when a second org appears (not in Sprint 1–5).
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
maintriggers 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.
Decision: ✅ Decided 2026-06-24 — Option A. Allowlist lives in application.yml under plate.auth.allowlist[] (bound by plate-auth's PlateAuthProperties). Adding/removing a family member is a YAML edit + Gitea Actions deploy. DB-backed allowlist (B) and admin UI (C) deferred to Sprint 4 (Ember) at the earliest.
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 inapplication.yml;SparkboardOnboardingHookchecks 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: ✅ Decided 2026-06-24 — Option A. Admin list lives in application.yml under sparkboard.admins[] (bound by SparkboardAdminProperties). SparkboardOnboardingHook.onFirstSignIn(user) reads it and writes Role.ADMIN if user.email() is in the list, otherwise Role.MEMBER. Initial seed list (placeholders — replace before first deploy): <patrick@email>, <friend@email>, <kid1@email>, <kid2@email>.
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.idcan 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: ✅ Decided 2026-06-24 — Option A. Hand-craft 2 PNGs (192×192 and 512×512), commit to frontend/public/icons/icon-192.png and frontend/public/icons/icon-512.png, reference both from frontend/public/manifest.json. No build-time generator in Sprint 1. Asset-generator tooling (Option B) revisited in Sprint 4+ when iOS splash / maskable / dark-mode variants are needed.
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, the full text stays in place (numbering stays stable). This section is a quick-reference log of decisions.
| ID | Topic | Decision | Date |
|---|---|---|---|
| Q01 | Single-org name and ID strategy | A — Hardcode FAMILY_SPARK_ID = 00000000-0000-0000-0000-000000000001 |
2026-06-24 |
| Q02 | Allowlist management UX | A — plate.auth.allowlist[] in application.yml |
2026-06-24 |
| Q03 | Admin promotion model | A — sparkboard.admins[] YAML list, read by SparkboardOnboardingHook.onFirstSignIn |
2026-06-24 |
| Q07 | PWA assets pipeline | A — Hand-crafted 192×192 + 512×512 PNGs in frontend/public/icons/ |
2026-06-24 |
| F3 | Reviewer finding — hook method spelling | Renamed afterFirstLogin(OnboardingContext) → onFirstSignIn(AuthenticatedUser) across all sparkboard docs (plate-auth spelling wins) |
2026-06-24 |
| F4 | Reviewer finding — UT-06 package typo | de.plate.sparkboard.auth.SparkboardOnboardingHookTest → de.plate.sparkboard.onboarding.SparkboardOnboardingHookTest |
2026-06-24 |
Cross-references
- Sprint-1-Plan and Sprint-1-Plan-Part-4 — Sprint-1 impact of Q01, Q02, Q03, Q07
- Roadmap — when each unresolved question is scheduled to bite
- Architecture — context for Q01 (single-org), Q06 (security)
- plate-auth wiki — for auth-side decisions (NOT in this list)
End of Open Questions. Status: 6 open, 4 resolved. (Q01, Q02, Q03, Q07 decided 2026-06-24.)