The acceptance oracle for the 2026-07-28 booking round. Two things get proven with pixels
rather than with code reading: (1) a lead who books through the custom booking app lands in BOTH the
Notion touchpoint history AND the pipeline dealstore, and (2) a meeting booked BY HAND in Google
Calendar does the same. Everything else here is regression cover for the copy round shipped today on
claude/sales-reminder-email-copies-57920b (60bb7f3b, c4d39f8c,
c74c9732). Walk arena is staging; production is touched only by the landing step.
@example.com are
driven · AUTOSEND_ENABLED stays off · Notion destructive ops are archive-only, never delete ·
Playwright drives every browser step (Chrome DevTools MCP only for Notion and Google Calendar, which need
Mátyás's logged-in Chrome) · every booking-app screen is judged at desktop 1280, tablet 768 and mobile 390,
because the booking page is the one surface a real lead sees on a phone · every artifact this run creates is
purged afterwards and the purge is re-read to prove it.google_ws.py:236-243 → send_ledger.py:424-431,
recorded reason send-guard blocked (test recipient) — verified live on the staging store), so with ZZ sentinel addresses the rendered copy is produced and recorded but
nothing is delivered to an inbox. That is the correct, safe behaviour for QA and it is what this EBO
asserts: the exact subject and body the product produced for that send, read off the dash in pixels. A real
lead (an address that is not test-shaped) is unaffected. Proving actual inbox delivery would require sending
to a non-sentinel address, which this run deliberately does not do.gcal_watch
(app/flows_calls.py:954-1000) writes only gcal_event_id onto the deal and
logs a Notion touchpoint. It never writes sales_call_date and never moves the stage to
appt_booked. So a meeting Mátyás books by hand lands in the touchpoint history but the pipeline
never learns the call date. S3.4 asserts the whole invariant — the meeting must reach the dealstore too — and
is therefore expected to fail on today's build. Signing this document authorises building that fix.
flows.py:2721 then flows.py:2815) rather than patching
in place; a true patch exists only on the operator dash path (flows.py:2663). The observable
invariant that matters — exactly one live event, no duplicate — is what S2.4 asserts. If Mátyás wants a true
in-place patch on the lead path, that is a separate change and should be said before signing.| # | You do | You should see | Element that changes | Underneath | Must NOT happen | 🕓 History |
|---|---|---|---|---|---|---|
| 1 | Open the staging booking root with no deal or token query parameters, at desktop width 1280. |
The booking page for a cold visitor: the heading "Foglalj időpontot", and an identity form asking Neved, E-mail and Telefonszám (opcionális). | The identity fields are editable and empty — not pre-filled from a deal. | No deal is touched; the page is read-only until submit. | No pre-filled name or e-mail belonging to somebody else; no error banner; no "Időpontok betöltése…" stuck forever. | 🕓 Nothing is written yet. |
| 2 | Reload the same generic booking root at mobile width 390. | The same three fields and the slot area, stacked and fully readable on a phone — every label and the confirm button inside the viewport. | Layout reflows to one column. | Nothing. | No horizontal scrollbar, no field or button cut off at the right edge, no text overlapping another element. | 🕓 Nothing is written yet. |
| 3 | Fill the form as the sentinel lead ZZ-BOOK-S1 Kft / zz-book-s1@example.com and let the slot list load. |
A grid of selectable appointment slots, each showing a day and a time. | The slot grid replaces the "Időpontok betöltése…" placeholder. | Still nothing written — slots are read from the calendar. | No empty slot area, no "Időpontok betöltése…" left on screen, no raw JSON or stack trace. | 🕓 Nothing is written yet. |
| 4 | Pick a slot and press the confirm button (Foglalás megerősítése). | The thank-you screen: "Foglalás megerősítve!", showing the booked time and a Google Meet joining link. | The form is replaced by the confirmation panel. | A deal now exists for zz-book-s1@example.com. |
No error message, no blank page, no raw meet.google.com URL shown as unformatted text where a link is expected. |
🕓 A booking touchpoint is written. |
| 5 | Open the dash Pipeline board on staging with the dash token and find the sentinel lead. | A card for ZZ-BOOK-S1 Kft sitting in the Booked / Sales Call Prep column, showing the booked call date and time. | The card appears in the Booked column with its call date. | The deal carries stage_key = appt_booked, a sales_call_date and a meet_link. |
The lead must NOT still sit in a New-lead column; the card must NOT show an empty or missing call date. | 🕓 Board reflects the booking. |
| 6 | Open that lead's touchpoint history panel on the dash. | A booking entry for ZZ-BOOK-S1 Kft reading "meeting booked to" with the date and time, and a Google Meet invite entry. | The history panel lists the booking event. | The entries are real Notion activity-log rows, not local state. | No empty history panel; no "no activity yet" message for this lead. | 🕓 This step IS the touchpoint-history half of invariant 1. |
| 7 | Open that lead's 🕓 Full history drawer on the dash and find the confirmation e-mail the booking produced. | Subject "Sikeres Foglalás", and a body line reading "Az időpontot rögzítettük a következő dátumra:" followed by a FULL Hungarian date — month name, day number, day abbreviation in brackets and the time, in the shape július 29. (Sze) 15:30. |
The sent-mail entry shows the rendered subject and body. | The send row is recorded under category booking_confirmation. |
The date line must NOT show a bare time such as 15:30 with no date; the subject must NOT be a generic invite subject; no unrendered {{token}} placeholder anywhere in the body. |
🕓 Copy round: the "Sikeres Foglalás" receipt. |
| 8 | In the browser, open the Notion CRM row for this lead and look at its linked activity/touchpoint entries. | A CRM row named ZZ-BOOK-S1 Kft exists, and its linked activity carries the booking entry with the meeting date. | The Notion page shows the lead and its activity relation. | The Notion row is the same one the pipeline wrote to. | No missing CRM row; no CRM row with an empty activity relation. | 🕓 Proven in the Notion UI itself, not from the API alone. |
| # | You do | You should see | Element that changes | Underneath | Must NOT happen | 🕓 History |
|---|---|---|---|---|---|---|
| 1 | Open the reschedule link belonging to ZZ-BOOK-S1 Kft (the personalised booking URL carrying its deal and token). | The booking page in reschedule mode, showing the lead's own details and the currently booked time. | Identity fields are pre-filled for this lead. | Nothing written yet. | No empty form; no other lead's name or e-mail; no "invalid token" error. | 🕓 Nothing written yet. |
| 2 | Pick a DIFFERENT slot and confirm the change. | The confirmation screen "Időpont átütemezve!" showing the NEW time. | The page switches to the reschedule confirmation. | The deal's call date is rewritten to the new slot. | No error; the screen must NOT still show the old time. | 🕓 A reschedule touchpoint is written. |
| 3 | Re-open the dash Pipeline board and look at the same card. | The ZZ-BOOK-S1 Kft card now shows the NEW call date and time, still in the Booked column. | The card's call date changes to the new slot. | sales_call_date now holds the new slot and the reschedule counter has moved. |
The card must NOT show the original time; there must NOT be a second card for the same lead. | 🕓 Board reflects the move. |
| 4 | Open matyas@clientsflow.hu's Google Calendar in the browser and look at the day the call now sits on, and at the day it used to sit on. | Exactly one meeting for this lead — on the new day. The old day carries no leftover meeting for this lead. | One calendar event, at the new time. | The deal tracks a single live calendar event. | There must NOT be two meetings for this lead; the original slot must NOT still hold an event for it. | 🕓 No duplicate is the invariant here. |
| 5 | Open the same 🕓 Full history drawer and find the e-mail the reschedule produced. | Subject "Sikeres Átütemezés", and a body line reading "Az új időpontot rögzítettük a következő dátumra:" followed by the full Hungarian date of the NEW slot. | A second, distinct sent-mail entry appears. | The row is recorded under category booking_reschedule, separate from the original confirmation. |
The subject must NOT be "Sikeres Foglalás" — a lead who moved the call must not get the original confirmation copy again. No bare time without a date. | 🕓 Copy round: the distinct reschedule receipt. |
| 6 | Re-open the lead's touchpoint history panel on the dash. | Both the original booking entry AND a later entry recording the move to the new time. | A new history entry is appended. | The reschedule is recorded in the Notion activity log. | The original booking entry must NOT have been overwritten or removed. | 🕓 History keeps both moments. |
| # | You do | You should see | Element that changes | Underneath | Must NOT happen | 🕓 History |
|---|---|---|---|---|---|---|
| 1 | Look at the dash Pipeline board for the sentinel lead ZZ-GCAL-S3 Kft, which exists in the pipeline but has no call booked. | A card for ZZ-GCAL-S3 Kft that is NOT in the Booked column and shows no call date. | Nothing — this is the before picture. | The deal has no call date and is not at the booked stage. | The card must NOT already show a call date — that would make the rest of this scenario meaningless. | 🕓 The honest before-state. |
| 2 | In matyas@clientsflow.hu's Google Calendar, create a meeting by hand — no booking app, no dash — with a Google Meet link and zz-gcal-s3@example.com as the guest. |
The new meeting visible in Google Calendar with its Meet link and that guest. | A calendar event exists that the pipeline has never seen. | Nothing in the pipeline yet. | The guest must NOT be missing — the pickup matches on the guest's e-mail address. | 🕓 Nothing in the pipeline yet. |
| 3 | Trigger the calendar pickup on staging. The ten-minute poll that normally does this is deliberately inert on staging (Modal's scheduled-function cap), so it is invoked explicitly through the product's own path — the same function the production cron calls, running inside the deployed staging app. | The pickup reports that it ran and logged the event. | The pickup completes without error. | The pipeline has now seen the manually created event. | It must NOT report a calendar access error or skip the event. | 🕓 The pickup runs. |
| 4 | Open the lead's touchpoint history panel on the dash. | A booking entry for ZZ-GCAL-S3 Kft reading "meeting booked to" with the meeting's date and time, marked as coming from a manual calendar event. | A new history entry appears for a meeting nobody booked through the app. | A Notion activity row was written for the manual event. | No empty history; no entry attributed to the booking app. | 🕓 This is the touchpoint half of invariant 2. |
| 5 | Re-open the dash Pipeline board and look at the ZZ-GCAL-S3 Kft card. | The card now sits in the Booked / Sales Call Prep column and shows the date and time of the meeting Mátyás created by hand. | The card moves into Booked and gains the call date. | The deal now carries the meeting's sales_call_date and stage_key = appt_booked. |
The card must NOT still sit outside the Booked column, and must NOT show an empty call date — a meeting the pipeline cannot see is the exact failure this scenario exists to catch. | 🕓 This is the dealstore half of invariant 2 — expected to fail on today's build. |
| # | You do | You should see | Element that changes | Underneath | Must NOT happen | 🕓 History |
|---|---|---|---|---|---|---|
| 1 | Open the scheduled-reminder entry in the touchpoint history of the first-call lead ZZ-BOOK-S1 Kft. | Three scheduled reminders — three days before, one day before and two hours before — each carrying a line "Megbeszélés időpontja:" followed by a full Hungarian date in the shape július 29. (Sze) 15:30. |
The reminder preview lists all three with dated times. | All three reminder bodies render the date label, not a bare clock time. | No reminder may show a bare time such as 15:30 with no date; no unrendered {{time}} placeholder. |
🕓 Copy round: dated meeting times. |
| 2 | Read the FIRST-call reminder bodies for the same lead. | The first-call pitch is present: the paragraph about the design (látványterv) and the paragraph about the top 50 keyword research. | The first-call bodies keep their sales content. | These are the first-call reminder bodies. | These paragraphs must NOT be missing for a first call. | 🕓 First-call copy intact. |
| 3 | Open the scheduled-reminder entry for ZZ-PROP-S5 Kft, a lead whose deal is already at or past the proposal-sent stage. | Reminders that still carry the dated "Megbeszélés időpontja:" line, but WITHOUT the first-call pitch. | A different, neutral reminder body set is used. | The second-call reminder bodies are used for a post-proposal deal. | The words látványterv and top 50 must NOT appear — pitching the first-call material to somebody who already has the proposal is the bug this step catches. | 🕓 Copy round: second-call reminders. |
| # | You do | You should see | Element that changes | Underneath | Must NOT happen | 🕓 History |
|---|---|---|---|---|---|---|
| 1 | Negative control — run this BEFORE the silence step. Look at the 🕓 Full history drawer for ZZ-BOOK-S1 Kft, the ordinary pre-proposal lead that booked in S1. | A confirmation e-mail entry with the subject "Sikeres Foglalás" is present. | The history drawer shows a sent confirmation for this lead. | A booking-confirmation send row exists for a pre-proposal booking. | Nothing — this step exists to prove the check can see a confirmation when one is produced. If this step cannot find one, the silence step below proves nothing and the run stops. | 🕓 Proves the probe rings. |
| 2 | Book a meeting for ZZ-PROP-S5 Kft, whose deal is already at or past the proposal-sent stage, through the same booking app. | The booking confirms on screen exactly as it does for any other lead. | The booking succeeds; the deal gets its call date. | The meeting is recorded, but no confirmation e-mail is produced. | NO confirmation e-mail entry may exist for this lead — not with the subject "Sikeres Foglalás", not with any other subject. After the proposal, Mátyás sends those details by hand. | 🕓 The silence half. |
| 3 | Open the calendar event that booking created, in matyas@clientsflow.hu's Google Calendar. | The meeting exists, with Mátyás on it. | The event is created as normal. | The lead was not invited to the calendar event. | zz-prop-s5@example.com must NOT appear in the event's guest list — no calendar invite reaches a lead after the proposal. |
🕓 The no-invite half. |
| # | You do | You should see | Element that changes | Underneath | Must NOT happen | 🕓 History |
|---|---|---|---|---|---|---|
| 1 | Open the dash Templates panel. | Rows for the booking reschedule invite text, the booking confirmation subject, the booking reschedule subject, and the three second-call reminder bodies (three days, one day, two hours). | The new copy keys are listed and editable. | Each row maps to the copy key the product actually sends. | None of these rows may be missing; no row may show an empty body. | 🕓 Copy round: Templates inventory. |
| 2 | Open the dash Journey copy map and find the Meet-invite step. | The step lists the booking copy: the event title, the confirmation subject, the invite description, the reschedule subject and the reschedule invite text. | The Meet-invite step points at the booking copy set. | Editing there edits what actually sends. | The step must NOT be empty and must NOT point only at the old single invite text. | 🕓 Journey map wired to real copy. |
| 3 | In the same Journey copy map, find the Pre-call reminder step. | The step lists the six reminder bodies that actually send — the three first-call bodies and the three second-call bodies. | The Pre-call reminder step points at the six live bodies. | Editing there changes the reminder a lead receives. | The step must NOT point at the legacy reminder body that no send site reads — that was the bug fixed today, where editing the reminder text changed nothing the lead received. | 🕓 Copy round: the journey fix. |