ClientsFlow · Email-System Overhaul · Workstream W1

EBO — W1 · Send Integrity (FINAL)

FINAL · 2026-07-12 — owner comment round folded in (comments override round-3 wherever they conflict). The target behavior after W1 ships — every send made provably match the locked auto-send whitelist, every send-click idempotent, every send outcome honestly recorded (pending → accepted → failed) so "the screen said sent" can never again mean "nothing left the building." Walked click-by-click on the staging dashboard. Awaiting owner signature.

The whitelist (as ruled through the 2026-07-12 comment round)
Five things may leave WITHOUT a same-session owner click: booking confirmations · pre-call reminders · the post-signing signed-contract email (already exists) · owner-pre-edited scheduled sequence emails · the post-signing daily payment-reminder chase. Sequence continuations ride the arm the owner already clicked. There is no click-gated payment button — the payment chase is automatic (one reminder per day, capped, stopped by payment or a logged phone call).
The honesty layer
Every send is written to a ledger first (pending), then flipped to accepted (relay took it, with a message id) or failed (with a reason). Nothing is ever marked success before the network round-trip returns. Failures surface as a red dashed ring on the card + a board banner — nothing more, nothing less. The ledger never says delivered (there is no bounce webhook to prove it).
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
Owner rulings folded in — 2026-07-12 comment round (these OVERRIDE round-3 and the W1 handoff wherever they conflict)S1 the current Send-Emails modal UI stays exactly as it is ("don't fix what works"); on hitting Send the modal closes immediately so the owner can move on — the guarantee moves to the backend: sends must reliably fire in the background and be provable afterwards. The old "success toast only after 2xx" step is deleted entirely. · S3 (overrides Q11=C) a failed send surfaces as a red dashed ring + a banner notification — and that's it: no popup-on-open, no Failures list. · S7 the signed-contract email already exists — the owner signs the proposal BEFORE sending it; when the client signs, an automatic email goes out (pre-existing system copy: confirmation with the signed contract, contract download link, payment reminder with the Stripe link + bank-transfer details). W1 verifies this flow, it does not redesign it. · S8 (overrides "payment chase is click-only") there is NO "send payment link" button: if the client hasn't paid 1 day after signing, an automatic daily payment-reminder fires — one per day for 3–4 days — until payment arrives or the owner logs a phone call (which stops the chase). · S9 booking confirmation + pre-call reminder: the current setup is perfect — verified unchanged, no redesign.
Round-3 rulings that still standFreeze model: clicking "Send Emails" generates the copy for ALL emails of the sequence in the modal; Send fires the first and schedules the rest frozen exactly as previewed. · Q2=B if lead data drifts before send-day the frozen email HOLDS — the row turns amber until re-approve. · Q7=A sequences fire until reply/stop. · Q8=A a hostile reply lands as a silent card in Negative Replies, no auto-reply. · Q9=A editing an already-sent email is an internal record fix + an "edited" note, never a re-send.
Invariants that hold everywhere — the ZZ-sentinel hard guard blocks any send to a test lead, independent of every flag; N clicks on any send-triggering button = exactly 1 send (client-side single-flight + a server-side idempotency key); the ledger's success state is accepted (relay took it), never delivered; pre-call reminders and booking confirmations were never wired to the global gate and must stay that way; W1 owns the send path only — W2 owns how the ledger + frozen rows are rendered in touchpoint history; W0 owns the alarm/banner plumbing the failure surfaces ride on. Operator UI strings are English (Hungarian appears only inside client-facing email copy).

S1 — "Send Emails" → modal previews everything, Send closes instantly, delivery proven in the background (happy path)

Who: Mátyás  ·  When: A fresh lead is on the board and Mátyás decides to start its outreach sequence. He wants to see everything that will go out, approve it once, and immediately move on to other work — while the system reliably fires the first email in the background and queues the rest exactly as he saw them, with real proof each one actually left.

