ClientsFlow · Email-System Overhaul · W3 · EBO

EBO — W3 Lead Details Panel

FINAL · owner comment round folded in · 2026-07-12. The target board-card Details drawer after W3 ships — every piece of saved lead data shown as an editable field, sorted by how likely Mátyás is to need it, the {{greeting}} the AI opens every automated email with finally visible, editable and previewable, a human verification gate on genuinely ambiguous AI legal data (in the post-call modal, not the drawer), and a way to re-run the legal lookup when a lead entered without a website. Scenarios S1 … S9. Acceptance oracle for /ebo-live-build.

What W3 owns
The Details drawer's field completeness, editability, and ordering: the field grid (Contact / Deal / Legal groups), the Greeting field + its rendered preview, the ambiguous-legal-data verification block inside the post-call modal, the "Re-run legal lookup" action, the auto re-enrichment after a late website URL, and the guard that keeps the drawer's field list in lockstep with what the backend actually saves.
What W3 only reflects (owned elsewhere)
W2 owns the touchpoint-history drawer, orange scheduled rows, and copy parity. W1 owns whether/when any email sends — W3 may add fields but must NOT change how or when anything sends. Enrichment machinery (website scrape + AI legal lookup) already exists — W3 adds a re-trigger, an ambiguity signal, and a visibility layer; it does not rebuild it.
You do → click/action/hover You should see → on-screen result Element changes → copy · look · where What changes underneath → data/state Must NOT happen → bug guard 🕓 Touchpoint history → impact on the card's history (or —)
Owner rulings folded in (comment round 2026-07-12 · REQUIREMENTS_INVENTORY 25–28 · ANSWERS_ROUND3)
① Every saved field is editable (item 27, settled D1) — including the four the handoff deferred: payment_plan, appendix_mode, price_expiry_date, price_after_expiry. They sit low in the Deal group (item 28: relevance ordering).
② Rarely-touched tail de-emphasized — each group keeps its top rows full-size; the rarely-touched tail sits below a hairline divider with smaller/muted labels, so completeness never costs glanceability.
③ Greeting stakes live in a hover tooltip — the field's label is plain "Greeting"; hovering it reveals "Opens every automated email to this lead". The stakes are NOT baked into the label text.
④ Constrained fields are dropdowns — Payment plan and Appendix mode render as selects with exactly the backend's valid options (config.PAYMENT_PLANS = 50% / 100% upfront; appendix = structured / freeform). No free text, no invalid states.
⑤ Greeting fallback is the literal "Szia," — when no greeting was captured, every automated email opens with Szia, (no name). This replaces the old "Szia <first name>" fallback everywhere it resolves. The empty Greeting field carries a quiet amber tint so the one state that most needs attention is the one that catches the eye.
⑥ Rendered-greeting preview — one muted line under the Greeting field shows the exact first line the lead will read ("Kedves Bence,"), so typed-vs-sent punctuation is never a guess.
⑦ Legal verification only when TRULY ambiguous, and only in the post-call modal — no confidence badge anywhere in the Details drawer. The human gate appears solely inside the post-call modal, solely when the AI found conflicting legal candidates and cannot decide which is real. A merely low-confidence-but-unconflicted lookup raises nothing.
⑧ A failed save outlives its toast — if the drawer is closed with an unsaved failed field, the card's Details button stays in a warning state until the value saves.
⑨ Desktop-only — GLOBAL RULE: the pipeline dashboard is a desktop operator tool. Mobile/tablet optimization is explicitly out of scope (all workstreams, not just W3). No agent spends a minute on narrow-width layout; a drawer that is awkward below ~1100px is not a defect.
⑩ Greeting sits SECOND in the Contact group (owner ruling D4 — SETTLED, no longer a question) — directly under the lead's name, above every other Contact row. A Greeting field anywhere else is a defect.
⑪ Late-URL re-enrichment is VISIBLE, not silent (owner ruling D5 — SETTLED, and it ⚠️ REVERSES the earlier "silent, no progress UI" spec) — while the background legal lookup runs, the card shows a small "looking up legal data…" hint; it clears by itself when the lookup finishes, whether or not it found anything. The hint never blocks: the URL save still confirms instantly and the board stays fully usable.
⑫ No "last lookup" line (owner ruling D6 — SETTLED) — the muted "last lookup: today 14:32 — nothing found" line under the Re-run button is not built. The button stays bare.

