ClientsFlow Pipeline · EBO Extension

Extended EBO — B1–B6 + Touchpoint History + UX Critique

Pipline live-test bug backlog · Scenarios S1–S9 · 2026-06-25 · Sources: bnf-pipelinetest-2026-06-24 + bnf-finish-2026-06-24

You do → click/action You should see → on-screen result Element changes → copy · look · where What changes underneath → data/state Must NOT happen → bug guard 🕓 Touchpoint history → what the card's history panel shows 💡 UX critique + suggestion → what's unclear + how to improve
Two new columns (highlighted)🕓 Touchpoint history describes the expected entry in the card's history/touchpoints panel after each action. 💡 UX critique + suggestion gives a specific UX problem and a concrete improvement idea for each step. All existing five columns are unchanged from the signed-off EBO.

S1 — B1 — Cancellation via the email link (happy path)

Who: Réka — a lead who booked but must cancel  ·  When: Réka has a Booked appointment and opens the booking email; the card sits in the Booked column.

# You do You should see Element that changes
copy · look · where
What changes underneath Must NOT happen 🕓 Touchpoint history 💡 UX critique + suggestion
1 Réka clicks the 'Lemondás' / cancel link in her booking email (GET /cancel?token=… on the booking app) A confirmation page: the appointment was cancelled — not an error Copy: "Időpont lemondva" · Look: plain confirmation, no error styling · Where: full-page on booking app cancel_booking runs on the ZZ deal No error thrown; no NEW booking confirmation email sent NEW ENTRY
Type: Lemondás · label: "Lemondás · {date}" · icon: 🚫 · timestamp: now (Budapest)
Critique: The cancel link text in the email is generic ("Lemondás") — no time/date shown in the link itself, so Réka might not be sure which appointment she's cancelling.
Suggestion: Include the appointment datetime in the link text: "Lemondás (2026. júl. 3. 10:00)" and on the confirmation page show the cancelled time prominently alongside the ✓.
2 Card moves OUT of Booked INTO 'Contacted — No Appointment Booked' ZZ card appears under 'Contacted — No Appointment Booked', GONE from Booked Copy: card title under 'Contacted — No Appointment Booked' · Look: normal board card in booking_fup column · Where: pipeline board stage_key set to value mapping to booking_fup; NOT ghost 'contacted' key Card must NOT stay in Booked; must NOT land in New Lead No new touchpoint for the column move itself — the cancellation touchpoint from step 1 is the record. History panel now shows the Lemondás entry at the top. Critique: The column name "Contacted — No Appointment Booked" is long and ambiguous for a cancel scenario — a just-cancelled lead looks identical to a lead who was never booked.
Suggestion: Either (a) add a prominent RED "Lemondott" badge on the card face as the main visual signal, or (b) create a dedicated "Lemondott" sub-column / filter view so cancelled leads are visually separated from never-booked ones.
3 RED 'Lemondás' label + cancellation date appears on the card RED label reading 'Lemondás' + cancellation date on the ZZ card Copy: "Lemondás · {date}" · Look: RED label/badge · Where: card label/badge area Deal stores cancellation flag + date → drives red label render Label must NOT be neutral/blue; date must be cancellation date, NOT original booking date History panel already shows the Lemondás entry from step 1. The red label on the card face is the board-level signal; the history entry is the detailed record. Critique: The red label is correct but sits in the same visual slot as regular tags — easy to miss if the card has several other tags crowding it.
Suggestion: Make the Lemondás badge slightly wider/bolder or pin it to the top-right corner of the card (as a ribbon/flag), so it can never be visually buried by other badges.
4 Card history shows a proper CANCELLATION entry (not 'Call booked' tag) Newest history entry tagged as a CANCELLATION — 'Lemondva' / 'Cancelled' Copy: history row tag = cancellation label · Look: visually distinct from booking tag · Where: card's history/touchpoints list Touchpoint Type is cancellation (not 'booking') → HIST_LABEL renders correctly History tag must NOT read 'Call booked'; description text stays correct The Lemondás touchpoint shows: icon 🚫 · label "Lemondás" · "Időpont lemondva" as description · exact timestamp. Clicking the row expands to show the full detail if any note was added. Critique: The history panel currently has no visual hierarchy — all touchpoint types (booking, cancellation, email, note) use a similar pill badge style, making it hard to scan quickly.
Suggestion: Assign a consistent colour-coding per touchpoint type: red = cancellations/negatives, green = bookings, blue = emails, grey = notes/system. This makes the history timeline scannable at a glance without reading every label.

