Design preview · Retreat Booking (TMPL-RTBK01) · beta · 2026-08-06
← Design HQ · HTML on GitHub ↗ · PRD ↗

Retreat Booking — every screen through the flow

Retreat Booking — Discover, set it up, go live, then the guest thread.

SizePortrait scrolls 375 · sideways is a slide deckNotes
Design brief — overview · scope · journey (closed)

The launch-wedge assistant's standing design contract — drawn FROM the shipped beta flow plus prd-as-retreat-booking.md, for Peyton sign-off. Companion to the template-agnostic master assistant-flows/assistant-lifecycle.html — this file inherits it and must not contradict it. Full framing in 1 · Design overview and 2 · Scope below.

8-section shape · card → live → monitored · desktop + mobile relay payment model — MystFlo never touches the money amber = action/pending · emerald = live/paid only

Widths here sample a fluid continuum — content scales continuously; structure steps only at sm 640 · md 768 · lg 1024. Kit: design-canon.md U8. Reading order (kit-locked 2026-07-31): § Desktop screens first, then § Mobile screens — 375, then the 768 tablet sample. 375 is still designed and scored FIRST in real work.

Every $ figure drawn here ($1.75/run, deposit and balance amounts) is illustrative — prices arrive from props/the DB at runtime, never authored literals (single-source pricing). Demo persona: Salt & Stone Retreats (host Lena), consistent with the on-brand-support.html exemplar. Program: Bali Spring Retreat · Apr 12–19 · 12 spots · 30% deposit.

1 · Design overview what this is, in plain language

Retreat Booking takes the payment chase off a retreat host's plate. Guests already ask to book in WhatsApp — this assistant holds the spot, relays the host's own payment link, nudges the deposit, chases the balance at T-30/14/7 in the host's voice, and confirms the booking the moment the money is seen in the host's roster, Stripe, or a one-tap "Mark paid". The host watches it all from one Pulse card and makes every overdue call herself — Mark paid, extend, or release. It is the launch wedge: the retreat-persona card on the shared Program Booking Engine, and the first assistant a retreat host meets. MystFlo never touches the money — it chases, tracks, and confirms only.

2 · Scope what this build is — and what is explicitly out
In this build
  • TMPL-RTBK01 · beta · $1.75 per confirmed booking — renamed from Cohort Booking by migration 20260802012707_rtbk01_rename_cohort_to_retreat.sql; price renders from price_per_run, never authored.
  • Relay payment model (BINDING) — every pay moment relays the HOST'S own link or instruction; no MystFlo checkout appears anywhere. Copy implying MystFlo takes money is a blocker.
  • The retreat lens of the shared Program Booking Engine — payment_mode: deposit_balance is the DEFAULT here (hidden on the workshop card); engine mechanics live in the RTBK01 PRD (the engine home). Sibling card: workshop-booking.html (TMPL-WSBK01).
  • The full founder flow (Explore card → detail → Personalize → Connect → Try → Activated → Pulse → overdue verdict → post detail sheet) plus the guest WhatsApp phase — drawn to SHIPPED gating, no invented actions.
Explicitly out
  • The workshop lens — lives entirely in the sibling workshop-booking.html.
  • Attendees records surface · mirror write-back · booking-invite founder field — deferred, drawn as compact cards in 8 · Future design only. Shape re-cut 2026-09-02 (Peyton, R36): Attendees is a block IN the Pulse tab + a nested scrollable sheet, entered from a slot IN the Pulse card — no longer a full-screen page. Drawn once on the engine home (workshop-booking.html sec-13) and the master (assistant-lifecycle.html 8d · 9b-r · 9e); not redrawn here.
  • A pre-overdue "nudge" action — not built; deliberately not drawn (gap 1).
  • BYOK AI provider picker — gated by BYOK_AI_HIDDEN; the AI Model row has no caret (kit lock 2026-08-05).
  • sec-2 Personalize payment-plan direction — ON HOLD pending a Peyton ruling (gap 6); the drawn content is frozen, do NOT build Personalize from this file yet.
3 · Content structure the data/copy shape every screen below is built from
Catalog copy — from the PRD, verbatim
  • description (card one-liner) · details.tldr · long_description · 4 details.benefits cards (typographic, no icons — T1203) — all VERBATIM from the PRD Catalog Copy table; screens never paraphrase the DB.
  • details.flow "How it works" chart — 6 nodes · 1 branch, deterministic seed; the live seed lags this contract on two counts (gap 5: "cohort" rename + missing chase node).
Setup shape (Personalize + Connect)
  • Roster binding via roster_sources (sheet | airtable | stripe + column map) — a read SOURCE, not a storage destination; read at send time, never stored (AC-COHB-06 ↗). No storage role declared (Required storage | — |).
  • Payment relay instruction — the host's link or bank/Wise text, relayed word for word (AC-COHB-03 ↗).
  • Sign-off name — asked ONCE across the booking family (AC-SIGNOFF-06 ↗); hidden when no name known (AC-SIGNOFF-07 ↗).
  • payment_mode: deposit_balance default — 30% deposit confirms the hold, balance due T-14.
Chase schedule + booking states
  • Balance reminders at T-30 / T-14 / T-7 — a FIXED engine rhythm, not a founder setting; roster re-read before every send (AC-COHB-06 ↗). Exactly ONE overdue notice, then reminders HOLD and the host is pinged (AC-COHB-07 ↗).
  • Booking rows live in MystFlo's own cohort_bookings — never written back to the host's file. States: held → deposit paid → confirmed · overdue → host verdict; ambiguous roster values stay pending, never accidentally paid (AC-COHB-05 ↗).
  • Overdue host-verdict set — exactly Mark paid · Extend due date · Release spot (AC-COHB-08 ↗), gated to overdue; read + verdict, never edit (Records-Layer rule); never a unilateral release (AC-COHB-07 ↗).
  • Billing shape: exactly ONE $1.75 meter, fired on the paid → confirmed edge only (AC-BILL-01 ↗ / AC-BILL-02 ↗); skips and cancels are visibly free (AC-BILL-04 ↗); wallet-can't-cover holds the booking at 402 (AC-BILL-03 ↗).
4 · User journey — full the host sets the chase once, guests get walked to paid — phases in the PRD's own vocabulary (U10), diagram verbatim