The owner decision register for W3 is CLOSED — every step below is fully specified; no step may be reported AMBER for want of an owner answer.

Invariants that hold everywhere — a human-typed value always wins: once Mátyás edits a field, no later auto-enrichment ever overwrites it (existing, tested behavior — verify unchanged). Saves are non-blocking: he types, the floating Save button confirms in the background, his flow is never held hostage by a spinner. The Deal store is the single source of truth; the Notion CRM mirror is best-effort and never blocks a save. W3 never sends, schedules, or alters an email. Notion destructive ops stay archive-only. All edits live behind the dash token — no new public surface. English-only operator UI — Hungarian appears only inside the greeting value itself (client-facing copy, e.g. "Szia Bence").
🌱 Seeded test identities (stable — re-created VERBATIM on every reseed)
Every scenario runs on its own ZZ-sentinel lead whose name is a constant, never minted fresh per replay. The judge ties each frame to the seeded entity by this exact string; the walker's probes assert the same lead underneath. Purge all of them after the run (existing ZZ purge path).
ZZ-S1-FIELDS Kft — every saved field populated, greeting Szia ZZ-S1 Bence  ·  ZZ-S1-NOGREET Kft — no greeting captured  ·  ZZ-S2-GREET Kft — greeting Szia ZZ-S2 Bence, edited to Kedves ZZ-S2 Bence  ·  ZZ-S3-AMBIG Kft — legal lookup returned conflicting candidates; confirmed legal name ZZ-S3 Confirmed Legal Kft  ·  ZZ-S3-CLEAN Kft — unambiguous lookup  ·  ZZ-S4-LATEURL Kft — no website, legal enrichment never ran; late URL https://zz-s4-lateurl.example  ·  ZZ-S5-DRY Kft — lookup ran, returned nothing; hand-typed legal name ZZ-S5 Handtyped Legal Kft  ·  ZZ-S6-MANUAL Kft — hand-typed values ZZ-S6 Kezzel Irt  ·  ZZ-S7-SAVEFAIL Kft — save path forced to fail; the never-saved value is ZZ-S7 Never Saved  ·  ZZ-S8-ROUNDTRIP Kft — every field re-typed with a ZZ-S8-RT… value  ·  ZZ-S9-WIDE Kft — all fields populated with long values.