#You doYou should see Element that changes
copy · look · where
What changes underneathMust NOT happen 🕓 Touchpoint history
1 Mátyás clicks "Send Emails" on a lead card The CURRENT modal — unchanged (owner: the current UI is nice, keep it exactly as-is) — opens showing the copy for EVERY email in the sequence, most fixed templates, a few AI-filled, each with its scheduled send-day. NOTHING has been sent yet Copy: full body of each step incl. resolved greeting + canonical sign-off (client-facing copy stays Hungarian: "Üdv,
Sarudi Mátyás…") · Look: the existing stacked preview cards, one per step, Send button at the bottom — no redesign · Where: modal over the card
Copy generated for all steps and held in the modal; no ledger row yet; no network send Must NOT send anything on modal-open; must NOT show raw {{greeting}}/{{token}} placeholders; must NOT show only the first email; must NOT restyle or restructure the existing modal No touchpoint yet — previewing is not a send. History rows appear only when the background send actually completes (step 3).
2 Mátyás clicks Send The modal closes immediately — he is free to do other work at once. No spinner-modal, no waiting screen. The board is fully usable Copy: — · Look: modal gone on click; board interactive · Where: board The send batch is handed to a reliable background worker BEFORE the modal closes: a pending ledger row for step 0 + all remaining steps stored frozen (subject + body + send-day exactly as previewed); client single-flight absorbs any second click Must NOT block the owner in a spinner modal; must NOT lose the batch if he navigates away or closes the tab right after; must NOT re-generate/alter the copy at send time — what he saw is what freezes Still no committed "sent" row — the pending ledger entry is internal until the background send resolves. No claim before proof.
3 (Automatic, seconds later) the background worker completes the first send Without any owner action, the card gains a 📤 sent row in its history and the clockwise-circling dashed border (active sequence). No toast is required — the history row + border ARE the proof Copy: history row "First sequence email sent" + subject · Look: dashed circling border on the card · Where: board card + its history panel Step 0 ledger row flips pending → accepted with the relay's message id; steps 1–3 sit as frozen scheduled rows; the arm is idempotent — re-arming is a logged no-op Must NOT mark accepted without a real 2xx + message id from the relay; must NOT double-send step 0; the ledger must NOT record delivered; the email must actually arrive in the test inbox NEW ENTRY
Type: Email sent · icon: 📤 · label: "First sequence email sent" · desc: subject line + "3 more scheduled" · timestamp: send-completion time · by: operator. The 3 frozen future rows render as scheduled (W2 owns the styling).

S2 — Mash the Send button / re-submit → exactly ONE send (guardrail / idempotency)

Who: Mátyás  ·  When: On a slow staging connection Mátyás double-clicks Send, or later re-opens the modal on an already-armed deal and tries to send again. Historically this is exactly where "clicked 6×, nothing delivered / or delivered twice" came from — it must now be provably one send.

#You doYou should see Element that changes
copy · look · where
What changes underneathMust NOT happen 🕓 Touchpoint history
1 Mátyás clicks Send twice in quick succession The modal closes on the first click; the second click lands on nothing. Exactly one email goes out in the background Copy: — · Look: one modal-close, no double anything · Where: board Client single-flight swallows click 2; even if a second request reaches the server, the deterministic idempotency key (deal · category · step) makes it a no-op via put-if-absent — one ledger row, one send Must NOT fire two sends; must NOT create two ledger rows for the same step; must NOT deliver two copies to the test inbox Exactly ONE 📤 "Email sent" entry — never a duplicate pair. A duplicated history row is itself the visible symptom of a double-send bug.
2 Mátyás re-opens "Send Emails" on the already-armed deal and tries to send again No second batch goes out — the modal shows the sequence is already running and Send is disabled Copy: notice "This sequence is already running" · Look: already-armed state, Send disabled · Where: re-opened modal Idempotency key already present for step 0; arm() sees an armed deal → logged no-op; frozen future rows untouched Must NOT re-arm or re-schedule; must NOT reset the frozen copy or send-days; must NOT double-fire step 0 No new entry — the re-submit produces nothing. The original 📤 entry stands alone.

S3 — A send FAILS → red dashed ring + banner, and that's it (failure path · owner override of Q11=C)

Who: Mátyás + System  ·  When: Mátyás sends a sequence and moves on (the modal already closed); in the background the mail relay rejects the first email (bad address, relay 5xx, timeout). The single worst legacy behavior was a green "sent" over a send that never happened — the ledger exists for this. Owner ruling: the failure surfaces as a red dashed ring on the card + a banner notification — nothing else (no popup-on-open, no Failures list).

#You doYou should see Element that changes
copy · look · where
What changes underneathMust NOT happen 🕓 Touchpoint history
1 (Automatic) the background send is rejected by the relay No fake success anywhere — the card's history gains a ⛔ failed row with a plain-language reason; the board reflects the failure without the owner having done anything Copy: history row "Email send failed" + reason in plain words (e.g. "recipient address invalid — check the lead's details"), raw relay code kept in a foldout · Look: ⛔ failed row · Where: card history Step 0 ledger row goes pending → failed with the captured reason; the arm is NOT left half-done (either fully armed with step 0 marked failed, or not armed — never "board says armed, nothing scheduled") Must NOT record accepted; must NOT show any "sent" claim in history; must NOT swallow the failure with no record; must NOT leave a half-armed sequence FAILURE ENTRY
Type: Send failed · icon: ⛔ · label: "Email send failed" · desc: subject + plain-language reason · timestamp: failure time · by: system
2 Mátyás glances at the board The affected card wears a red dashed ring and a board banner reads "1 failed send — review". That is the WHOLE failure surface: no popup when he opens the card, no separate Failures list Copy: banner "⛔ 1 failed send — review" · Look: red dashed ring around the card + banner notification · Where: board card + board header The failed ledger row keeps the ring + banner up until the failure is handled; banner count = open failed rows; a retry (from the ⛔ history row) re-enters the send path with the SAME idempotency key + frozen copy — success flips failed → accepted and clears ring + banner Must NOT pop a dialog when the card is opened; must NOT render a Failures list/panel anywhere; must NOT let the failure fade silently (ring stays until handled); a retry must NOT double-send if the first actually went The ⛔ failure entry persists on the timeline — it is the durable record behind the ring. ON RETRY-SUCCESS it gains a follow-up 📤 "Resend succeeded" entry (the failure stays in history — resolved, not erased).

S4 — Mail relay is DOWN mid-batch → honest ledger, scheduled steps don't vanish (failure path)

Who: Mátyás + System  ·  When: Missive / the mail relay is having an outage. Mátyás arms a sequence during it, and separately a previously-scheduled frozen step comes due while the relay is still down. Neither must be quietly lost.

#You doYou should see Element that changes
copy · look · where
What changes underneathMust NOT happen 🕓 Touchpoint history
1 Mátyás arms a sequence while the relay is down (modal closes instantly as always) The first send fails honestly in the background and surfaces exactly like S3 (red dashed ring + banner); the remaining steps stay frozen + scheduled, not thrown away Copy: banner reason "mail service unavailable" · Look: red dashed ring + banner · Where: board card + board header Step 0 ledger row = failed (reason: relay unreachable); frozen steps 1–N remain as pending scheduled rows, untouched Must NOT report the batch as sent; must NOT discard the scheduled steps because step 0 failed; must NOT infinite-retry-hammer the dead relay FAILURE ENTRY
⛔ "Email send failed — mail service unavailable". The scheduled future steps still render as pending (W2 styling).
2 (Automatic) a previously-scheduled frozen step comes due while the relay is still down That step attempts its send, fails honestly, and surfaces as its own failed row — it does not silently skip its slot Copy: banner count increments · Look: red dashed ring on the affected card · Where: board The due frozen step writes a pending → failed ledger row (never a phantom "accepted"); it stays reviewable/retryable from its ⛔ history row Must NOT mark a step "accepted" because its scheduled time passed; must NOT drop the step off the schedule with no record FAILURE ENTRY
⛔ per due step that couldn't send — each keeps its own reason + timestamp so the timeline shows exactly which scheduled email didn't make it.

S5 — Sequence continues automatically until reply/stop (Q7=A), auto-sends visibly tagged (happy path / whitelist)

Who: System + the lead  ·  When: A frozen scheduled step reaches its send-day and the lead hasn't replied — the continuation rides the arm Mátyás already clicked, so it may auto-send (whitelisted). Then the lead replies and the sequence must stop itself. Owner-approved feature: an auto-continuation must be distinguishable in history from an owner-sent email.

#You doYou should see Element that changes
copy · look · where
What changes underneathMust NOT happen 🕓 Touchpoint history
1 (Automatic) a frozen step's send-day arrives, still no reply The email auto-sends exactly as it was frozen; the card's history gains a 📤 sent row that byte-matches the preview AND carries a small "⚙️ auto" marker so an autopilot send never masquerades as a hand-sent one Copy: the frozen subject + body, unchanged since arm; history row tagged "⚙️ auto" · Look: scheduled row flips to sent, muted-grey system styling + the auto marker · Where: touchpoint history (W2 rendering) Continuation is a whitelisted category (rides the existing arm — no fresh click); ledger row pending → accepted; the sent copy = the frozen copy (100% parity, Q7) Must NOT re-generate or re-resolve the copy at send time; must NOT require a new owner click for a continuation; must NOT render the auto-send identically to an operator send (the ⚙️ marker is required) NEW ENTRY
Type: Email sent · icon: 📤 + ⚙️ auto marker · label: "Sequence step {n} sent (auto)" · desc: subject · timestamp: send time · by: system (muted grey — auto, but whitelisted)
2 The lead replies before the next scheduled step The sequence stops itself; remaining frozen steps are cancelled and the circling dashed border clears Copy: history "Sequence stopped — reply received" · Look: dashed border removed; scheduled rows shown cancelled · Where: card + history Reply detected → sequence teardown; pending scheduled ledger rows move to a cancelled/superseded terminal state (not left as ghost pending) Must NOT keep sending after a reply; must NOT leave orphan scheduled rows firing; must NOT mark cancelled rows as failures NEW ENTRY
Type: Sequence stopped · icon: 🛑 · label: "Sequence stopped (reply)" · desc: "The lead replied — remaining emails cancelled" · timestamp: reply time · by: system

S6 — Lead data drifts before send-day → frozen row HOLDS amber, re-approve to release (Q2=B) (guardrail)

Who: Mátyás + System  ·  When: A frozen email is scheduled for next week, but in the meantime the lead's underlying data changes (name/company/greeting corrected on the Details panel). The frozen copy might now be wrong, so it must not fire blind. (W2 owns the amber row UI; W1 owns the gate that refuses to auto-send an un-re-approved drifted step.)

#You doYou should see Element that changes
copy · look · where
What changes underneathMust NOT happen 🕓 Touchpoint history
1 The lead's data changes after arming but before a frozen step's send-day That scheduled row turns amber and is put on HOLD — it will NOT auto-send on its day until re-approved Copy: row badge "⏸ Review needed — lead data changed" · Look: amber scheduled row · Where: touchpoint history / scheduled rows (W2 rendering) Drift detected between frozen snapshot and current lead data → step marked needs-reconfirm; the auto-send gate refuses this step while held (its approved flag is not set for the new data) Must NOT auto-send the stale frozen copy; must NOT silently re-resolve to the new data and send without the owner seeing it; must NOT drop the step HOLD ENTRY
Type: Scheduled email held · icon: ⏸ · label: "Scheduled email held (data changed)" · desc: "Lead data changed — re-approval needed" · timestamp: drift-detect time · by: system
2 Mátyás opens the held row and clicks re-approve (accepting or editing the refreshed copy) The row leaves amber and returns to a normal scheduled state; it will fire on its day with the approved copy Copy: row back to normal scheduled styling · Look: amber cleared · Where: scheduled rows Re-approval writes the fresh frozen copy + sets the approved/owner_edited flag → the auto-send gate now permits this step (owner-edited scheduled email = whitelisted) Must NOT require re-arming the whole sequence; must NOT lose the step's original send-day; must NOT send before the owner's explicit re-approve NEW ENTRY
Type: Scheduled email approved · icon: ✅ · label: "Held email re-approved" · desc: "Updated copy approved — schedule resumes" · timestamp: approval time · by: operator

S7 — Client signs → the EXISTING automatic signed-contract email fires (verify, don't redesign) (happy path · existing flow)

Who: System  ·  When: The real flow, as the owner runs it: Mátyás signs the proposal BEFORE sending it to the client. When the client signs, the system's already-existing automatic email goes out. W1's job is to verify this existing flow rides the ledger + whitelist correctly — not to redesign it.

#You doYou should see Element that changes
copy · look · where
What changes underneathMust NOT happen 🕓 Touchpoint history
1 (Automatic) DocuSeal reports the client has signed the owner-pre-signed contract The existing automatic email goes out to the client immediately — no owner click — containing all four pieces: ① the pre-existing copy already set up in the system, ② confirmation with the signed contract, ③ the contract download link, ④ the payment reminder with the Stripe link + bank-transfer details Copy: the system's existing signed-contract email (client-facing, Hungarian) with signed-PDF + download link + Stripe link + transfer details · Look: 📤 sent row in history · Where: touchpoint history + the client's inbox Send passes the whitelist gate as the signed-contract category; ledger pending → accepted with message id; per-deal idempotency flag prevents a double-send on a repeated webhook Must NOT wait for an owner click; must NOT double-send on a duplicate webhook; must NOT go out with any of the four pieces missing (no contract attachment/link, no payment details); must NOT be blocked by any global flag — the whitelist permits it by category NEW ENTRY
Type: Email sent · icon: 📤 · label: "Signed-contract email sent" · desc: "Client signed — confirmation + signed contract + download link + payment details sent automatically" · timestamp: signing time · by: system

S8 — Unpaid after signing → automatic daily payment-reminder chase, stopped by payment or a logged call (owner override — no payment button, no click)

Who: System + Mátyás  ·  When: After S7 the client has the payment details. Owner ruling (replaces the old "click-only payment chase"): there is no "send payment link" button. If the client hasn't paid 1 day after signing, an automatic payment-reminder email fires — one per day, for 3–4 days in a row — until the payment arrives or Mátyás logs a phone call, either of which stops the chase.

#You doYou should see Element that changes
copy · look · where
What changes underneathMust NOT happen 🕓 Touchpoint history
1 (Automatic) 1 day after signing, no payment has arrived The first payment-reminder email auto-sends — no button, no owner click anywhere. The card's history shows it as an automatic send Copy: the payment-reminder email (client-facing, Hungarian — Stripe link + transfer details) · Look: ⏰ reminder row in history with the ⚙️ auto marker · Where: touchpoint history The payment-reminder chase arms itself off the signing event with a 1-day fuse; the reminder is a whitelisted auto-send category; ledger pending → accepted; payment state checked immediately before send Must NOT render any "Send payment link" button or owner task; must NOT send if payment already arrived within the first day; must NOT send more than one reminder in a day NEW ENTRY
Type: Payment reminder sent · icon: ⏰ + ⚙️ auto · label: "Payment reminder 1 sent" · desc: "No payment 1 day after signing — automatic reminder" · timestamp: send time · by: system
2 (Automatic) the client still doesn't pay on the following days One reminder per day fires — day 2, day 3, capped at 3–4 reminders total. The moment a payment arrives (Stripe or bank transfer), the chase ends silently — no further reminders Copy: daily reminder emails · Look: one ⏰ row per day in history; on payment the chase rows simply stop · Where: touchpoint history A daily cron checks payment state before each reminder; hard cap at the 3–4-reminder ceiling; payment-arrival flips the chase to a terminal completed state and cancels any remaining reminders Must NOT send a reminder after payment has arrived; must NOT exceed the reminder cap; must NOT send two reminders on one day; must NOT keep the chase alive forever past the cap One ⏰ "Payment reminder {n} sent" entry per day, each through the ledger. After payment: no further chase entries (the payment itself is logged by the money path, not by W1).
3 Mátyás logs a phone call on the deal mid-chase (he called the client about the payment) The chase stops immediately — the logged call is the stop signal; no more payment reminders go out Copy: the existing Log-Call panel, unchanged · Look: call touchpoint appears; scheduled reminders shown cancelled · Where: card history Logging a call on a deal with an active payment chase cancels the remaining reminders (terminal stopped_by_call state); the call touchpoint itself is logged as today Must NOT keep sending reminders after a logged call; must NOT require any extra "stop chase" action beyond logging the call; must NOT delete the already-sent reminder history NEW ENTRY
Type: Chase stopped · icon: 🛑 · label: "Payment chase stopped (call logged)" · desc: "Owner logged a phone call — remaining reminders cancelled" · timestamp: call-log time · by: operator

S9 — Booking confirmation + pre-call reminder — current setup is perfect, verify unchanged (happy path / whitelist · no redesign)

Who: System + the lead  ·  When: A lead self-books a call, and later the pre-call reminder comes due. Owner ruling: the current setup is perfect — don't try to fix what is working; test it however. Both auto-send on the whitelist, never wired to the global gate. This scenario verifies the existing behavior and guards against a well-meaning "add a gate to everything" regression. One owner-approved rendering improvement: reminder and confirmation rows must be distinguishable in history, not two identical generic rows.

#You doYou should see Element that changes
copy · look · where
What changes underneathMust NOT happen 🕓 Touchpoint history
1 A lead self-books a call slot The booking-confirmation email goes out instantly, automatically — exactly as it does today; no owner click, no gate, no behavior change Copy: the existing booking confirmation (time, Meet link — client-facing Hungarian, unchanged) · Look: 📅 confirmation row in history · Where: touchpoint history Send passes the whitelist as the booking-confirmation category; ledger pending → accepted; the rep is on the invite as an attendee (B16 re-verify — W2/owner) Must NOT be blocked by any global send flag; must NOT require a click; must NOT change the existing email's content or timing (verify-only, no redesign) NEW ENTRY
Type: Booking confirmation sent · icon: 📅 (booking-lifecycle family — distinct from sequence 📤 rows) · label: "Booking confirmation sent" · desc: "Slot confirmed: {time}" · timestamp: booking time · by: system
2 (Automatic, cron) the pre-call reminder becomes due The reminder auto-sends on schedule, exactly as today; in history it reads clearly as a reminder, visually distinct from the confirmation row (owner-approved distinction) Copy: the existing pre-call reminder body (unchanged) · Look: ⏰ reminder row, same 📅 booking-lifecycle family, distinct label/icon from the confirmation · Where: touchpoint history Send passes the whitelist as the reminder category; per-(deal,idx) claim + quiet-window suppression already in place; ledger pending → accepted Must NOT fire inside the quiet window; must NOT double-send the same reminder index; must NOT render confirmation and reminder as two indistinguishable generic rows NEW ENTRY
Type: Call reminder sent · icon: ⏰ (booking-lifecycle family) · label: "Pre-call reminder sent" · desc: "Reminder about the upcoming call" · timestamp: cron time · by: system — clearly distinct from the 📅 confirmation row above it

S10 — Hostile reply → silent card in Negative Replies, no auto-reply (Q8=A) (guardrail · resolves deferred A3)

Who: System + a hostile lead  ·  When: A lead sends an angry / hostile reply to an outreach email. The round-3 ruling (Q8=A) resolves the handoff's deferred angry-reply owner-decision: it MUST land as a card in Negative Replies, but silently — the system must not fire back an antagonizing auto-reply.

#You doYou should see Element that changes
copy · look · where
What changes underneathMust NOT happen 🕓 Touchpoint history
1 A lead sends an angry / hostile reply A card appears in the Negative Replies column, tagged as hostile — and NO automated reply is sent back Copy: card tag "😠 hostile" · Look: silent card in Negative Replies (no notification blast) · Where: Negative Replies column Inbound classified hostile → a Negative Replies card is minted with a hostile tag; the send/no-send decision is NO auto-reply; any armed sequence stops Must NOT auto-reply / antagonize; must NOT drop the reply with no card (round-3 requires the card); must NOT keep the outreach sequence running after a hostile reply NEW ENTRY
Type: Hostile reply · icon: 😠 · label: "Hostile reply — silent card" · desc: "The lead replied hostilely — card minted in Negative Replies, no automated response" · timestamp: reply time · by: system

S11 — Editing an already-sent email → internal record fix + "edited" note, never a re-send (Q9=A) (guardrail)

Who: Mátyás  ·  When: Mátyás notices a typo or wants to correct the stored text of an email that has ALREADY gone out. Editing it fixes the internal record only — the lead already has the original, so nothing is re-sent; the edit is marked so history stays honest.

#You doYou should see Element that changes
copy · look · where
What changes underneathMust NOT happen 🕓 Touchpoint history
1 Mátyás opens an already-SENT email in touchpoint history, edits the text, and saves The stored record updates and the row gains an "edited" note; NOTHING is re-sent to the lead Copy: row badge "✎ edited" + edit timestamp · Look: updated body, "edited" marker · Where: the sent row in touchpoint history The sent record's stored copy is updated for internal accuracy; an "edited" note (who/when) is attached; no send call is made — the ledger's original accepted send is untouched Must NOT re-send to the lead; must NOT overwrite the record with no "edited" note (that would break history honesty); must NOT alter the original send's ledger row NEW ENTRY
Type: Record edited · icon: ✎ · label: "Sent-email record edited" · desc: "Internal record corrected — NO email re-sent to the lead" · timestamp: edit time · by: operator
🕓 Touchpoint history — send-integrity principles:
Every send outcome produces exactly one honest history row, driven by the ledger, never by an optimistic UI: 📤 accepted (relay took it, with a message id) · ⛔ failed (with a plain-language reason, and it stays on the timeline even after a successful retry — resolved, not erased) · ⏸ held (frozen row waiting on drift re-approval) · 🛑 stopped (reply / logged call / manual teardown) · ⏰ reminder family (payment chase, pre-call). Auto-sends carry a ⚙️ auto marker and muted-grey system styling so autopilot never masquerades as the owner's hand; booking-lifecycle sends (📅 confirmation · ⏰ reminder) form their own visual family, distinct from outreach 📤 rows. The single hard invariant: a history row that says "sent" exists only because a real 2xx with a message id came back — the recurring "the screen said sent but nothing left" class is closed, now proven asynchronously (the modal closes instantly; the proof appears on the card seconds later). The ledger never records delivered (no bounce webhook can prove it). W2 owns the visual rendering of these rows; W1 owns their truth.

Work-item → scenario-step mapping

One row per W1 work item — which scenario steps prove it. The QA skill walks only the listed steps per item. Owner comment-round (2026-07-12) overrides noted where they reshape an item.

W1 work itemWhat it deliversProven by (scenario · step)Ruling note
W1-1 Verify the existing signed-contract auto-sendWhen the client signs the owner-pre-signed contract, the EXISTING automatic email (system copy + signed-contract confirmation + download link + Stripe/transfer payment details) fires through the ledger, idempotent on duplicate webhooksS7·1REWRITTEN by owner comments — the email already exists; W1 verifies it (all four content pieces present), it does not redesign or gate it.
W1-2 Automatic daily payment-reminder chaseNo payment 1 day after signing → one reminder per day (cap 3–4), stopped by payment arrival or a logged phone call; no button, no owner clickS8·1, S8·2, S8·3REVERSED by owner comments — the old click-only "Send payment link" button is deleted; the chase is fully automatic with two stop conditions.
W1-3 Per-category whitelist gate (replaces the global boolean)Only whitelisted categories auto-send; a flipped flag can't re-open a leakS5·1, S7·1, S8·1, S9·1, S9·2Whitelist = booking confirmations · pre-call reminders · signed-contract email · owner-edited scheduled sequence emails · post-signing payment reminders; sequence continuation rides the arm.
W1-4 Instant modal-close + reliable background sendSend closes the modal immediately; a background worker reliably fires the send and the proof (ledger + history row) appears on the card without owner action; every send button single-flightS1·1, S1·2, S1·3, S2·1, S3·1, S3·2REWRITTEN by owner comments — replaces the old "modal stays open until 2xx + success toast" design. Failure surface reduced to red dashed ring + banner only (no popup, no Failures list).
W1-5 Send ledger (pending → accepted → failed)Every send recorded honestly; failures never swallowed; never "delivered"S1·3, S3·1, S4·1, S4·2, S5·1, S9·1, S9·2Terminal states pending/accepted/failed; the board ring + banner read this ledger.
W1-6 Deterministic idempotency key per intended sendN clicks / duplicate webhooks = exactly 1 sendS2·1, S2·2, S7·1Unchanged.
A3 Angry-reply routing (was deferred owner-decision)Hostile reply → silent card in Negative Replies, no auto-replyS10·1RESOLVED by Q8=A — silent card; sequence also stops.
Q2=B gate Drift hold on frozen scheduled emailsDrifted frozen step holds amber; won't auto-send until re-approvedS6·1, S6·2W2 owns the amber-row UI; W1 owns the gate that refuses an un-re-approved drifted step (owner-edited flag).
Q7=A Sequences fire until reply/stopContinuations auto-send with frozen-copy parity + a visible ⚙️ auto marker; reply auto-stopsS5·1, S5·2100% parity between history copy and sent copy is the hard requirement; the auto marker is owner-approved.
Q9=A Edit a sent emailInternal record fix + "edited" note, never a re-sendS11·1Touches the send record but makes no send call; original ledger row untouched.
❓ Flagged for build-time confirmation (small residuals, not blockers): (a) S8 — the owner said "3 or 4 days in a row"; the exact reminder cap (3 vs 4) is confirmed at build time. (b) S9·1 — whether the rep is reliably an invite attendee (MASTER_FINDINGS B16) is left to W2/owner re-verification, not decided here. (c) History-row labels/icons above are illustrative — final operator-UI wording is English per the standing rule; client-facing email copy stays Hungarian and is W6's source-of-truth.
Sign-off — acceptance oracle. By signing, Sarudi Mátyás locks scenarios S1–S11, the whitelist, and the invariants above as the acceptance answer key for W1 — Send Integrity. Every "sent" claim the system makes after this is verified against these steps. No W1 code merges to main before this signature and a green staging QA pass captured in SHIP_LEDGER.md.
Sarudi Mátyás FINAL · 2026-07-12 · awaiting owner signature (EBO_W1_SEND_INTEGRITY.html.signed)