S2 — B1 — Cancellation sends NO automatic email (guardrail)

Who: Réka  ·  When: Réka cancels via email link; verify the no-email guardrail explicitly.

#You doYou should see Element that changes
copy · look · where
What changes underneathMust NOT happen 🕓 Touchpoint history💡 UX critique + suggestion
1 Réka clicks the cancel link Cancellation completes (column move + red label + history) with NO outbound email queued or sent Copy: no new outbox/email item for this deal · Look: outbox shows nothing newly armed · Where: absence is the assertion No email send triggered by the cancel path; AUTOSEND not flipped NO automatic email sent to lead; AUTOSEND never flipped; safe_click stays armed No additional touchpoint beyond the Lemondás entry from S1. The Outbox tab should show zero newly armed items for this deal. Confirm by checking the Outbox/send queue in the Control Panel. Critique: There is no visible confirmation to the operator (Mátyás) that no email was sent — the absence of an email is invisible and could be confused with a system failure.
Suggestion: Add a system note touchpoint: "🔇 Auto-email suppressed on cancellation" in the history — low-key, grey styling, so the operator knows the no-send was intentional and not a bug.

S3 — B2 — Lead-side reschedule: error today (failure / current bug)

Who: Gergő  ·  When: Gergő uses the reschedule link from the lead side — today this throws an error.

#You doYou should see Element that changes
copy · look · where
What changes underneathMust NOT happen 🕓 Touchpoint history💡 UX critique + suggestion
1 Gergő opens the lead-side reschedule link and picks a new slot, then confirms TODAY (bug): UI throws "időpont közben elkelt" / slot taken — nothing reschedules Copy: error message "időpont közben elkelt" · Look: red error state · Where: lead-side booking/reschedule page No appointment time change persisted After the fix this error must NOT appear — see S4 No touchpoint is created (the action failed). History is unchanged. Critique: "időpont közben elkelt" is technically accurate but confusing for a lead rescheduling their OWN appointment — they hold the existing slot, so "it was taken" makes no sense from their perspective.
Suggestion: Once fixed (S4), change the error copy to something actionable if it ever fires: "Valami nem sikerült — próbáld újra, vagy hívj minket: +36 70 327 8543". Never expose "slot taken" to a rescheduling lead.

S4 — B2 — Lead-side reschedule succeeds (happy path)

Who: Gergő  ·  When: Gergő reschedules to a new valid time from the lead side.