Probe environment. Probes are shell commands run by the walker with $BASE (the staging app root) and $DASH_TOKEN (the live staging dash token — validated with one GET, never computed from local env) exported. The ONLY curly placeholder the walker binds is {lead_id} (the seeded deal's id). No other {…} placeholder is substituted.

S1 — Details drawer opens complete: every saved field present, sorted by relevance (happy path / foundation)

Who: Mátyás  ·  When: He clicks "Details" on any board card to check or fix what the system knows about a lead. Today the drawer shows 18 fields but the system silently saves more (greeting, payment plan, appendix mode, price-expiry pair, legal form) — after W3, nothing the system saves is invisible, and the most-touched fields sit at the top.  ·  Seeded leads: ZZ-S1-FIELDS Kft (greeting Szia ZZ-S1 Bence, all fields populated) and ZZ-S1-NOGREET Kft (no greeting captured).

#You doYou should see Element that changes
copy · look · where
What changes underneathMust NOT happen 🕓 Touchpoint history
1 Mátyás clicks "Details" on the ZZ-S1-FIELDS Kft card The drawer opens with the familiar three groups — Contact → Deal → Legal — and every field the system saves for this lead has a row, pre-filled with its current stored value. Inside each group the daily-touched fields sit full-size at the top; the rarely-touched tail sits below a hairline divider in smaller, muted labels Copy: group headers unchanged (Contact · Deal · Legal) · Look: same drawer, fuller field grid, a hairline divider + smaller muted labels for each group's tail · Where: the Details drawer The drawer renders the full saved-field set from the lead's stored record; the board payload's info projection delivers every field the drawer lists (nothing stripped before it reaches the screen) Must NOT show a field the backend would silently refuse to save (a visible field that can't round-trip is worse than a hidden one); must NOT drop any of the 18 existing fields while adding the new ones; must NOT place a rarely-edited field above a daily-edited one — (opening the drawer reads state; it never writes history)
2 Mátyás scans the Contact group, then hovers the Greeting label Directly under the lead's name sits a new "Greeting" field, pre-filled with the greeting captured from the lead's cold email (Szia ZZ-S1 Bence) — the exact line every automated email to this lead opens with. Hovering the label reveals the tooltip "Opens every automated email to this lead" Copy: label "Greeting"; hover tooltip "Opens every automated email to this lead" · Look: normal wide editable text field, second slot in Contact (right after the name) · Where: Contact group The stored greeting value (captured at pipeline entry from the cold email) is projected into the board payload and rendered into the drawer Must NOT bake the stakes text into the visible label (owner ruling: it belongs in the hover tooltip); must NOT show a raw {{greeting}} token instead of the value; must NOT show a greeting belonging to a different lead; must NOT render it read-only — (the greeting was captured at intake by the inbound flow; showing it adds no entry)
3 Mátyás scans the Deal and Legal groups and opens the Payment-plan dropdown Deal now also lists the four previously-hidden saved fields — Payment plan, Appendix mode, Price expiry date, Price after expiry — in the group's muted tail; Payment plan and Appendix mode are dropdowns (Payment plan offers exactly "50% upfront" / "100% upfront"; Appendix mode offers "structured" / "freeform"), the other two are a date and a number field. Legal gains an editable "Legal form" field alongside the existing four Copy: labels "Payment plan" · "Appendix mode" · "Price expiry date" · "Price after expiry" · "Legal form" · Look: two selects + a date + a number + a text row, all in the muted tail of their group · Where: Deal group tail + Legal group Each new row reads its saved value; the dropdown options are generated from the backend's existing option lists (config.PAYMENT_PLANS, the structured/freeform appendix modes) — never a hand-copied duplicate; the save whitelist and the Notion CRM mirror mapping are extended in lockstep so each new field round-trips (see S8) Must NOT hide any of these because they "seem unimportant" (explicit owner ruling — every saved field gets a row); must NOT render Payment plan or Appendix mode as free text; must NOT offer a Payment-plan option the backend rejects (e.g. a 70% option — it was dropped)
4 Mátyás opens Details on ZZ-S1-NOGREET Kft — a lead whose cold email had no usable greeting line The Greeting field is empty and quietly amber-tinted, with the placeholder: "empty — emails will open with 'Szia,'" — so he knows exactly what the fallback does before any email goes out Copy: placeholder text as quoted · Look: empty field with a quiet amber tint (the one state that needs attention is the one that catches the eye) · Where: Greeting field, Contact group No stored greeting → the field stays blank; the greeting-resolution fallback is the literal "Szia," (owner ruling 2026-07-12 — this replaces the previous "Szia <first name>" fallback in the shared resolver, so every send path inherits it) Must NOT invent/auto-fill a greeting into the stored record just to fill the box (an empty store stays empty until a human or the capture flow writes one); must NOT hide the field when empty; must NOT resolve to "Szia <first name>" anywhere after this change

S2 — Edit the Greeting → the next automated email opens with the new line (happy path / the item-26 payoff)

Who: Mátyás  ·  When: The auto-captured greeting is wrong or clumsy (a garbled first line, wrong nickname) and he wants every future automated email to this lead to open with a line he chose — without touching anything else.  ·  Seeded lead: ZZ-S2-GREET Kft (greeting Szia ZZ-S2 Bence).