Two journeys, one diagram. The founder's Activate step opens the guest's booking door; every guest payment event flows back as a Pulse row, and the ONLY money-moment MystFlo owns is the $1.75 meter on the paid → confirmed edge. Standard vocabulary: founder Discover → Evaluate → Activate → Monitor → Decide · guest Trigger → Interact → Outcome → Fallback.

FOUNDER JOURNEY ────────────────────────────────────────────────────────────
┌ DISCOVER & EVALUATE ────────────────────────────────────────────────────┐
│ Explore card (sec-1) → detail sheet: what/why/how (sec-1b)               │
└─────────────────────────────────┬────────────────────────────────────────┘
                                  ▼
┌ ACTIVATE ────────────────────────────────────────────────────────────────┐
│ Personalize — roster + round + payment relay + sign-off (sec-2)          │
│ → Connect — WhatsApp · roster source · Brain Check (sec-3)               │
│ → Try — readback + one real test reminder (sec-4) → Activated (sec-5)    │
└─────────────────────────────────┬────────────────────────────────────────┘
                                  ▼        ◄── guests book from here on
┌ MONITOR ─────────────────────────────────────────────────────────────────┐
│ Pulse card — bookings, chase activity, needs-attention rows (sec-6)      │
└─────────────────────────────────┬────────────────────────────────────────┘
                                  │ a due date passes unpaid
                                  ▼
┌ DECIDE — the host verdict moment ────────────────────────────────────────┐
│ Overdue row: Mark paid · Extend due date · Release spot (sec-6b)         │
│ — never a unilateral release (AC-COHB-07 ↗ / AC-COHB-08 ↗)               │
└──────────────────────────────────────────────────────────────────────────┘

GUEST JOURNEY ──────────────────────────────────────────────────────────────
┌ TRIGGER ─────────────────────────────────────────────────────────────────┐
│ Guest asks about the retreat in WhatsApp → capture gate: name + email    │
│ → spot held pending deposit (sec-7a) — sold-out waitlists instead        │
│   (AC-COHB-20 ↗), never a silent overbook                                │
└─────────────────────────────────┬────────────────────────────────────────┘
                                  ▼
┌ INTERACT — the chase, in the host's voice ───────────────────────────────┐
│ deposit nudge (sec-7a) → balance reminders T-30/14/7 with the HOST'S     │
│ payment link relayed (sec-7b, AC-COHB-03 ↗) → "I already paid" honest    │
│ re-check (sec-7c, AC-COHB-10 ↗)                                          │
└─────────────────────────────────┬────────────────────────────────────────┘
                                  ▼
┌ OUTCOME ─────────────────────────────────────────────────────────────────┐
│ payment learned (sheet tick · Stripe event · host Mark paid) →           │
│ WhatsApp + email confirmation <30s, booking = confirmed, ONE $1.75      │
│ meter (AC-COHB-04 ↗ · AC-BILL-01 ↗ · AC-BILL-02 ↗)                       │
└─────────────────────────────────┬────────────────────────────────────────┘
                                  ▼
┌ FALLBACK ────────────────────────────────────────────────────────────────┐
│ overdue → ONE notice, reminders HOLD, host pinged (sec-7d) · stale       │
│ roster ≥7d → guest sends HELD (AC-COHB-12 ↗) · cancel intent honoured,   │
│ no fee (AC-COHB-09 ↗ / AC-BILL-04 ↗)                                     │
└──────────────────────────────────────────────────────────────────────────┘
  
Money guardrail BINDING (PRD)
  • This assistant chases, tracks, and confirms — never handles payment. Every frame that shows a pay moment relays the HOST'S own link or instruction; no MystFlo checkout ever appears. Copy that implies MystFlo takes money is a blocker.