#You doYou should see Element that changes
copy · look · where
What changes underneathMust NOT happen 🕓 Touchpoint history💡 UX critique + suggestion
1 Gergő picks a new valid slot and confirms Success confirmation: appointment moved to the NEW time, no error Copy: "Időpont átütemezve {new time}" · Look: plain success, no error · Where: lead-side reschedule page Deal's appointment datetime updated; old slot cleared No error shown; no duplicate booking; old slot not still blocked No touchpoint yet — the reschedule touchpoint is created after the CRM update (step 3). At this point the history is unchanged from the operator side. Critique: The success page only confirms the new time — but does not show the cancellation of the old appointment, leaving uncertainty about whether the original slot was properly released.
Suggestion: Show both: "✓ Régi időpont törölve: júl. 3. 10:00 → Új időpont: júl. 5. 14:00". Two lines, old crossed out, new highlighted. Removes all ambiguity.
2 Card STAYS in the Booked column, showing the NEW date ZZ card still under 'Booked / Sales Call Prep', updated date Copy: new appointment date/time on the card · Look: normal Booked-column card, updated · Where: 'Booked / Sales Call Prep' column stage_key stays at booked-mapped value; card does NOT leave Booked Card must NOT move to another column; date must NOT be the old one No new touchpoint for the column-stay (no column change). History is unchanged at this step. Critique: The operator (Mátyás) sees the new date on the card but has no immediate indication that this was a lead-initiated reschedule vs. an operator-initiated one.
Suggestion: Add a small "🔄 Átütemezve" badge to the card (similar to the Lemondás red badge) that persists until the appointment date, so the operator can spot reschedules at a glance on the board.
3 History gets a RESCHEDULE entry New history entry recording the reschedule (old → new time) Copy: "Átütemezés" · Look: reschedule-type pill · Where: card's history/touchpoints list Reschedule touchpoint appended History must NOT drop the reschedule; must not mislabel it as a fresh booking NEW ENTRY
Type: Átütemezés · icon: 🔄 · label: "Átütemezés" · description: "Régi: {old datetime} → Új: {new datetime}" · timestamp: now · initiated_by: lead
Critique: The reschedule touchpoint currently shows only a single time (the new time). The old time is lost from the history view, making it hard to audit the sequence of changes.
Suggestion: Always store and display both old AND new datetime in the reschedule touchpoint: "Régi: júl. 3. 10:00 → júl. 5. 14:00". This is one DB field addition and makes the audit trail complete.
4 Updated calendar invite sent to lead + rep Updated Google Calendar invite for the NEW time reaches the lead's email and the rep Copy: calendar invite with new time · Look: Google Calendar invite email · Where: lead inbox + rep inbox create_meet_event runs with attendees + sendUpdates='all' for new time; old event deleted Do NOT fake-green if invite blocked by Workspace / DWD config — FLAG as owner action NEW ENTRY
Type: Meghívó elküldve · icon: 📅 · label: "Naptár meghívó · Átütemezés" · description: "Naptármeghívó kiküldve az új időpontra: {new datetime}" · timestamp: now
Critique: If the Google Calendar invite fails silently (DWD config issue), neither the operator nor the lead know — the card looks fine but no invite was sent.
Suggestion: Add a system touchpoint "⚠️ Naptár meghívó sikertelen — manuális küldés szükséges" if the invite throws an exception. This turns a silent failure into an actionable item.

S5 — B3 — Google Calendar invite reliably reaches the lead (happy path)

Who: Gergő  ·  When: A booking is created for a ZZ lead.

#You doYou should see Element that changes
copy · look · where
What changes underneathMust NOT happen 🕓 Touchpoint history💡 UX critique + suggestion
1 On booking, a Google Calendar invite is created with lead + rep as attendees Invite reaches the lead's inbox AND the rep (matyas@clientsflow.hu) Copy: Google Calendar invite email · Look: standard GCal invite · Where: lead inbox + rep inbox create_meet_event called with lead in attendees[] and sendUpdates='all' Booking must NOT silently succeed with no invite; invite must NOT go only to the rep NEW ENTRY
Type: Foglalás · icon: 📅 · label: "Foglalás · {datetime}" · description: "Időpont foglalva, naptármeghívó kiküldve" · timestamp: now
Critique: After booking, the lead has no visible "what happens next" context — they land on a generic confirmation page with no instructions about the Google Meet link or how to prepare.
Suggestion: Booking confirmation page: add a "Következő lépések" block: (1) nézd meg a naptárba érkező meghívót, (2) a meetinglinkre kattintva csatlakozhatsz, (3) ha kérdésed van: {phone}. Convert a flat confirmation into a short onboarding moment.