#You doYou should see Element that changes
copy · look · where
What changes underneathMust NOT happen 🕓 Touchpoint history
1 Mátyás retypes the Greeting field (Szia ZZ-S2 BenceKedves ZZ-S2 Bence) and clicks the floating Save button While he types, a muted preview line under the field shows the exact first line the lead will read: "Kedves ZZ-S2 Bence," (the comma the template adds is visible, so double-punctuation is impossible to miss). On Save the drawer stays free instantly — the existing floating Save button does its usual brief "Saved" confirmation; no modal, no blocking spinner Copy: the field holds his new text; the preview line reads "Kedves ZZ-S2 Bence," · Look: one muted line directly under the Greeting input + the existing Save-button confirmation · Where: Contact group + floating Save The new greeting is written to the lead's record and stamped as a manual field (so no future auto-capture/enrichment can overwrite it); the Notion CRM's existing "Greeting" property is mirrored in the background, best-effort, never blocking the save. The preview renders through the same resolver the sender uses — it is not a second implementation Must NOT be silently dropped by the save whitelist (today a greeting write is discarded server-side — that is the exact bug W3 closes); must NOT block the UI on the Notion mirror; must NOT touch any other field in the same save; must NOT show a preview line that disagrees with what the sender would render — (a field edit is not a lead touchpoint; history stays owned by W2)
2 Later, the next automated email for this lead is generated (Send-Emails modal or any template render) — Mátyás just reads it The email copy opens with his edited line ("Kedves ZZ-S2 Bence,") — not the originally captured greeting, not the fallback Copy: the email's first line = the saved Greeting value · Look: unchanged email UI · Where: wherever email copy renders (Send-Emails modal, history preview) The existing {{greeting}} resolution reads the stored value — W3 changed the stored value's visibility and editability plus the empty-case fallback, never the resolution mechanism (existing flow, verify unchanged) Must NOT render the stale pre-edit greeting anywhere copy is generated after the save; must NOT alter when/whether that email sends (W1's domain) When that email actually sends, W2's history row must show the resolved edited greeting — parity between shown and sent is W2's hard bar; W3 just feeds it the right value.
3 Background proof (QA, not a click): probe the saved record and the CRM mirror a few seconds after step 1 — (nothing on screen; this row is the reliability check behind the non-blocking save) The lead record holds the new greeting + the manual-field stamp; within a short background window the Notion CRM page's "Greeting" property shows the same value (capped at the same 300-char limit used at intake) Must NOT find the store and the CRM mirror permanently disagreeing; a failed mirror write must not un-save the store value

S3 — Conflicting AI legal data → one human verification gate, inside the post-call modal (happy path / trust gate — REWRITTEN by owner comment)

Who: Mátyás  ·  When: The AI legal lookup found conflicting candidates (two companies at the same brand, an old and a new tax number) and cannot decide which is the real legal entity — yet that data auto-fills the contract's Megbízó block. He must confirm the truth exactly once, at the moment it matters: in the post-call modal, on the way to the proposal/contract. Owner ruling: this gate appears ONLY when the lookup is truly ambiguous/conflicting, and ONLY in the post-call modal — never as a badge in the Details drawer.  ·  Seeded leads: ZZ-S3-AMBIG Kft (lookup returned two conflicting candidates) and ZZ-S3-CLEAN Kft (unambiguous lookup).

#You doYou should see Element that changes
copy · look · where
What changes underneathMust NOT happen 🕓 Touchpoint history
1 Mátyás opens the post-call modal on ZZ-S3-AMBIG Kft (the 3-step post-call wizard on the card) Before he can move on to the proposal, a compact "Verify legal data" block sits in the modal: the conflicting candidates the AI found, side by side, each showing legal name / seat address / tax number / representative — plus editable fields so he can correct anything, and one "Confirm" button Copy: block title "Verify legal data — the AI found conflicting records"; button "Confirm" · Look: compact amber-edged block inside the existing post-call modal, above the proposal step · Where: post-call modal only The block renders off the stored ambiguity signal the legal lookup wrote (conflicting candidates + the "cannot decide" flag). Nothing is written by showing it Must NOT appear for a lead whose lookup was unambiguous (even if it was merely low-confidence); must NOT appear for a lead with no legal data at all (nothing to verify — that is the Re-run case, S5); must NOT appear anywhere outside the post-call modal; must NOT block him from closing the modal entirely (he can leave; the gate returns on the next open) — (verifying CRM data is not a lead touchpoint)
2 Mátyás picks the correct candidate (or corrects a field by hand) and clicks Confirm The block collapses to a quiet one-line confirmation and the modal continues to the proposal step exactly as before; the confirmed legal values now appear in the Details drawer's Legal group Copy: "Legal data confirmed" one-liner replacing the block · Look: block gone, wizard proceeds · Where: post-call modal → Details drawer Legal group The four legal fields are written to the deal and stamped as manual fields (human-owned forever); the ambiguity flag is cleared, so the gate never asks again for this lead; the contract's Megbízó block fills from these confirmed values Must NOT ask again on the next post-call-modal open for the same lead; must NOT let a later auto-lookup overwrite the confirmed values; must NOT send or schedule anything as a side effect of confirming
3 Mátyás opens the post-call modal on ZZ-S3-CLEAN Kft (unambiguous lookup), then its Details drawer No verification block in the modal — it goes straight to the proposal step. The Details drawer shows the legal values as plain editable fields with no badge of any kind: silence is the normal state Copy: — · Look: clean modal, clean Legal group header · Where: post-call modal + Details drawer No ambiguity flag stored → nothing renders; the drawer's Legal group never carries a confidence badge in any state Must NOT show a confidence/"AI guess" badge in the Details drawer (owner ruling: the drawer stays clean, the gate lives in the post-call modal); must NOT show a stale verification block carried over from a previously-opened lead

S4 — Website URL added late → legal enrichment re-fires by itself (happy path / automation-default)

Who: Mátyás + system  ·  When: A lead entered the pipeline with no discoverable website, so the legal lookup had nothing to search and the Legal group sat blank. Later Mátyás learns the website and types it into the existing "Website URL missing" red-badge inline form. Today that saves the URL and nothing else happens — the lead stays legally blank forever. After W3, the lookup re-fires on its own, and it says so on the card while it works ("looking up legal data…" — owner ruling D5, which reverses the earlier silent design).  ·  Seeded lead: ZZ-S4-LATEURL Kft (no website, no legal enrichment ever run).

#You doYou should see Element that changes
copy · look · where
What changes underneathMust NOT happen 🕓 Touchpoint history
1 Mátyás types the lead's website into the red-badge inline URL form and saves The form confirms instantly, exactly as today — he is free to keep working immediately; no new dialog, no wait. And the card immediately shows a small "looking up legal data…" hint (owner ruling D5 — the re-enrichment is NOT silent), so he knows the machine picked the URL up and is already working Copy: unchanged existing form + the new hint text "looking up legal data…" · Look: the existing form untouched; the hint is a small muted line/chip on the card where the red website badge used to sit · Where: the card's website-missing badge/form area The URL saves as before, plus: because this lead's legal enrichment never ran, a background enrichment pass (website scrape → AI legal lookup) is kicked off fire-and-forget — the save response never waits for it. The lead now carries an "enrichment in flight" marker that the board payload exposes; the card renders the hint straight off that marker Must NOT block or slow the URL save on the multi-second AI lookup; must NOT run the lookup silently with no on-screen sign (the old spec — reversed); must NOT make the hint a modal/spinner that blocks the card or the board; must NOT re-fire enrichment for a lead whose legal enrichment already completed (only the never-ran case auto-triggers) — (enrichment fills CRM fields; it is never a touchpoint — enrichment stays invisible to history)
2 Nothing — he watches the card while the background lookup runs, then finishes The "looking up legal data…" hint sits on the card for the whole run, then disappears by itself when the lookup finishes — whether it found legal data or came back empty. Nothing else changes on the card; the board was usable the entire time Copy: hint present → hint gone · Look: the hint clears with no toast, no banner, no click needed · Where: the card The enrichment pass clears the in-flight marker when it completes (success, empty result, or error — all three clear it); the card stops rendering the hint on the next board refresh. A crashed/timed-out lookup must not strand the hint forever — the marker expires Must NOT leave the hint stuck on the card after the lookup ends (including when it found nothing or errored); must NOT require a click or a reload to clear it; must NOT show the hint on a lead with no lookup in flight
3 A little later, Mátyás opens the lead's Details drawer The Legal group is populated — Legal name, Seat address, Tax number, Representative and Legal form carry the lookup's results, all editable, with no badge Copy: real legal values in the fields · Look: filled editable fields · Where: Legal group The background lookup wrote the legal fields through the existing enrichment guard — any field Mátyás had already typed by hand was skipped, untouched. If the lookup came back conflicting, the ambiguity flag is stored for S3's post-call gate (nothing renders in the drawer) Must NOT overwrite a hand-typed legal field (the human-wins invariant); must NOT leave the contract's client block blank when this data now exists; must NOT surface the ambiguity in the drawer

S5 — Lookup finds nothing → fields stay blank + a manual "Re-run legal lookup" button (edge / recovery)

Who: Mátyás  ·  When: The AI lookup ran but found nothing usable (dead site, no legal data findable), or he suspects the company's records changed. He needs an explicit retry that doesn't require re-entering anything — and the hand-typed escape hatch must keep working.  ·  Seeded lead: ZZ-S5-DRY Kft (lookup ran, returned nothing).

#You doYou should see Element that changes
copy · look · where
What changes underneathMust NOT happen 🕓 Touchpoint history
1 Mátyás opens Details on ZZ-S5-DRY Kft — a lead whose lookup ran but produced nothing Empty Legal fields and a small "↻ Re-run legal lookup" button on the Legal group header — bare: no "last lookup: … — nothing found" line under it (owner ruling D6, settled: that line is not built) Copy: button "↻ Re-run legal lookup" and nothing else · Look: compact secondary button beside the group header · Where: Legal group, Details drawer Nothing yet — the button only renders; the empty-result state is stored so the drawer knows the lookup ran and came back dry, but that state is never surfaced as a line of text Must NOT hide the button (it is the only retry affordance); must NOT show any badge over the empty fields (nothing was guessed, nothing is conflicting); must NOT render a "last lookup …" / "nothing found" line anywhere near the button (explicitly ruled out)
2 Mátyás clicks "↻ Re-run legal lookup" (and immediately clicks it a second time) The button acknowledges instantly (brief "running…" state) and goes inert; he is free to close the drawer and move on — the lookup runs in the background; on his next drawer open the results (or the still-blank fields + button) tell him the outcome Copy: button flips to "running…" then back · Look: button-only state change, drawer never locks · Where: Legal group header The lead's enrichment-done marker is cleared and the existing enrichment machinery re-runs in the background (reused, not reimplemented); results write through the human-wins guard as always; the second click is swallowed — exactly one lookup runs Must NOT block the drawer while the AI call runs; must NOT stack duplicate lookups from double-clicks (the button is inert while one is in flight); must NOT overwrite any hand-typed field with the retry's results
3 The AI still finds nothing — Mátyás types the four legal fields by hand and saves The values persist like any field edit; from now on those fields are human-owned and no future lookup (auto or manual re-run) touches them Copy: his typed legal data · Look: normal field save · Where: Legal group Each typed field gets the manual-field stamp — the existing, tested invariant (existing flow, verify unchanged) Must NOT let the re-run button's next click clobber the hand-typed values; must NOT regress the existing manual-edit-wins test

S6 — Human-typed values always beat auto-enrichment (existing invariant — verify unchanged)

Who: Mátyás + system  ·  When: He types a value into any Details field before an enrichment pass completes for that lead (common with the new late re-enrichment: he might fill Legal name by hand seconds before the background lookup lands). This is existing, tested behavior W3 extends to its new fields — the EBO verifies it did not regress.  ·  Seeded lead: ZZ-S6-MANUAL Kft.

#You doYou should see Element that changes
copy · look · where
What changes underneathMust NOT happen 🕓 Touchpoint history
1 Mátyás hand-edits fields on ZZ-S6-MANUAL Kft — including the new Greeting and Legal form — saves, and a background enrichment pass completes afterwards Reopening the drawer shows his values, exactly as typed — the enrichment filled only the fields he never touched Copy: his values intact; other blanks filled by enrichment · Look: unchanged drawer · Where: Details drawer Enrichment writes through the guard that skips every manually-stamped field — existing tested behavior, now also covering greeting and legal_form Must NOT overwrite any human-typed value, ever — including the new W3 fields; must NOT strip the manual stamp during any save/mirror path

S7 — Save fails → the failure is loud, and it outlives the toast (failure)

Who: Mátyás  ·  When: A save from the Details drawer fails (network hiccup, expired token). The nightmare to prevent: he "fixes" the greeting, the save dies silently, he closes the drawer, and next week an automated email opens with the bad line he thought he'd corrected.  ·  Seeded lead: ZZ-S7-SAVEFAIL Kft (save path forced to fail).

#You doYou should see Element that changes
copy · look · where
What changes underneathMust NOT happen 🕓 Touchpoint history
1 Mátyás edits the Greeting on ZZ-S7-SAVEFAIL Kft, clicks Save while the save path is broken, then closes the drawer The existing "save failed" warning appears on the floating Save button, his typed text is still sitting in the field (not reverted) — and after he closes the drawer, the card's Details button stays in a warning state until the value actually saves. The failure outlives the toast Copy: the existing failure toast wording (unchanged) · Look: existing warning state on the Save button + a persistent warning mark on the card's Details button · Where: floating Save button · edited field · the card's Details button The store write did not happen; the drawer keeps the unsaved text client-side so a retry click can resend it (existing flow, verify unchanged — extended to the new fields); the card remembers it holds an unsaved failed field until a save succeeds Must NOT show "Saved" when the write failed; must NOT wipe his typed text back to the stored value; must NOT let the warning disappear when the drawer closes; must NOT leave the new fields (Greeting etc.) outside this failure path while the old 18 are inside it

S8 — Every visible field round-trips: no silently-dropped saves, ever again (guardrail / anti-desync)

Who: QA / the system  ·  When: The greeting bug existed precisely because the drawer's field list, the backend save whitelist, and the CRM mirror mapping live in three hand-synced places — a field can be visible yet unsavable. This scenario locks the guard that makes that class of bug impossible to reintroduce.  ·  Seeded lead: ZZ-S8-ROUNDTRIP Kft.

#You doYou should see Element that changes
copy · look · where
What changes underneathMust NOT happen 🕓 Touchpoint history
1 QA sweep (no owner click): edit-and-save every single field the drawer shows on ZZ-S8-ROUNDTRIP Kft, then reopen the drawer Every value comes back exactly as typed — all groups, old fields and new alike; zero fields accept input on screen but lose it on save Copy: — · Look: — · Where: whole Details drawer, field by field Every drawer field has a matching entry in the save whitelist and (where the CRM has the property) the Notion mirror mapping; a test fails the build if any of the three lists drifts from the others Must NOT ship any field that renders but doesn't persist; must NOT let a future field addition touch one list without the guard test catching the other two

S9 — The fuller drawer holds up on the desktop dashboard (guardrail / layout)

Who: Mátyás  ·  When: He opens Details from his laptop. The drawer just grew ~6 fields, two dropdowns, a preview line, and a button — nothing may overflow, clip, or overlap. Owner ruling (global): the pipeline dashboard is a desktop-only operator tool — tablet/mobile layout is explicitly out of scope and is NOT tested here.  ·  Seeded lead: ZZ-S9-WIDE Kft (all fields populated with long values).

#You doYou should see Element that changes
copy · look · where
What changes underneathMust NOT happen 🕓 Touchpoint history
1 Mátyás opens the Details drawer on ZZ-S9-WIDE Kft at desktop widths (1280 and 1440) All three groups, the Greeting field + its preview line, both dropdowns, the muted tail rows and the Re-run button render cleanly inside the drawer; long values wrap or scroll inside their own field; no horizontal page scroll Copy: full · Look: full drawer layout, nothing clipped · Where: Details drawer Standard desktop layout; the drawer scrolls vertically when the field list is long Must NOT clip the Re-run button or a dropdown off the drawer edge; must NOT overlap labels and inputs; must NOT push the page into horizontal scroll
🕓 Touchpoint-history note for W3:
W3 is a CRM-data workstream — it never authors a touchpoint. Editing a Details field, running a legal lookup, confirming ambiguous legal data, and fixing a greeting all change what the system knows, not what happened between Mátyás and the lead; every history column above is "—" by design (enrichment activity is never written to history — project rule). The one place W3's output reaches history is indirect: after a greeting edit (S2), the next email W1 sends and W2 records must open with the edited line — the history row's copy-parity guarantee is W2's bar; W3's job is only that the stored value W2 reads is the one Mátyás saved.

Work-item → scenario-step mapping

Each build work item for W3 and the scenario steps that prove it works when clicked through live on staging. The /ebo-live-build walker drives exactly the listed steps after each item ships, then the full set before merge. Ruling notes mark where the 2026-07-12 owner comment round reshaped an item.

Work itemWhat it deliversProven by (scenario · step)Ruling note
WI-1 · Greeting field + preview + fallbackEditable "Greeting" field in Contact (second slot) with a hover tooltip and a live rendered-first-line preview, wired end-to-end: board projection → drawer → save whitelist → manual stamp → Notion "Greeting" mirror → template resolution. Empty-greeting fallback becomes the literal "Szia,"; the empty field carries a quiet amber tint.S1·2 · S1·4 · S2·1 · S2·2 · S2·3RESHAPED by owner comments — stakes text moved from the label into a hover tooltip; preview line applied; fallback changed from "Szia <first name>" to "Szia,". D4 SETTLED — the field's place is the SECOND slot of the Contact group, under the lead's name; anywhere else is a defect. Cross-workstream: the fallback change is in the shared resolver — W1/W2 send copy inherits it.
WI-2 · Full field census + tail de-emphasisEvery saved lead field gets an editable row (adds Payment plan, Appendix mode, Price expiry date, Price after expiry, Legal form), sorted by relevance; each group's rarely-touched tail sits below a hairline divider in smaller muted labels; Payment plan + Appendix mode render as dropdowns fed by the backend's own option lists.S1·1 · S1·3 · S8·1D1 SETTLED — the four deferred Deal fields are IN (item 27 + the owner's dropdown ruling presupposes them). Dropdowns + tail de-emphasis applied per comments.
WI-3 · Ambiguous-legal verification gateThe legal lookup records a conflicting-candidates / cannot-decide signal; the post-call modal shows a "Verify legal data" block with the candidates + editable fields + Confirm, only for that state. Confirming stamps the four fields manual and clears the flag.S3·1 · S3·2 · S3·3 · S4·3REWRITTEN by owner comment — the Details-drawer confidence badge is DEAD. Verification only when truly ambiguous/conflicting, and only inside the post-call modal. A merely low-confidence lookup raises nothing.
WI-4 · Auto re-enrichment on late URL + the "looking up legal data…" hintSaving a website URL for a never-legally-enriched lead fires the existing enrichment machinery in the background without blocking the save — and the card shows a small "looking up legal data…" hint for as long as the lookup runs, clearing itself on completion (found, empty, or error), with no click and no reload.S4·1 · S4·2 · S4·3⚠️ REVERSED by owner ruling D5 (settled) — the run is no longer silent: the in-flight hint is now REQUIRED visible behavior (an in-flight marker on the deal + the card hint + its self-clearing). Everything else about the item is unchanged.
WI-5 · Manual "Re-run legal lookup"Retry button on the Legal group header — instant acknowledgment, background run, double-click-safe, human-typed fields untouched. The button stays bare.S5·1 · S5·2 · S5·3D6 SETTLED — the muted "last lookup … — nothing found" line is NOT built. Do not add it.
WI-6 · Human-wins invariant extendedThe existing manual-edit-wins guard covers the new fields (greeting, legal form, the four Deal-tail fields) on every auto-fill path; existing tests stay green.S6·1Unchanged existing invariant — verified, not redesigned.
WI-7 · Save-failure integrityNew fields ride the existing failure toast + keep-typed-text behavior, and the failure now persists on the card's Details button after the drawer closes.S7·1EXTENDED by owner comment — the failure must outlive the transient toast.
WI-8 · Field-list lockstep guardA test that fails the build if the drawer field list, the save whitelist, the board projection, and the CRM mirror mapping ever drift apart again.S8·1Unchanged.
WI-9 · Desktop drawer layoutThe grown drawer holds at 1280 and 1440 — nothing clipped, overlapped, or pushed into horizontal scroll.S9·1NARROWED by owner comment — tablet/mobile dropped entirely (global desktop-only rule for the pipeline dashboard).
Sign-off — acceptance oracle. By signing, Sarudi Mátyás locks scenarios S1 … S9 and the invariants above as the acceptance answer key for the W3 Lead Details Panel. The decision register is CLOSED — D4 (Greeting second in Contact), D5 (the "looking up legal data…" hint is REQUIRED) and D6 (no "last lookup" line) are ruled and folded in above; no step may be reported AMBER for want of an owner answer. No W3 code is built or deployed before this signature.
Sarudi Mátyás  ✔ Final · 2026-07-12 · awaiting sign-off