Active the 1:1 build contract (kit canon U9) — matched pixel for pixel; current/being-built state only. Acceptance ids cited in frame notes (AC-* ↗) live in docs/product/assistants/shared/prd-as-retreat-booking.md §Test Plan → Acceptance Criteria. This artifact cites ACs, never mints them.
Discover
Retreat Booking
Beta♡
Chases deposits and balances for your retreat — so every booked spot actually pays before it starts.
Get Paid$1.75/run
Google SheetsWhatsApp BusinessStripe
Runs with On-Brand Support · each charged only when used
tap for detailsActivate
Frame 1 · Explore card [Discover]
  • Story — As a retreat host drowning in "did they pay yet?" spreadsheet checks, I see in one card that this chases the money for me, and what it costs.
  • Copy is VERBATIM from the PRD Catalog Copy table (`description`) — the card never paraphrases the DB.
  • Price renders from `price_per_run` — never an authored literal (AC-BILL-01 ↗ names the billable unit).
  • Beta badge is the amber status chip; app chips are the three `required_apps`. Pairing line (#5311, provisional): from the booking-family rule in code, not the DB; activation drawn once in workshop-booking.html → 3s / 6s, Pulse in assistant-lifecycle.html → 8f.
  • Component: `AssistantSummaryCardPre` (pre-activation taxonomy — BINDING).
768 not yet drawn for this flow. Desktop and 375 are the contract.
This step is 1180. No 375 drawing — use the Desktop size.
Evaluate
Retreat Booking TMPL-RTBK01
Chases deposits and balances for your retreat — so every booked spot actually pays before it starts.
$1.75 per confirmed booking · pay only when it runs
Runs with On-Brand Support — Support answers your guests; booking takes the booking. Both run together, and each is charged per run.
✕
Overview› Personalize › Connect › Try
What it does

Retreat Booking reads your roster (Google Sheet or Airtable), then runs the entire payment chase on WhatsApp: deposit nudges, balance reminders at T-30/14/7, and overdue handling — all in your voice. It relays your own payment link and tracks who's paid from your roster or your "mark paid" tap — MystFlo never touches the money. You decide every release from Pulse.

How it works
01
You
Set up the retreat onceseats, price, your pay link
02
Guest
A guest asks to book"I want the June one" in WhatsApp
03
Assistant
Holds the seat + sends your linktakes the request, relays your pay link
no seats left → hands the guest to Waitlist + Launch (overflow)
04
Assistant
Chases the balance for youdeposit nudge, then T-30/14/7 reminders — in your voice
05
You
Mark paidone tap when the money lands
◎
Assistant
Booking logged, you get the pingseat counted, on-brand confirmation sent
Key benefits
Balances stop leaking quietly
The T-30/14/7 reminder sequence fires automatically — the guests who forgot to pay before your retreat get nudged before the due date passes.
Re-checks your roster before every send
It reads your sheet live at the moment each reminder fires — so no duplicate chases on a spot that already paid.
Reminds them in your voice
Messages go out sounding like you — warm, on-brand — not like an automated payment chaser that damages the relationship before they even arrive.
You decide every spot change
Overdue spots don't get released automatically. You tap extend, mark paid, or release from Pulse — nothing changes without you.
Frame 1b · Detail sheet [Evaluate]
  • Story — Before I pay for anything, I want the what, the why-trust-it, and the price on one sheet.
  • `tldr` / `long_description` / 4 benefit cards VERBATIM from PRD Catalog Copy — benefits are typographic, no icons (T1203).
  • Tab strip is the pre STEPPER (archetype `assistant-detail-tabs.md` / T1147): Overview › Personalize › Connect › Try with chevron separators + ✓ on completed steps — opens on Overview, Try disabled until Connect + Personalize complete.
  • "How it works" chart nodes come from the DB `details.flow` seed (`20260610150000_T-HIW-SEED`, §5.5 contract, deterministic, NO ai tag) with exactly TWO contract deltas, both flagged in the gap list: (1) n1 "Set up the cohort once" → "retreat" (rename catch-up — the 2026-08-02 migration never touched `details.flow`); (2) a NEW node 04 "Chases the balance for you" (Peyton-ruled 2026-08-04 — the chase leg is the product's core and now shows in the public chart; cadence copy states the FIXED T-30/14/7 rhythm as fact, not a founder setting). Now 6 nodes · 1 branch — still inside the §5.5 4–6 node cap. Branch pill hangs off n3, never its own column; outcome stays the ONE lit tile.
  • Pre-activation modal `AssistantDetailModalPre` on `AssistantModalShell` — its post twin is drawn at sec-6c.
  • Money guardrail visible in copy: "relays your own payment link", never "takes payment".
768 not yet drawn for this flow. Desktop and 375 are the contract.
This step is 1180. No 375 drawing — use the Desktop size.
Activate
Retreat Booking TMPL-RTBK01
Set it up once — it runs every retreat after this.
✕
Overview› Personalize› Connect › Try
01 · Your retreat
Which retreat is this for? picker shipped · T1092 dropdown + reveal on pick · #4824 · not built

Edit what you sell in your Brain →

Name *
Bali Spring Retreat
Price
1700
Currency
USD · EUR/GBP/IDR/AUD/SGD/CAD/NZD

Same retreat, new round? Just set when and where.

Date — a range for retreats · per round
Pick the dates
Time — optional
Arrival — e.g. 15:00

Arrival, or a range with the last day’s end — 15:00 or 15:00–11:00. Empty means nothing is promised in the messages.

Where * always shown · #5249 · not built pre-fill · open for Peyton
Villa Sari, Jl. Raya Sayan, Ubud, Bali
https://maps.app.goo.gl/3vR8kQwLm2P… · map link, optional

Where guests arrive — fills the confirmation and the chase messages. Starts as the offering’s venue from your Brain, or your business address when it has none; change either here if this round is somewhere else. With a map link guests get a one-tap “Open in Maps” button; without it they still see the address.

Spots
12

Edits here change only this retreat — your Brain stays as it is.

Field names match your Brain’s offering fields one-to-one — Name · Price · Date · Time · Where · Spots (Peyton 2026-09-23). Date always starts empty after a pick — same retreat, new round.

The main frame is a recurring retreat: standing content fills, Date and Time start empty. Other states of 01 — same as Workshop Booking (workshop sec-3), retreat nouns.
Nothing picked yet · only the dropdown
Pick a retreat
No empty form under it; 02 · Deposit & balance carries on below.
Loading (“Loading what you sell…”, disabled, nothing below) and couldn’t load (“Couldn’t load what you sell.” + Retry ↻, attention tone, fields revealed) are the master picker’s — drawn once in workshop sec-3.
Open
Pick a retreat
Bali Spring Retreat
Recurring · $1,700 · 12 spots
Ubud Silent Week
One-time · 3–9 Aug · $2,400 · 8 spots
+ Something new
One-time picked · everything the Brain has fills in
Ubud Silent WeekOne-time · 3–9 Aug
Date 3–9 Aug, Time, Where and Spots fill from the Brain; anything it doesn’t hold stays blank. No hint — there is no new round to set. Not answered yet → fills whatever the Brain has, no hint.
+ Something new · nothing in your Brain
Something new reveals the same fields, empty — Where still starts as your business address. Nothing in your Brain: no dropdown, the empty fields show straight away under “Type it below — or add what you sell to your Brain →”.
02 · Deposit & balance
Payment plan
30% deposit + balancePay in full to bookFree RSVP
Balance due
14 days before start · reminders at T-30 / T-14 / T-7
03 · How guests pay you
Your payment link or instruction — relayed word for word
pay.saltandstone.com/bali-spring · or paste bank / Wise details
04 · Who signs the messages
LenaSalt & Stone Retreats
05 · Chase channels — required
WhatsAppEmail

Which channels the deposit/balance chase can use — connect the app for each in Connect.

Live preview
Hi Maya — just a gentle nudge: the balance for Bali Spring Retreat is due Mar 29. Here's the link when you're ready: pay.saltandstone.com/bali-spring — Lena sample
Frame 2 · Personalize [Activate]
  • Story — I answer a handful of questions in my own words and immediately SEE what a guest will receive, signed the way I sign.
  • Roster + round fields feed `roster_sources` + reminder scheduling (AC-COHB-01 ↗).
  • Payment field is the RELAY instruction — helper copy says "relayed word for word"; no MystFlo checkout (money guardrail).
  • Sign-off picker shows a live-rendered greeting per option, asked ONCE across the booking family (AC-SIGNOFF-06 ↗); hidden when no name is known yet (AC-SIGNOFF-07 ↗).
  • Retreat lens: `deposit_balance` is the DEFAULT here (it is hidden on the workshop card — sibling contract).
  • NEW — "05 · Chase channels" (2026-08-07, Peyton): which channels the chase can use, WhatsApp + Email both on by default. This is a DECISION only — no app connects here. Connect (sec-3) is where the actual app for each picked channel gets connected; picking a channel here without connecting its app in Connect blocks activation (same gating pattern Waitlist+Launch uses for its email-capture check).
  • 01 · One dropdown, fields on pick (Peyton 2026-09-25, #4824): the radio rows collapse into one dropdown (“Which retreat is this for?”, platform Select; rows name + price · spots, separator, “+ Something new” last). Before a pick nothing else in 01 renders. The Brain asks per offering “One-time or recurring?” (Peyton 2026-09-25 — never inferred from a date) and each dropdown row prints the answer. Recurring (drawn) → standing content fills (Name · Price · Currency · Spots · Where, Where falling back to the business address, #5249 — Where pre-fill on recurring is open for Peyton, chipped in the frame); Date and Time start empty under “Same retreat, new round? Just set when and where.” One-time → everything the Brain has fills, Date · Time · Where included; blanks stay blank; no hint. Not answered yet → fills whatever the Brain has, no hint. “+ Something new” reveals the fields empty. Nothing in the Brain → no dropdown, empty fields. Full state set drawn on workshop sec-3.
  • 01 · Picked fields stay editable (Peyton 2026-09-23, #4824 — amends the HOLD for this block only; Payment plan / cadence stay frozen): the shipped T1092 OfferingPicker (row grammar now the dropdown above — formerly radio rows over brain_profile.services[], price sub-line, ✓ on the pick, “+ Something new” dashed) now sits ABOVE Name · Price + Currency · Date (a range) · Time (optional — arrival, end optional) · Where (address, map link beneath; prefilled from the offering’s venue or else the business address, always shown, #5249) · Spots — the shared Brain offering vocabulary, one-to-one (Peyton 2026-09-23; a retreat is a program in that vocabulary, so Where + Time ride here too; “Retreat name” / “Cohort name” → Name, “Dates & spots” split into Date + Spots) — which stay visible and editable after a pick; today COHORT_PICKER_FIELD_KEYS (ConfigureStep.tsx:267) removes name + price from the form once picked — that removal is what this frame retires. A pick fills wizard configuration only; the one quiet line under Spots says the Brain is untouched. Dates stay per-round, never quick-filled. AC-COHB-01 ↗ (3-tier model) still holds; the “stays editable” clause is NEW (PRD debt) for prd-as-retreat-booking.md’s Tier-2 sentence. Where + Time follow the Workshop/Event contract exactly (AC-COHB-28 ↗ · AC-COHB-31 ↗ name this card — but the #4478 schema patch skipped RTBK01, so both fields are specified-not-built here: the build adds them to configuration_schema, it does not unhide them); only the Try-step “Add it to what you sell?” row stays drawn on the siblings, not here (scope per Peyton).
  • Booking share rail (#3789, shipped — note only, frame body stays HOLD-frozen): the shared `BookingShareLinks` component also renders in Personalize via the `cohort_booking_link` field (full + short + QR, retreat noun "spot"; never hides when the phone is missing). Drawn in full on the Pulse tab at the master (`assistant-lifecycle.html` #sec-9b-r) and at the engine home (`workshop-booking.html` 03a) — not re-drawn inside this frozen frame.
Retreat Booking TMPL-RTBK01
Everything it reads — nothing it can't explain.
Complete Setup✕
Overview› Personalize › Connect› Try
Connect apps
Communication WhatsApp Business + Resend ▴
Matches your Chase channels from Personalize — connect an app for each one you picked
Connected ⌄
WhatsApp
◉WhatsApp Business
Sends reminders from your number · +62 812 ···· 41
Email
◉Resend
Sends balance chases from your connected Resend account
⊘Gmail coming soon
AI Model MystFlo AI
Writes the chase messages in your voice. Choosing your own AI model is coming later.
Connected ⌄
Storage Google Sheets
Bali Spring Retreat roster · read at send time, never stored — MystFlo knows nothing until it next reads · any column layout works, you map your headers once; a missing column pauses with its name, never a guess
Connected ⌄
Storage MystFlo Storage ▾
Confirmed bookings live on MystFlo — visible on Pulse. Tapping the caret opens the platform's standard 3-destination picker (Google Sheet/Supabase are dead-end picks here — nothing routes them yet)
Connected ⌄
Payment Stripe ▾
Optional — only if payments should be learned from your own Stripe
Reconnect
Brain check
●Business name & voice
Salt & Stone Retreats · warm, direct
Edit in Brain →
●Cancellation policy
Answers guest cancel questions from this (AC-COHB-09 ↗)
Edit in Brain →
○Retreat dates & capacity
It needs your retreat dates before it can take a booking
Add in Brain →
! 2 of 3 ready — 1 needs you before it can take a booking
Ready to run Apps ✓ · Wallet ✓ · Brain ✕
Gap closed (2026-08-07)
  • This frame was already TOC'd under § Desktop screens but only ever drawn as a 560px .panel — no true 1180-width realization existed anywhere in this file (this file's own "desktop" convention tops out at .panel-wide, 720px — see sec-1b/sec-2). Re-drawn here at the file's own pre-defined .desktop 1180 class (present in the stylesheet, previously unused) using this file's own header chrome (.m-head/.ttabs.pre-stepper, not the foreign `.sheet-top`/`.reveal` pattern from other mockups) plus the master's two-column split (apps left flex-1, Brain Check right 400px — `assistant-lifecycle.html` sec-5). Same rows/copy as before, nothing invented.
Frame 3 · Connect [Activate]
  • Story — I see exactly which of my accounts it touches, as read-only rows I recognize — not blank fields to re-type.
  • Exactly two connection verbs platform-wide: Reconnect (host can fix) and Pending (waiting on provider) — never Verify/Refresh/Manage.
  • ONE Connect-apps list, no "Included" tier (kit ruling 2026-08-04, Option C): the AI row is label "AI Model" + badge "MystFlo AI", status Connected; the roster row carries the read-only clarity pair (no info until next read · map-your-own-columns). Master contract: `assistant-lifecycle.html` B3.
  • Caret is structural pre-activation too, on every row but AI Model (kit `design-decisions.md` 2026-08-05/06, caret scope amended 2026-08-07, Peyton-locked): the badge carries the ▾ whether or not a second option is real today — supersedes the earlier "no caret pre-activation" reading. AI Model is the one exception, no caret and no coming-soon row — `AiModelPicker.tsx` has no provider dropdown, and BYOK is gated by `BYOK_AI_HIDDEN` as a deliberate product decision, not an unbuilt roadmap item. Payment (Stripe) carries the caret pre-activation — tapping it opens the current pick as the sole selected option plus one disabled `⊘ More providers · coming soon` row, the same shape as Storage's disabled Supabase group. Communication is NO LONGER a placeholder caret (2026-08-07 amendment, same day as the Storage-label reversal below): Personalize's new "05 · Chase channels" field is a pure DECISION (which channels apply), and Communication's caret expands into one real pick-group per channel picked — WhatsApp (single provider, already connected) and Email (Resend real + connectable, Gmail `⊘ coming soon`), same T353 channels-mode grammar as `waitlist-launch.html` sec-3. Drawn expanded here. The roster row is not a Storage-role row at all (next bullet), so `DestinationPicker`'s create-new/link-existing choice does not apply to it and it carries no caret — the roster is already-chosen, read-only, and swapping it is a Personalize decision, not a Connect-row expansion. Master contract: `assistant-lifecycle.html` sec-5b. Post-activation, Communication/Payment's caret gains a third muted/live state instead — see the 6c frame note (build gap #3120).
  • Roster row is label "Storage" + badge "Google Sheets" (Peyton, 2026-08-07 — reverses this row's earlier "Roster source" naming, same day). One role vocabulary everywhere, no per-assistant invented label: every file's storage-flavored row reads "Storage," full stop, matching `assistant-lifecycle.html` sec-5/5b. Behavior is unchanged — this stays read-only (roster is bound through roster_sources: sheet | airtable | stripe + column map, read at send time, never written back — AC-COHB-06); the label change doesn't imply a write path was added. No caret — the source is bound in Personalize, not a Connect-tab picker.
  • Ruling (Peyton, 2026-08-08): "stay a separate concept." Resolves the "ripe since #3178" open question that stood here — cohort_bookings gets its OWN row (the new row above, badge "MystFlo Storage"), not merged into the roster row's badge. Read source and write destination are different mechanisms and now render as two distinct rows, matching WSBK01's own storage role shape byte-for-byte (providers: ["mystflo_storage"], single provider). Shipped: `required_roles` migration seeding `chat` + `storage` roles for TMPL-RTBK01 (previously `[]`, legacy-fallback-only) — this is the full cutover onto the required-roles schema, since `useRoleMode` is all-or-nothing; the legacy "Google Account" app requirement is dropped (no roster-picker UI exists to gate on it yet, zero live instances at migration time) and tracked as separate follow-up work, not silently lost.
  • Both rows drawn "Storage" here — a mockup-only overlap, not a real production one (found in PR #3269 review, 2026-08-08). `StorageRoleRow.tsx` hardcodes its rendered label to the literal word "Storage" (ignores the role's own `label` field — the migration sets `"Where bookings save"`, but it never renders) and always shows a caret + the full MystFlo/Google Sheet/Supabase picker, regardless of `providers.length` (2026-08-07 "ONE Storage row, THREE destinations" lock has no single-provider carve-out). Picking Google Sheet/Supabase here is a dead end today — no `configuration_schema` field routes that pick anywhere for either RTBK01 or WSBK01, so the engine keeps writing `cohort_bookings` regardless (no data loss, just a confusing tap). This is a pre-existing, already-live gap (WSBK01 has had it since 2026-08-07), not something this migration introduces — tracked separately, not fixed here. The roster row above it is mockup-only illustration (no real required_roles entry backs it — roster_sources has no frontend picker built), so today's real Connect tab shows only ONE row saying "Storage," not two.
  • Roster row states the trust contract: "read at send time, never stored" (AC-COHB-06 ↗ fire-time re-check).
  • Brain Check is compact (this assistant's scope), each row deep-links via the slide-over choreography — never a nested modal.
  • Missing Brain rows gate nothing here — Brain is never a gate (Option C); amber row + link, wizard continues.
  • Connect honesty (2026-08-15): Dates & capacity is a blocker, so the summary may say “before it can take a booking”. Attention-only (Offerings Optional / stale) must read “it can {verb} · K to review” — drawn on the master at assistant-lifecycle.html #sec-5c. Header is amber Complete Setup because Payment is Reconnect (Connect incomplete). Once Connect is done this slot becomes quiet Continue to Try — a Brain blocker does not keep the amber label. Ready-to-run bar: Apps · Wallet · Brain. Wallet pending (label, no glyph) while the card hook loads — not drawn here; see master #sec-5c.
Retreat Booking TMPL-RTBK01
One real reminder, to you — before any guest sees it.
✕
Overview› Personalize › Connect › Try
What you set up
RetreatBali Spring Retreat · Apr 12–19 · 12 spots
Payment plan30% deposit + balance, due T-14
Guests pay viapay.saltandstone.com/bali-spring
RosterBali Spring Retreat roster · Google Sheets
SignedLena
Test it
Sent to YOUR WhatsApp just now — no guest saw this
Hi Maya — just a gentle nudge: the balance for Bali Spring Retreat is due Mar 29. Here's the link when you're ready: pay.saltandstone.com/bali-spring — Lena
If your roster changes shapeColumns reordered? It heals itself. A column missing? It pauses and tells you which one — it never guesses at money data.
Activate · $1.75 per confirmed booking
Frame 4 · Try [Activate]
  • Story — I get proof it sounds like me and reads my real sheet BEFORE I commit — the test lands on my own phone.
  • Try owns the preview (Personalize-vs-Try separation, BINDING) — Personalize only echoes a sample bubble.
  • The amber notice is the schema-resilience contract in founder words (AC-COHB-13 ↗ auto-heal reorder / pause on missing).
  • Price is restated ON the activate button — price visible before commitment, always.
Retreat Booking TMPL-RTBK01
One real reminder, to you — before any guest sees it.
✕
Overview› Personalize › Connect › Try
What you’re activating
Retreat Booking
RetreatBali Spring Retreat · Apr 12–19 · 12 spots
Payment plan30% deposit + balance, due T-14
Guests pay viapay.saltandstone.com/bali-spring
RosterBali Spring Retreat roster · Google Sheets
SignedLena
Switching on with it
On-Brand SupportAnswers your guests’ questions · a booking link or QR tap goes straight to the form
Greeting“Hi! How can I help?” ✦AI
Routes to yourefunds · anything it can’t answer
Braincovers both — Support reads every section✓ N of M ready
Wallet$86.40 · $x per booking · $y per Support session

Test it — unchanged from frame 4 (one real reminder to your own WhatsApp · roster-shape notice).

Activate both

Both run on your WhatsApp from the moment you activate. Each is charged only when used.

Differs from Workshop 3s C: the booking box keeps only retreat rows; Greeting is the real system line (“Hi! How can I help?” + ✦ AI) and lives with Routes in the Support box only; Brain and Wallet appear once, after Support; no Support Name row.
Activating "Bali Spring Retreat"
✓Reading your roster — 9 rows found
✓Scheduling reminders from your due dates
Connecting your WhatsApp number
Final check
✓
"Bali Spring Retreat" is live

3 guests already owe a balance — the first reminders are scheduled for tomorrow 9am, their time.

View in Pulse
Frame 5 · Activation outcomes [Activate]
  • Story — The loader tells me what it's actually doing with MY data, and "live" tells me the first concrete thing that will happen.
  • Loader steps name real pipeline stages, in founder words — no spinner-only state.
  • Success state ALWAYS names the next event ("first reminders tomorrow 9am") — no dead ends.
  • Wallet-can't-cover at a later Mark-paid returns 402 `topup_required` and HOLDS the booking (AC-BILL-03 ↗) — drawn in sec-6b, not invented here.
768 not yet drawn for this flow. Desktop and 375 are the contract.
Explore dims behind the sheet
Retreat Booking
Personalize · step 2 of 4 — the stepper strip scrolls horizontally at 375
✕
01 · Your retreat
Which retreat is this for? dropdown · #4824

Edit what you sell in your Brain →

Name *
Bali Spring Retreat
Price
1700
Currency
USD

Same retreat, new round? Just set when and where.

Date — a range · per round
Pick the dates
Time — optional
Arrival — e.g. 15:00
Where * always shown · #5249 pre-fill · open for Peyton
Villa Sari, Jl. Raya Sayan, Ubud, Bali
https://maps.app.goo.gl/3vR8kQwLm2P… · map link, optional

Starts as the offering’s venue from your Brain, or your business address — change either if this round is somewhere else.

Spots
12

Edits here change only this retreat — your Brain stays as it is.

Field names match your Brain’s offering fields one-to-one — Name · Price · Date · Time · Where · Spots.

02 · Deposit & balance
30% deposit + balancePay in full
Your payment link — relayed word for word
pay.saltandstone.com/bali-spring
Continue → Connect
Pulse, mobile
Active
⏸⚙
Bali Spring Retreat ASST-7R2
Retreat Booking
1 booking needs your attention
This retreat
9 booked · 6 paid · 2 on deposit · 1 overdue
Maya K Balance overdue · $1,680Mark paid
Frame 8 · 375 tier [Activate + Monitor]
  • Wizard is a full-height bottom sheet over the dimmed page (sheet standard, PR #2346 chrome) — single column, groups stack in order.
  • All touch targets ≥ 44px; verdict buttons full-row on phones.
  • Same content as desktop frames — content scales fluidly, structure collapses at named breakpoints only (U8).
Explore dims behind the sheet
Retreat Booking
✕
OverviewPersonalize Connect Try
What you’re activating
Retreat Booking
RetreatBali Spring Retreat · Apr 12–19 · 12 spots
Payment plan30% deposit + balance, due T-14
Guests pay viapay.saltandstone.com/bali-spring
RosterBali Spring Retreat roster · Google Sheets
SignedLena
Switching on with it
On-Brand SupportAnswers your guests’ questions · a booking link or QR tap goes straight to the form
Greeting“Hi! How can I help?” ✦AI
Routes to yourefunds · anything it can’t answer
Braincovers both — Support reads every section✓ N of M ready
Wallet$86.40 · $x per booking · $y per Support session

Test it — unchanged from frame 4 (one real reminder to your own WhatsApp · roster-shape notice).

Activate both

Both run on your WhatsApp from the moment you activate. Each is charged only when used.

Differs from Workshop 3s C: the booking box keeps only retreat rows; Greeting is the real system line (“Hi! How can I help?” + ✦ AI) and lives with Routes in the Support box only; Brain and Wallet appear once, after Support; no Support Name row.
Active
⏸⚙
Bali Spring Retreat ASST-7R2
Retreat Booking
1 booking needs your attention
This retreat
9 booked · 6 paid · 1 overdue
Needs your attention
Maya K Overdue · $1,680Mark paid
Recent activity
Payment learned — Aria S Confirmed · WhatsApp + email$1.75
Frame 9 · 768 tier [Monitor]
  • At md the card and its attention/activity panel sit side by side — the grid gains a column; nothing else changes (structure steps only at named breakpoints, U8).
  • This is a SAMPLE of the continuum, not a third design — a build that snaps awkwardly at 700 or 900 fails the tier contract even if 375/768/desktop all pass.
7 · Components the reusable parts library this surface draws from — reference only; every part listed here is proven in context in sections 5/6, never introduced here alone (8-section core rule).
  • Explore / Team card: `AssistantSummaryCardPre` (pre-activation taxonomy, BINDING) — sec-1.
  • Detail sheets (twins, archetype `../../archetypes/assistant-detail-tabs.md`): `AssistantDetailModalPre` (sec-1b, stepper tabs) and `AssistantDetailModalPost` (sec-6c, plain-nav tabs — Pulse · Overview · Personalize · Connect, R30) — both on `AssistantModalShell` (never `ModalWrapper` for the detail modal); `AssistantSummaryCardPost` carries the Pulse card that opens the post sheet — sec-6.
  • Wizard steps: `ConfigureStep` → `ConfigFieldRenderer` (config-schema-driven Personalize, sec-2/8), Connect rows + compact Brain Check module (sec-3), Try readback + test panel (sec-4).
  • Cross-reference choreography: "Edit in Brain →" rows use `PageSlideOver` / the 60/40 split — never a nested modal (overlays.html contract).
  • Attention + verdicts: `AttentionPanel` booking rows — Mark paid always; Extend due date / Release spot gated on overdue (sec-6/6b); Release styled as text-only destructive (`ConfirmDialog` convention).
  • Guest thread frames: the WhatsApp chat vocabulary (`.wa-*`/`.bub`, locked-template dashed bubbles) shared verbatim with on-brand-support.html / workshop-booking.html — sec-7.
  • Records component (future): the Attendees surface inherits `RecordsTable`/`RecordRow` per `../../archetypes/records-surface.md` — one component, noun is a prop. Since 2026-09-02 (R36) it renders as a block inside the post sheet's Pulse tab plus a nested `ModalWrapper` sheet (`nestedBackdrop`, `broadcastCloseAll={false}`), with the in-card slot on `AssistantSummaryCardPost` as the way in — never a routed page, never a fifth tab.
Master contract + sources
  • assistant-flows/assistant-lifecycle.html is the primary structural contract; assistant-flows/workshop-booking.html is the engine sibling AND the canonical structural reference (design-canon.md § "Assistant-flow mockup structure — 8-section shape", Peyton-locked 2026-08-06); token block borrowed verbatim from on-brand-support.html.
  • Sources: prd-as-retreat-booking.md §Test Plan (AC-COHB-* / AC-BILL-* / AC-SIGNOFF-*) · rename migration 20260802012707_rtbk01_rename_cohort_to_retreat.sql · _shared/chat-handler.ts booking engine · AttentionPanel.tsx verdict gating · ledger ../parity/parity-retreat-booking.md.
Live
Active
⏸⚙
Bali Spring Retreat ASST-7R2
Retreat Booking
1 booking needs your attention
This retreat
9 booked · 6 paid in full · 2 on deposit · 1 overdue
Attendeeswho's coming, who still owes you9 booked · 3 left ›
Needs your attention
Maya K Balance overdue — was due Mar 29 · $1,680 outstandingMark paid
Recent activity
Balance reminder sent — Jonas P T-14 · re-checked roster first, still unpaidApr 1 · 9:00
Payment learned from your sheet — Aria S Confirmation sent · WhatsApp + emailMar 30 · $1.75
Reminder skipped — Tomas B Roster showed paid at send time · skipped_paidMar 30
Frame 6 · Pulse card + feed [Monitor]
  • Story — One glance tells me who's paid, who's owing, and the single thing waiting on me.
  • Card shell is `AssistantSummaryCardPost` — identical shell across Pulse, sub-status drives visuals (BINDING lifecycle model). Status stays a dot + Active; the amber line is a meta line, not a status. Tapping the card opens the post detail sheet (sec-6c) on its Pulse tab.
  • The $1.75 rows make billing legible in the feed — the meter fires ONLY on paid → confirmed (AC-BILL-01 ↗), skips/cancels are visibly free (AC-BILL-04 ↗).
  • `skipped_paid` row surfaces the fire-time re-check promise (AC-COHB-06 ↗).
  • Not-yet-overdue rows offer ONLY Mark paid — extend/release appear at overdue (sec-6b), matching shipped `AttentionPanel` gating.
  • Attendees slot IN the card (2026-09-02, R36): last zone after "This retreat" — collapses to zero on a non-records card, never for guests; taps into the post sheet (sec-6c) with the Attendees block under the numbers and the full list as a nested sheet (master 8d · 9b-r · 9e). Not built.
Maya K — balance overdue since Mar 29One overdue notice was sent Mar 30. Reminders are on hold. Nothing changes until you decide.
Booked2 spots · deposit paid Feb 12
Outstanding$1,680 balance
Last reply"traveling, will pay next week" · Mar 27
Mark paid Extend due date Release spot

Mark paid confirms her and sends the confirmation · Extend picks a new date and resumes reminders · Release frees the spot and tells her kindly.

Frame 6b · Overdue verdict [Decide]
  • Story — When money is on the line, I make the call in one tap — the assistant holds everything until I do.
  • Exactly the shipped verdict set: Mark paid · Extend due date · Release spot (AC-COHB-08 ↗) — never a unilateral release (AC-COHB-07 ↗). Read + verdict, never edit (Records-Layer rule).
  • Release is a text-only destructive action (no filled red button) — overlays.html confirm-dialog convention.
  • Each verb's consequence is written under the buttons — no mystery taps.
  • If the wallet can't cover the $1.75 at Mark paid: 402 → "Top up to confirm" state, booking stays held, nothing lost (AC-BILL-03 ↗).
Revisit · 2026-09-04 (#4432 Track 2, updated same day — locked mirror supersedes the ledger) — header + Personalize are drawn ONCE in the masterThe post detail header is the shared four-line hierarchy (assistant-lifecycle.html 9g / 13g: subject · “Retreat Booking Assistant · id” · status · price · live since · activity — no health line, no repeated type word). This template's post Personalize tab is master frame 9c / 13c, the locked mirror — same visual structure as this file's own pre-activation Configure step, every field showing its saved state, nothing clickable — with this file's own categories (cohort · payment · links; booking_keywords hidden). Not redrawn here on purpose.
RECURRING SURFACE · drawn once in the master
Tapping the Pulse card (sec-6) opens the full detail sheet — the visual TWIN of the pre sheet (same shell, same tab family, same language — shell-language #7). Retreat Booking's Pulse tab is the master's BOOKING (records) fill of the ONE Pulse-tab template — roster FIRST in the right slot, then the booking link/share/flyer.
assistant-lifecycle.html #sec-9b-r ↗
  • Noun = Attendees; retreat persona noun for the booking link itself is spot ("Hi! I'd like to book a spot for {retreat}"). Verdict copy (Mark paid · Extend due date · Release spot) is unchanged from sec-6b.
  • Booking-link rail (full wa.me/…?text= + Copy, short wa.me/{digits} + Copy short, QR + Download QR) and its not-connected state ("Connect WhatsApp to generate your booking link and QR") are the same component as every booking-type sibling — drawn once at the master, not forked per assistant.
  • Overview/Personalize/Connect tab bodies (word-for-word sec-2's Overview + 5-node flow, sec-5's read-back set, Connect apps + Brain check rows) are unchanged from the prior drawing — not redrawn here since they aren't part of the Pulse-tab template this pass touches; the muted-caret Connect mechanic is master contract assistant-lifecycle.html sec-9d (build gap, pending #3120).
Twins rule (shell-language #7) and R28/R29/R30 apply unchanged — Overview is word-for-word the pre Overview, Personalize carries the read-back, Connect tabs are unchanged from the prior drawing.
768 not yet drawn for this flow. Desktop and 375 are the contract.
This step is 1180. No 375 drawing — use the Desktop size.
Guest
This step is 375. No 1180 drawing — use the Mobile size.
768 not yet drawn for this flow. Desktop and 375 are the contract.
Guest phase — WhatsApp mobile-only by nature, inside the Mobile journey. Four beats: capture & deposit (7a) · balance chase (7b) · "I already paid" (7c) · overdue & hold (7d).
Frame 7a · Capture + sold-out [Guest · Trigger]
  • Story — As a guest I book in the same thread I asked in — no form link unless WhatsApp's native form is available.
  • Name + email are the real capture gate — no booking row without both; seats re-checked at submit.
  • Over-capacity requests are refused and waitlisted (AC-COHB-20 ↗) — never a silent overbook.
  • The AI discloses itself in the sender line; deposit ask relays the HOST'S link (AC-COHB-03 ↗ relay model).
  • Two live booking cards on one number (#3789, shipped): when this retreat shares the WhatsApp number with another live booking card (e.g. a workshop), a bare "book" or a "what's on?" gets the deterministic name+date list — never the wrong card's form. A unique full program name ("Bali Spring Retreat") still routes straight here. Thread drawn once at the engine home: `workshop-booking.html` sec-9 · Mode D. Concurrent cards are supported product usage; single-card isolation in QA walks is a test constraint, not the design story.
Frame 7b · Balance reminder → paid → confirmed [Guest · Interact/Outcome]
  • Story — The nudge reads like Lena, not like a bank — and the second I pay, I KNOW I'm in.
  • Outside the 24h window only approved Meta templates send — the dashed bubble marks locked template copy (AC-COHB-14 ↗).
  • Payment learned (sheet tick / Stripe event / Mark paid) → WhatsApp + email confirmation within 30s (AC-COHB-04 ↗); exactly ONE $1.75 meter, deduped (AC-BILL-01 ↗ / AC-BILL-02 ↗).
  • Sign-off follows the host's stored preference on every channel (AC-SIGNOFF-01 ↗).
Frame 7c · Claimed payment [Guest · Interact]
  • Story — When I say I've paid, I'm believed enough to stop the chasing — but the host's ledger stays the truth.
  • Roster re-read on the spot; found → confirm + meter; not found → honest "flagged for Lena" + host ping — NEVER auto-confirmed on the guest's word (AC-COHB-10 ↗).
  • Ambiguous roster values stay `pending`, never accidentally paid (AC-COHB-05 ↗).
Frame 7d · Overdue notice [Guest · Fallback]
  • Story — Even overdue, I'm treated like a guest, not a debtor — and there's always a human path.
  • Exactly ONE overdue notice; then reminders HOLD and the host is pinged with a Pulse deep-link (AC-COHB-07 ↗) — the verdict is sec-6b's, never the engine's.
  • Stale roster ≥7d holds guest sends entirely and nudges the host instead (AC-COHB-12 ↗) — no chase fires off stale money data.
8 · Future design never a build target Part II · Future under its plain-language name (kit canon U9) — deferred scope with PRD anchors, compact cards only, no drawn screens. Sections 1–7 collectively are the Active build contract.
Attendees — the records surface AC-COHB-17 ↗ / AC-COHB-18 ↗ · not built
  • Re-cut 2026-09-02 (Peyton, R36 — supersedes "a Pulse row opening a full-screen list"): a slot IN the Pulse card ("Attendees · 9 booked · 3 left ›") opens the post sheet on its Pulse tab, where an Attendees block (≤5 rows, needs-verdict first, "See all N ›") sits between the metrics and the activity; "See all" rises a nested, scrollable sheet with every booking, status, filter chips, pagination, and CSV export in the header band — deep-linked as ?assistant=<id>&records=open. Read + verdict only — the verdict set stays exactly Mark paid · Extend · Release; no editable cells ever (an editable row is a CRM). Frames live on the engine home (workshop-booking.html sec-13) and the master (8d · 9b-r · 9e); not built.
Mirror write-back AC-COHB-19 ↗ · not built, M2
  • The existing roster READ is not a mirror. When write-back ships it is offered at activation, defaults OFF, never auto-provisioned.
Booking-invite line as a founder field AC-COHB-16 ↗ · field not built · runtime default shipped
  • `copy_flow_intro` inherits from the shared engine unchanged — same Personalize textarea as the workshop card, one text for every entry path. The RTBK01 Personalize field itself is still unbuilt, but the engine's runtime default already names the program + date ("{{workshop_name}} — {{date}}. Tap below to grab your spot:", #3789 — `chat-handler.ts` `FLOW_INTRO_DEFAULT_TEMPLATE`), so retreat guests on the native Flow see "Bali Spring Retreat — Apr 12, 2026. Tap below to grab your spot:" today.
Known deltas between this contract and shipped code — for the parity ledger, not guesses
  1. Verdict gating is drawn as shipped: a not-yet-overdue row offers only Mark paid; Extend due date + Release spot appear at overdue (`AttentionPanel.tsx` gating). A pre-overdue "nudge" action is NOT built and is deliberately not drawn.
  2. Meta template names (`retreat_balance_reminder`, `retreat_overdue_notice`) are illustrative labels for the locked-template convention — exact production template names/bodies live in the workshop-templates/engine layer and are graded by the parity ledger, not asserted here.
  3. Detail-sheet tab set (Overview · How it works · Apps) follows the master `assistant-lifecycle.html` pre-detail contract — if that master changes, this file inherits, never forks.
  4. Sold-out waitlist copy (7a right phone) is the AC-COHB-20 ↗ behaviour in Brain voice — shipped fallback wording should be graded against it at render-check.
  5. The LIVE `details.flow` seed lags this contract on two counts — n1 still says "cohort" (the 2026-08-02 rename migration never touched `details.flow`), and it has no chase node (5 nodes vs the 6 drawn here — the chase leg was ruled INTO the public chart by Peyton, 2026-08-04). ONE catalog-copy sync migration (+ PRD same-PR per the guard) closes both.
  6. sec-2 Personalize payment-plan content is ON HOLD pending a Peyton ruling (2026-08-04). DB + spec verification found the drawn "Payment plan" selector and editable-looking reminder copy are NOT in the live `configuration_schema`, and the chase cadence is fixed, not founder-configurable; the spec'd-but-unbuilt gap is a roster-source picker + column map (build-spec:389). Direction TBD (build the drawn UI / build the spec'd roster picker / match what's live) — the drawn content is deliberately frozen as-is until the ruling; do NOT build Personalize from this frame yet.

Parity ledger: ../parity/parity-retreat-booking.md · PRD: docs/product/assistants/shared/prd-as-retreat-booking.md · Engine sibling: workshop-booking.html · Master: assistant-lifecycle.html