Product PRD · v1.0
Product PRD
1 · Personas & roles
| Role | Definition | Sees |
|---|---|---|
| Organizer | Creates the meeting; the only role with Google OAuth in v1; tags decision-makers; approves locks and reschedules. | Everything — per-person paints, scores, reasoning. |
| Decision-maker (DM) | Invitee(s) tagged by the organizer. Multiple allowed; quorum requires all DMs (assumed pick, round 3). | Aggregate heatmap only, like any invitee. |
| Invitee | Guest, no account. Paints availability via tokenized link; may claim their history later by magic link. | Aggregate heatmap; never per-person paints. |
2 · Nebula state machine
| State | Motion signature | Trigger in / out |
|---|---|---|
| OPEN | Fast chaotic swirl; density ∝ responses/total; speed ∝ 1 − responses/total. | In: invites sent. Out: all responses in, or organizer forces compute. |
| COMPUTING | Dense fast swirl, orb expands (~2s beat). | Out: proposal ready → PROPOSED. |
| PROPOSED | Orb shrinks and docks onto the winning grid cell (2b); gold outline. | Out: organizer locks, or picks an alternate (re-score, stay PROPOSED). |
| LOCKED | Slow breathing, gold. Near-still. | Out: decline/removal → DESTABILIZED; meeting passes → CLOSED. |
| DESTABILIZED | Gold→amber flicker, swirl speeds up — motion changes before any words. | Out: organizer chooses a repair path. |
| RECOMPUTING | Amber swirl; reuses existing paints, nobody re-fills the grid. | Out: new PROPOSED (history keeps the struck-through old slot). |
| CLOSED | Static. | Terminal. |
Copy is first-person and template-driven ("I found a time that works for all 6 of you") but no LLM writes anything — every string is a versioned template filled from the score breakdown. Reasoning lives behind a ⓘ tap and shows the raw deterministic breakdown.
3 · CUJ specifications (v1)
3.1 Core loop — create, paint, reveal, lock (wireframes 1b–1d)
- Create: one intent field + constraint chips (duration, window, people). AI drafts the ask; organizer can veto suggested windows before sending.
- Paint: drag-paint free / if-need-be. Organizer-side conflict nudges from Google read ("Tuesday clashes with your 1:1 — keep it paintable?"); the AI flags, never edits paint. Invitee nudges are v1.1.
- Submitting visibly densifies the shared orb — every response adds mass in realtime for everyone watching.
- Reveal = 2b grid-anchored: the orb docks onto the winning cell; popover shows one proposal + "Why not [other slot]?" interrogation; alternates behind a button, never a ranked wall. After lock the gold cell stays as the record.
- Acceptance: lock writes a Google Calendar event (organizer's calendar, invitees as guests) and sends the Resend confirmation. Median time-to-lock target < 24h.
3.2 Reschedule loop (1e)
- Decline detection (calendar webhook or in-app) → DESTABILIZED. Organizer chooses: find new time (all) or keep without the decliner. AI never re-books on its own.
- Re-compute reuses all paints. Re-lock shows history:
Tue 2pm→ Thu 10am.
3.3 No-common-slot negotiation (2f)
- When no cell satisfies all required invitees, the orb visibly fails to converge (amber, won't settle), then the AI names the cheapest single flex ask ("only Ken painted Wed 4pm busy, with a movable block").
- Organizer sends the flex request; invitee gets a one-tap Sure / Can't. Best-5 fallback is cut — if the flex fails, the AI proposes widening the window (new dates), which re-enters the paint loop for the new cells only.
3.4 Add-after-lock (2g)
- Adding a person to a locked meeting runs a calendar check (if they're a known/OAuth user) or an availability ask (guest). No conflict → gold stays gold. Conflict → organizer chooses optional-invite or re-run, which enters 1e scoped to 7 people.
3.5 Meeting-space graph (1f)
- Star = meeting (anchored). Person nodes settle by home city; dashed teal halos mark co-location clusters; square nodes = artifacts (agenda, docs).
- Drag physics: shift-drag (desktop) or move-toggle; pull propagates along links, weaker per hop; links stretch elastically; swirl intensifies during drag; release re-settles at speed 7.
- Semantic drops: into a city halo = join that cluster in person; onto a person = attend remotely with them; open space = return home. Every drop is confirmed in copy with a ⓘ.
3.6 Mobile invitee (2d)
- One day per screen, 3 columns, cells ≥44px; swipe between days; long-press toggles if-need-be; conflict nudge (v1.1) as a bottom sheet; done-state fits one screen with the orb as receipt.
3.7 Landing (1g)
- Nebula is the hero; particles are team voices (hover → creator quote — names/quotes still placeholder, owner: design, due wk 5). Features told as the three states. CTA drops into create with no signup wall.
4 · Privacy model
- Organizer sees all; invitees see aggregate. The heatmap invitees see is count-per-cell only; per-person paints are organizer-scoped (enforced by Supabase RLS, see Backend §6).
- Flex requests (2f) are the one deliberate exception: the named invitee learns "everyone else can do X" — the ask itself reveals aggregate, never who painted what.
5 · v2: breakout blobs — groundwork shipped in v1
Blobs (wireframe t3): drag multiple nodes together → persistent proto-blob → live room anyone enters by dragging their own node in; blobs stay until clicked (peek card) or closed (×), and closing folds artifacts back into the parent meeting.
- Cluster detection is generic (n nodes, not person→halo only) — blob-spawn reuses v1 hit-testing.
- Drag physics unchanged; blobs are only a new drop-target type.
- Solid ring is reserved: v1 halos are always dashed so "solid ring = enterable room" stays unambiguous in v2.
- Artifact nodes carry a
parent_meeting_idfrom day one so breakout notes can re-attach. - Graph canvas never auto-crops — blobs need parking room.
- Media layer decision: wk 6 — see Integrations §4.