S6 — B3 — Invite blocked by Workspace/DWD config (failure — flag, don't fake-pass)

Who: Gergő  ·  When: create_meet_event raises an HttpError or GCAL_DRIVEN_CONFIRMATION is off.

#You doYou should see Element that changes
copy · look · where
What changes underneathMust NOT happen 🕓 Touchpoint history💡 UX critique + suggestion
1 Invite delivery fails at the Google Workspace / DWD / config layer QA run does NOT mark B3 green; raises config blocker as a RESIDUAL HUMAN action Copy: residual_human TODO: "enable DWD calendar scope / GCAL_DRIVEN_CONFIRMATION" · Look: flagged owner-action row in QA report · Where: QA report residual-human section Blocker recorded as owner action, not silently passed NEVER fake-green a blocked invite; never claim delivery succeeded when Workspace/DWD blocks it SYSTEM ENTRY
Type: Rendszerfigyelmeztetés · icon: ⚠️ · label: "Naptár meghívó sikertelen" · description: "A Google Calendar meghívó nem lett elküldve (Workspace konfig szükséges). Manuális link küldés ajánlott." · timestamp: now
Critique: When the operator opens a card after a failed invite, nothing on the board surface signals a problem — the card looks identical to a successful booking.
Suggestion: Surface the failed invite as an orange warning badge on the board card face: "⚠️ Meghívó nem ment ki". This is high-priority because the lead thinks they have a Meet link but doesn't — it directly causes no-shows.

S7 — B4 — Negative Replies column = New Leads button parity (happy path)

Who: Mátyás  ·  When: Mátyás views the board; a ZZ lead sits in the Negative Replies column.

#You doYou should see Element that changes
copy · look · where
What changes underneathMust NOT happen 🕓 Touchpoint history💡 UX critique + suggestion
1 Mátyás looks at a card in the 'Negative Replies' column The card shows the SAME 'Send Emails' button as a New Leads card; NO stray booking button / datetime-local field Copy: 'Send Emails' button present; no 'Book + send invite' / datetime-local · Look: button row matches New Leads card · Where: action-button row on the Negative Replies card Per-stage button logic renders same functions for negative as new_lead No stray booking button / date-picker on Negative Replies; 'Send Emails' must NOT be missing No touchpoint created by viewing the card. If the operator clicks 'Send Emails', a new touchpoint is created: Type: Email sorozat indítva · icon: ✉️ · label: "Sorozat újraindítva" — identical to the New Lead path. Critique: A Negative Replies card looks almost identical to a New Leads card after this fix — an operator who wasn't there for the negative reply has no context for why the lead is negative.
Suggestion: Add a collapsible "Előzmény" banner at the top of the card (one line, collapsed by default): "💬 Negatív válasz érkezett: {date} · {truncated reply text}". This gives the operator instant context before sending any follow-up.
2 Mátyás compares New Leads vs Negative Replies button sets The function/button SET on Negative Replies is identical to New Leads (same Send Emails, same Log Call; no extra booking control) Copy: identical button labels across both columns · Look: matching action rows · Where: both card types Button-set parity holds for both columns Two columns must NOT expose different booking controls No touchpoint for visual comparison. Any subsequent action (Send Emails, Log Call) creates its own touchpoint as normal. Critique: Button parity is correct from a technical standpoint, but from a UX standpoint it may be too identical — there's value in slightly differentiating the negative flow to prompt the operator to consider a different message tone.
Suggestion: Keep the same buttons, but change the 'Send Emails' button tooltip/hover text for Negative leads to: "Negatív válasz után – győződj meg róla, hogy a szöveg megfelelő" (a soft reminder, not a blocker).

S8 — B5 — Move-to dropdown persists across reload (happy path)

Who: Mátyás  ·  When: Mátyás moves a ZZ card from New Leads to Negative Replies using the 'Move to' dropdown.

#You doYou should see Element that changes
copy · look · where
What changes underneathMust NOT happen 🕓 Touchpoint history💡 UX critique + suggestion
1 Mátyás opens the 'Move to' dropdown and picks '🚫 Negative Replies' Card visually moves into the Negative Replies column immediately (optimistic UI) Copy: card now under '🚫 Negative Replies' · Look: card in Negative Replies · Where: pipeline board api_move runs; neg_reply=true AND stage_key set (the bug: only stage_key was set before) Move must NOT only set stage_key; must also set neg_reply so deal_col derives negative NEW ENTRY
Type: Státusz változás · icon: 🔀 · label: "Áthelyezve: New Lead → Negative Replies" · description: "Manuális áthelyezés" · timestamp: now · operator: Mátyás
Critique: The optimistic UI moves the card instantly, but if the Notion write fails (network issue), the card silently snaps back. The operator has no idea why the card "teleported" back.
Suggestion: Show a brief "Mentve ✓" confirmation on the card for 2 seconds after a successful move, and a "⚠️ Nem sikerült menteni — próbáld újra" toast on failure. The success case is invisible today; make it explicit.
2 Mátyás reloads the board AFTER RELOAD the card is STILL in Negative Replies — does NOT jump back to New Leads Copy: card stays under '🚫 Negative Replies' after reload · Look: card persists · Where: pipeline board deal_col(deal)==negative survives reload because neg_reply is persisted Card must NOT jump back to New Leads after reload No new touchpoint on reload. The Státusz változás entry from step 1 remains as the record. Critique: The reload-persistence fix is correct, but the operator has no way to distinguish "this is a genuine Negative Reply" (from an incoming email) vs "I manually moved this here" from the card face.
Suggestion: Add a tiny operator-initials badge to manual moves: "🔀 M" (Mátyás) next to the column label on the card. Distinguishes operator-moved from system-routed in the board view.
3 Mátyás moves the card BACK to New Leads via Move-to, then reloads After reload, card is back under 'New Lead' — not in Negative Replies Copy: card under 'New lead' after reload · Look: card in New Leads · Where: pipeline board deal_col(deal)==new_lead after reload (neg_reply cleared) Reverse move must NOT silently leave it in Negative Replies; both directions persist NEW ENTRY
Type: Státusz változás · icon: 🔀 · label: "Áthelyezve: Negative Replies → New Lead" · description: "Manuális visszahelyezés" · timestamp: now · operator: Mátyás
Critique: Moving a lead BACK from Negative to New Lead is a significant operator decision (essentially "re-activating" a negative lead) but there's no confirmation or reason-capture.
Suggestion: On back-move to New Lead, show a quick 1-field prompt: "Miért?" with preset options (Tévedés / Kedvező visszajelzés / Egyéb). This adds a 3-second audit step and builds a reason corpus for future analysis.

S9 — B6 — Log Call panel: two buttons, then Book appointment (happy path)

Who: Mátyás  ·  When: Mátyás clicks Log Call on a ZZ card and chooses to book an appointment.

#You doYou should see Element that changes
copy · look · where
What changes underneathMust NOT happen 🕓 Touchpoint history💡 UX critique + suggestion
1 Mátyás clicks the 'Log Call' button on a card Log Call panel shows EXACTLY TWO buttons: 'Book appointment' and 'Set FUP date' — nothing else Copy: 'Book appointment' and 'Set FUP date' — only these two · Look: two buttons, no other fields by default (old redundant datetime-local GONE) · Where: inline Log Call panel under the card Panel default state renders only the two entry buttons; standalone cs-{id} datetime-local picker is removed Panel must NOT show the redundant standalone 'Book appointment (optional)' date-picker; no other fields shown until a button is clicked NEW ENTRY (at click, before choosing an action)
Type: Hívás rögzítve · icon: 📞 · label: "Hívás" · description: "Hívás rögzítve" · timestamp: now. Updated/replaced if the operator then books or sets FUP.
Critique: "Log Call" as a button name is ambiguous — is it logging a past call or initiating a new one? Operators have reported confusion about whether clicking it sends anything.
Suggestion: Rename to "📞 Hívás rögzítése" (slightly more explicit). Add a one-line panel header: "Mit szeretnél rögzíteni?" above the two buttons to frame the choice. Cost: 2 string changes.
2 Mátyás clicks 'Book appointment' Optional CRM note field AND prefilled, fully-EDITABLE Meet event fields: title, description, outgoing email(s), body text, meeting time, duration Copy: Title prefilled "ClientsFlow konzultáció — {name}"; body text prefilled; all editable; + note + time + duration · Look: form with prefilled but editable inputs · Where: expanded under 'Book appointment' button in Log Call panel Event fields prefilled from the deal and remain user-editable before send Fields must NOT be read-only; editing must NOT be blocked No additional touchpoint yet — the touchpoint is created only when the operator clicks the blue 'Book appointment' send button (step 3). Critique: The expanded form has 6 editable fields which can feel overwhelming after a sales call when the operator just wants to quickly log and move on.
Suggestion: Collapse the low-frequency fields (description, duration) by default under a "Részletek ▾" toggle. Show only: time, recipient email, and note. Most calls just need those three. Advanced fields expand on demand.
3 Mátyás edits a field and clicks the single blue 'Book appointment' send button The edited invite is sent only after the explicit blue 'Book appointment' click (human approve gate); booking created with edited values Copy: blue 'Book appointment' button is the single send entry · Look: one blue button · Where: Book-appointment sub-form in Log Call panel schedule_meeting runs with operator-edited event_name/email_body/time/duration; gcal invite sent NOTHING sent without explicit click; AUTOSEND never flipped; ZZ fixtures only UPDATED ENTRY
Type: Foglalás (manuális) · icon: 📅 · label: "Foglalás · {datetime}" · description: "Időpont rögzítve, meghívó elküldve (manuális). Megjegyzés: {note}" · timestamp: now · initiated_by: operator
Critique: After clicking the blue button there's no visible success state inside the panel — the panel just closes, leaving the operator wondering "did it go?"
Suggestion: After successful send, show a 2-second in-panel confirmation: "✓ Meghívó elküldve · {datetime}" before the panel collapses. Also update the card's date badge optimistically before the backend confirms.
4 On a separate card, Mátyás clicks 'Log Call' then 'Set FUP date' 'Set FUP date' reveals a NOTE field + a follow-up DATE picker only — nothing about booking an invite Copy: note textarea + follow-up date input · Look: small form with note field + date picker · Where: expanded under 'Set FUP date' in Log Call panel Follow-up date + note captured (logCall path with FUP date) FUP path must NOT show Meet-event/booking fields; setting FUP must NOT send any invite or email NEW ENTRY
Type: Hívás + Visszahívás · icon: 📞 · label: "Hívás · FUP: {fup_date}" · description: "Visszahívás ütemezve: {fup_date}. Megjegyzés: {note}" · timestamp: now
Critique: The FUP date picker has no context for what "follow-up" means in practice — is it a reminder for the operator, or does it trigger an automated sequence?
Suggestion: Below the date picker, add a one-line grey note: "Emlékeztető neked: {fup_date}-án jelenik meg a Feladatok táblában. Automatikus email NEM megy ki." This sets clear expectations and prevents the operator from thinking they've set up an automated sequence.
🕓 Touchpoint history — design principles extracted from this EBO:
Every user-initiated action that changes deal state should produce exactly one touchpoint entry: (1) a human-readable type label (never a raw backend string), (2) an icon that maps to the type, (3) a description line with the key delta (old→new, or the note), (4) a timestamp in Budapest local time, and (5) an initiated_by field (lead / operator / system). System-only events (cron, sweep, failed tries) should render in a muted grey style to not compete with human-initiated events.
💡 UX themes across all scenarios:
(1) Silent success is invisible — every action needs a 2-second positive confirmation (in-panel or toast). (2) Silent failure is a crisis — every failed backend action needs a visible warning on the card face, not just in logs. (3) Context on the card face — the operator should never have to open the history panel to understand the lead's current state; badges, banners, and colour coding on the card itself should tell the story. (4) Irreversible/significant moves need a 1-step reason capture — back-moves from Negative to New Lead, cancellations — cheap audit trail, high future value.