ClientsFlow · Email-System Overhaul · W6 · EBO
DRAFT for owner comments · 2026-07-12. The target state after W6 ships: every AI- or template-generated client-facing text is provably correct — canonical sign-off locked by a machine test, phone/bank details impossible to leak outside payment emails, every email's copy editable where the owner expects it (Templates) or visibly not, no raw {{variable}} ever reaching a client, and a written QA-trust gate + Codex second-model review plan that every other workstream's "done" claim must pass. Assembled from the W6 handoff, ANSWERS_ROUND3 (Q20/Q21 + Q1/Q4/Q7 copy anchors), the standing round-2 Q6 ruling, and the owner's W1 comment round. Scenarios W6-1 … W6-8.
{{greeting}} + the canonical sign-off, with 100% parity between shown and sent copy — W6 proves these properties; W2 builds the surfaces. W1 owner comments (newest, win on conflict): the failures list is dead (a failure = red dashed ring + banner, nothing more); the payment chase is a fully automatic daily reminder ladder (3–4 days, stops on payment or a logged phone call — no owner button); the signed-contract email already exists and works ("Don't try to fix what is working — you can test it however") — W6 verifies it, never redesigns it. Trust-crisis mandate (round-2 Q12, verbatim): "I can't really trust that if I say 'You go!' then it will refine the app to production grade" — everything below exists to convert that trust from vibes to proof.SIGNATURE_HU block (name / role line / company + address / email), no phone, no bank line; bank + phone details appear ONLY inside the three payment-instruction bodies (signed-contract delivery email, payment-reminder ladder, proposal footer); no client-facing text ever contains a raw {{token}}; W6 sends nothing new to anyone — it tests code, reads already-sent mail, and edits documents; all operator UI is English, Hungarian appears only inside client-facing copy; W6's regression test may never be disabled/skipped to make a run green — a red test is the feature working.| # | You do | You should see | Element that changes copy · look · where |
What changes underneath | Must NOT happen | 🕓 Touchpoint history | 💡 UX critique + suggestion |
|---|---|---|---|---|---|---|---|
| 1 | Run the email-copy test: pytest tests/test_signoff_footer_regression.py -v |
The test passes green and its output names every outbound email it checked — signed-contract delivery, payment reminders, booking confirmation, pre-call reminders, proposal, no-show ladder, first reply, negative follow-ups — each one confirmed to end with the canonical 4-line sign-off | Copy: test output = one named line per checked email body · Look: terminal, green · Where: new file tests/test_signoff_footer_regression.py |
Every outbound-email-composing function is rendered with sample lead data and asserted to end with SIGNATURE_HU (an optional "Üdv,\n" immediately before it is allowed); rep-only internal notification bodies are explicitly excluded, not silently skipped |
Must NOT skip any client-facing send site (a body not in the walk is a hole, exactly how the incident escaped); must NOT pass if even one body ends with anything other than the canonical block; must NOT need network/Notion to run | — (a test run touches no lead) | Critique: A test that names 15 functions is proof to a developer, but the owner can't read pytest output — his trust needs a surface he can see. Suggestion: Have the test also write a one-line dated stamp ("Sign-off check: 15/15 emails canonical — 2026-07-12") that the Health tab shows, so the owner sees the lock is on without reading a terminal. |
| 2 | Run the documented "teeth check": temporarily swap the sign-off constant to the OLD leaked 4-line footer (the one with "Phone (used in CTAs): +36 70 …" + "Bank: 1260…") and re-run the test | The suite goes RED and the failure message names each affected email function and quotes the offending leaked line — the exact 2026-07-11 incident is now impossible to ship silently | Copy: failure text = function name + the leaked string found · Look: terminal, red · Where: same test file (the swap recipe lives in its docstring as a manual step, never a permanent fixture) | Nothing persists — the swap is reverted immediately after the check; the point is a recorded proof that the test catches the historical incident, not a shipped state | Must NOT leave the leaked footer in the working tree after the check; must NOT encode the teeth check as an always-on expected-failure that could mask a real regression | — | Critique: A manual teeth check done once and forgotten decays — in a year nobody will remember whether the test still bites. Suggestion: Record the teeth-check date + result inside the test file's docstring each time it's performed, so "when was the lock last proven" is answerable in one glance. |
| 3 | Later, any workstream edits an email body and (accidentally) drops or mangles its sign-off, then runs the suite | The suite fails loudly, naming exactly which email lost its sign-off and what its last lines actually were — the fix is obvious from the message alone | Copy: "email_signed_document body does not end with SIGNATURE_HU — got: '…'" · Look: red test, precise message · Where: CI / local suite | No code merges through the Ship pipeline with this test red; the failing function is fixed, never the test loosened | Must NOT be "fixed" by disabling, skipping, or weakening the test (the EBO sign-off requires an explicit "this test was NOT disabled" statement from whoever runs it); must NOT pass because the body merely contains the sign-off somewhere — it must END with it | — | Critique: "Ends with" is strict and right, but future bodies might legitimately add a P.S. line under the signature and the test would block a wanted change. Suggestion: Keep strict "ends with" as the default; if a P.S.-after-signature is ever wanted, that's a deliberate one-line allowlist entry in the test with a comment — a visible decision, never a silent loosening. |
| # | You do | You should see | Element that changes copy · look · where |
What changes underneath | Must NOT happen | 🕓 Touchpoint history | 💡 UX critique + suggestion |
|---|---|---|---|---|---|---|---|
| 1 | Run the same regression test (it carries the leak guard as its second half) | Green, plus an explicit printed allowlist: bank/phone literals found ONLY at the three verified payment-instruction sites (signed-contract client body · payment-reminder bank lines · proposal footer bank block) and in the contract's legal block — nowhere else in any rendered email | Copy: the allowlist is spelled out in the test as three named sites, not a blanket skip · Look: terminal · Where: same test file | Every rendered body is scanned for the phone number, the bank account number, the "Bank:" footer prefix, and the historical "Phone (used in CTAs)" marker; hits outside the allowlist are hard failures | Must NOT allowlist by whole file (a whole-file skip would let a new leak into an allowed file pass); must NOT treat the DocuSeal contract legal block as a leak (it is a contract field, not an email) | — | Critique: Literals drift — if the bank account number ever changes, a stale literal in the test silently guards the wrong string. Suggestion: Make the test read the phone/bank strings from the single config constants it guards, so the guard always chases the real values, never a copy of them. |
| 2 | Simulate the failure: a change adds a bank-transfer line to a non-payment email (e.g. a booking confirmation), suite runs | Hard RED — the failure names the email and quotes the leaked line: payment details in a non-payment email cannot reach staging, let alone a lead | Copy: "bank literal found outside allowlist in email_booking_confirmation: 'Bank: 1260…'" · Look: red test · Where: CI / local suite | The offending body is fixed; the allowlist grows only by a deliberate, commented decision (a fourth legitimate payment body would be an owner-visible change) | Must NOT resolve the red by adding the leaking site to the allowlist as a reflex; must NOT average/soften the verdict (one leak = red, no partial credit) | — | — |
| # | You do | You should see | Element that changes copy · look · where |
What changes underneath | Must NOT happen | 🕓 Touchpoint history | 💡 UX critique + suggestion |
|---|---|---|---|---|---|---|---|
| 1 | Mátyás opens the Templates tab (Sablonok) on the dashboard | The signed-contract delivery email and each step of the payment-reminder ladder now appear in the template list as editable entries, alongside the existing templates — previously they were simply absent, with no hint they were code-only | Copy: new list entries "Signed contract — delivery email" · "Payment reminder — day 1…4" · Look: same template list style as existing entries · Where: Templates tab | The two bodies move from hardcoded Python literals into the owner-editable copy layer; their send timing and triggers stay exactly as they are (W1's automatic flow — auto-send on client signature, then one reminder a day for 3–4 days until payment or a logged phone call) | Must NOT change what these emails currently say (migration is verbatim — the current live text becomes the initial template text); must NOT alter when/whether they send (that is W1's, untouched); must NOT list them as editable while edits still silently do nothing (the whole point) | — | Critique: After migration, an owner edit here changes real money-path emails with no preview of a rendered result — a typo in the day-2 reminder goes to every future signer. Suggestion: Show a rendered preview (sample lead data, greeting + amounts resolved) beside the editor before Save — the same "read what will actually go out" habit the Send-Emails modal already trains. |
| 2 | Mátyás edits the signed-contract email's wording (e.g. rewrites the opening sentence in Hungarian), saves, then a ZZ-sentinel staging contract-signing test fires the email | The staging test email arrives with his edited opening — proof the edit is LIVE, not decorative; the email still carries everything the flow promises: confirmation, the signed-contract download link, the Stripe link, and the transfer details, ending with the canonical sign-off | Copy: his edited Hungarian text, e.g. "Kedves Anna, örömmel jelezzük, hogy a szerződés létrejött…" · Look: real received email (staging, ZZ lead) · Where: the ZZ test inbox + the lead card's history | The send site now reads the body through the owner-editable copy layer (live override → baked default), interpolating the deal's Stripe link/amount into the edited text; the regression test from W6-1/W6-2 still passes against the edited copy | Must NOT send the old hardcoded text after a save succeeded (the silent-no-op bug this exists to kill); must NOT let an edit strip the sign-off or the payment block (the test + locked block guard this); must NOT run this against a real lead — ZZ sentinel only, purged after | The staging ZZ card's history logs the sent email with the edited text — 100% parity between the history row and the received email (W2's parity bar, verified here on W6's migrated copy). | Critique: These bodies interpolate live deal data (amount, Stripe link) — an owner edit that deletes the amount placeholder would send a reminder that never says how much to pay. Suggestion: On Save, validate that the required placeholders (amount, payment links) are still present and refuse the save with a plain-English message naming the missing piece ("The payment amount is missing from this template") instead of saving a broken money email. |
| 3 | Mátyás looks at the payment-details part inside the template editor and tries to change or delete it | The bank-transfer + Stripe details render as a visibly locked block inside the template — readable in place so he sees the full email, marked "Locked — payment details", and not editable or deletable; his cursor edits flow around it | Copy: locked-block label "Locked — payment details" (English UI); the block's content stays the Hungarian bank/Stripe lines · Look: the block is visually set off (muted background, lock icon) from the editable text · Where: template editor for the two payment emails | The payment-details block stays sourced from the single code-side constant — owner edits can reposition text around it but the block's content is not part of the editable template body | Must NOT allow the locked block to be edited, emptied, or removed through any editor path (including select-all-delete — the saved template must still render the block); must NOT hide the block from the editor view (invisible-but-sent text is a parity violation) | — | Critique: A locked block the owner can't move at all may fight him — maybe he wants the payment details above his closing paragraph, not below. Suggestion: Keep content locked but allow the block to be repositioned as a unit within the body (drag or a position marker). Content integrity is the invariant; position is taste. |
| # | You do | You should see | Element that changes copy · look · where |
What changes underneath | Must NOT happen | 🕓 Touchpoint history | 💡 UX critique + suggestion |
|---|---|---|---|---|---|---|---|
| 1 | Run python3 scripts/detect_copy_drift.py |
A table of EVERY client-facing email/SMS body in the app — one row each, tagged EDITABLE (reachable from the Templates layer) or HARDCODED (code-only), with file:line — and the script exits clean. After W6-3 lands, the signed-contract + payment-reminder rows read EDITABLE | Copy: table columns = email name · source file:line · Editable/Hardcoded · which template key edits it · Look: terminal table · Where: new file scripts/detect_copy_drift.py |
The script walks all email-body constants and inline body literals across the send-site inventory and cross-checks each against the owner-editable copy layer's reference list (the ~25 existing wrapped sites are the positive baseline) | Must NOT miss inline body literals that never got a named constant (the strongest form of hardcoded — must be flagged, not skipped); must NOT report a body as editable when the Templates edit would actually be ignored | — | Critique: A terminal table is invisible to the owner — the person whose Templates edits silently failed is the last to see this output. Suggestion: If any body is HARDCODED, surface that same fact in the Templates tab as a small read-only entry marked "Code-only — editing requires a code change", so the tab never again implies an editability it doesn't have. |
| 2 | Later, some workstream adds a new email body as a raw Python string (no Templates wiring), then the detector re-runs | The new body appears in the table tagged HARDCODED with its file:line — the drift is a visible, reviewable fact within one detector run, not a surprise months later when an owner edit does nothing | Copy: new HARDCODED row · Look: terminal table diff · Where: same script output | Nothing auto-fixes — the detector reports; whether the new body should be wired into Templates is a per-case owner/reviewer call made ON the report | Must NOT auto-rewrite code (report-only tool); must NOT go stale — it derives from the live code on every run, never from a saved snapshot | — | Critique: Report-only tools get ignored unless something makes people run them. Suggestion: Fold the detector into the QA-trust gate (W6-7): a workstream that added/changed any email body must attach a fresh detector run to its GREEN claim. One line in the gate, and the tool can't be forgotten. |
{{variable}} ever reaches a client; greetings resolve correctly everywhere (happy path + guard / cross-surface verification)| # | You do | You should see | Element that changes copy · look · where |
What changes underneath | Must NOT happen | 🕓 Touchpoint history | 💡 UX critique + suggestion |
|---|---|---|---|---|---|---|---|
| 1 | Mátyás clicks Send Emails on a (ZZ staging) lead card and reads the generated sequence in the modal | Every email in the modal opens with the lead's real resolved greeting — "Kedves Anna," — correct name, correct Hungarian form; nowhere in any preview does a raw {{greeting}}, {{name}}, or any other {{…}} token show (existing flow, verify unchanged) |
Copy: resolved Hungarian greeting per email · Look: the Send-Emails modal previews · Where: lead card → Send Emails modal (W1/W2's surface) | Copy is generated at Send-click from the templates + touchpoint history (Q1's confirmed model); variable resolution happens at generation, so what he reads IS what will be sent/frozen | Must NOT show a resolved preview but send an unresolved body (preview↔send parity); must NOT fall back to an empty greeting ("Kedves ,") when a name field is missing — a missing input must surface as a visible problem in the modal, not ship as broken Hungarian | On Send, the frozen scheduled rows in history carry the same resolved text as the modal showed (W2's parity bar — W6 checks the copy half of it). | Critique: "Kedves ," (missing name) is worse than a raw token — it looks like carelessness in fluent Hungarian, and no scanner keyed on "{{" catches it. Suggestion: Extend the guard beyond "{{" to malformed-greeting shapes (greeting line with no name, doubled spaces, lowercase name) — a tiny list of Hungarian-specific tripwires alongside the token check. |
| 2 | Mátyás clicks the compose-AI-reply button on a card with an unreplied lead email and reads the AI draft (W2's new feature) | The draft opens with the proper resolved Hungarian greeting, its body draws on this lead's actual history, and it ends with the exact canonical sign-off — the three properties Q4 names, all present before he edits a word | Copy: "Kedves János," + contextual Hungarian body + the 4-line canonical sign-off · Look: the editable AI-reply field on the card · Where: lead card (W2/W8's surface) | The AI generation path runs the same greeting-resolution and sign-off rules as template emails — one copy-rule set, not a separate AI-path approximation | Must NOT show a raw token in an AI draft (AI output passes the same guard as template output); must NOT let the AI improvise its own sign-off variant (extra titles, different address, added phone) — the canonical block is appended by rule, not by the model's memory | — (a draft is not a send; the send that may follow is W1/W2's, and its history row must match what left — parity again) | Critique: The model can produce a perfect sign-off AND a redundant home-grown one above it ("Üdvözlettel, Mátyás"), reading as a double goodbye. Suggestion: The guard should also fail on duplicate closings — if the body already contains a closing formula right before the appended canonical block, flag the draft for one-click cleanup before send. |
| 3 | Run the automated variable-leak check (part of W6's test layer) across all rendered bodies + audit the recent real sent mail in Missive for the same | Zero {{ occurrences in any renderable body, and zero in the last weeks' actually-sent client emails — the guard holds in code and in reality |
Copy: test output + a short audit note (which sent emails were read, result) · Look: terminal + audit note in the run report · Where: test suite + Missive read-only audit | The suite renders every body with sample data and greps the OUTPUT (not the templates — templates legitimately contain tokens pre-resolution); the Missive audit is read-only history, no new sends | Must NOT grep template source and call it a pass (tokens belong in templates; the crime is tokens in rendered output); must NOT send anything as part of the audit | — | — |
| # | You do | You should see | Element that changes copy · look · where |
What changes underneath | Must NOT happen | 🕓 Touchpoint history | 💡 UX critique + suggestion |
|---|---|---|---|---|---|---|---|
| 1 | Open the Codex operating plan document W6 delivers | One document with exactly the three parts the ruling names: (1) the prompts to give Codex, written out verbatim and ready to paste; (2) how to give Codex access to the code/copy under review, as concrete steps; (3) what to do with Codex's handoff-back doc — where it lands, who triages, how a finding becomes a fix or a recorded won't-fix | Copy: plan doc, three numbered parts · Look: a readable document, exact steps (no "figure out access") · Where: plans/email-system-overhaul-2026-07-11/ |
A new document only — no code, no sends; this is the deliverable Q21=B names | Must NOT be a vague intention doc — every step must be executable by a fresh agent with no extra context; must NOT expose live secrets in the access recipe (the repo is private with committed keys — access instructions must not widen that) | — | Critique: Second-model review fails quietly when the reviewer is handed too much — Codex given the whole repo produces shallow everything-comments instead of deep copy findings. Suggestion: The plan's prompt section should scope each pass (e.g. "review ONLY the client-facing email bodies + greeting/sign-off rules against these invariants") — one focused prompt per review target, listed as a menu. |
| 2 | Run one full Codex pass over W6's copy surfaces exactly by the plan: paste the plan's copy-review prompt, grant access per its steps, receive the handoff-back doc | A Codex handoff-back doc listing its findings on the Hungarian client copy (grammar, tone, formality consistency, greeting forms), the sign-off/leak invariants, and the template texts — each finding triaged per the plan into fix / won't-fix-with-reason, and the fixes applied show up as normal branch changes | Copy: the handoff-back doc + a triage note per finding · Look: document + resulting diffs · Where: plan folder + branches | The pass proves the plan is executable as written; any step that didn't work as documented is corrected in the plan itself (the plan is the deliverable, the pass is its test) | Must NOT count the pass as done with zero findings AND zero evidence Codex actually read the material (an empty review of 20+ Hungarian templates is a broken pass, not a clean bill); must NOT auto-apply Codex suggestions to client copy without the normal branch→staging path | — | Critique: Codex reviewing Hungarian copy is a real risk — a second model can be confidently wrong about Hungarian register (tegezés/magázás) and "fix" copy into the wrong formality. Suggestion: The copy-review prompt must state the house register explicitly (the leads are addressed formally-friendly, consistent with the existing templates) so Codex reviews against the house voice, not its own taste; formality-change suggestions are always owner-gated. |
| # | You do | You should see | Element that changes copy · look · where |
What changes underneath | Must NOT happen | 🕓 Touchpoint history | 💡 UX critique + suggestion |
|---|---|---|---|---|---|---|---|
| 1 | Open QA_TRUST_GATE.md in the overhaul's plan folder |
One page, six plain rules: ① verdicts are GREEN/AMBER/RED only (no "adapted"/"pending"); ② GREEN requires a live browser run + a vision-judge pass on real screenshots (+ probes may only veto, never manufacture a green); ③ a blank/failed screenshot = RED, hard stop — never retried-and-averaged, never downgraded to a DOM check; ④ verdicts exist only as checkpoint files — no in-memory tallies or inflatable rollups; ⑤ pre-2026-07-06 "QA green" claims are UNVERIFIED until re-checked live; ⑥ every workstream's verification section must name the actual staging URL clicked and the actual prod probe run | Copy: the six rules, no jargon a reviewer can't apply · Look: ≤1 page markdown · Where: new file plans/email-system-overhaul-2026-07-11/QA_TRUST_GATE.md |
Condensed from the hardened global QA skill's verdict rules with inline citations back to it (so the page tracks the source rather than forking it); sibling workstream docs link to it from their verification sections | Must NOT fork/paraphrase the verdict rules into a drifting second truth (cite, don't copy); must NOT exceed a page (an unread gate gates nothing) | — | Critique: A checklist nobody is forced through is decoration — the historical false-greens happened WITH good rules existing in a skill file. Suggestion: Give the gate a fill-in stub (workstream · staging URL clicked · screenshot ledger path · vision verdict · date) that must be pasted, completed, into each EBO's sign-off area — an empty stub is itself the visible failure. |
| 2 | A workstream claims a scenario GREEN with full evidence: live-tagged browser run, real screenshots, vision-judge pass, checkpoint files on disk | The claim passes the gate — anyone (including Mátyás) can follow the named staging URL + ledger path and see the same evidence; GREEN now means "proven on pixels", nothing weaker | Copy: the completed gate stub in that workstream's EBO · Look: verifiable references, not prose confidence · Where: that workstream's EBO/run report | Checkpoint files are the single verdict source; the rollup only ever reflects them | Must NOT accept a GREEN whose screenshots can't be found on disk; must NOT let a confident narrative substitute for the ledger | — | — |
| 3 | A workstream claims GREEN with prose only — "QA passed, everything works" — no live tag, no vision verdict, no checkpoint files | The claim is rejected as UNVERIFIED at the gate: the scenario is recorded AMBER/RED until real evidence exists, regardless of how confident the reporting agent sounds — and the same rejection applies to citing any pre-2026-07-06 session's "green" as proof a feature works today | Copy: "UNVERIFIED — no live evidence (QA_TRUST_GATE rule ②/④)" recorded on the claim · Look: the scenario stays un-green in the run ledger · Where: the claiming workstream's ledger/EBO | Nothing ships on the rejected claim; the workstream re-runs QA properly and only then flips the verdict | Must NOT split the difference (a "probably fine" middle verdict that unblocks shipping); must NOT let the rejected workstream re-label the same evidence-free claim and pass on the second try | — | Critique: Gate rejections can turn adversarial between agents — endless relitigating of what counts as evidence. Suggestion: The gate's answer to any dispute is mechanical: "produce the checkpoint file or the verdict stands." No judgment calls at the gate; judgment lives in the vision judge, once, upstream. |
| # | You do | You should see | Element that changes copy · look · where |
What changes underneath | Must NOT happen | 🕓 Touchpoint history | 💡 UX critique + suggestion |
|---|---|---|---|---|---|---|---|
| 1 | Probe prod is alive (curl …/health), then open the last week's real sent client emails in Missive — at least one signed-contract delivery and one payment-chase send among them — and read each against the invariants |
Every audited email ends with the exact canonical 4-line sign-off; bank/phone details appear only in the payment-instruction emails; no raw {{token}} and no broken greeting anywhere; the audit closes as a short dated note listing which emails were read and the per-invariant result |
Copy: audit note — emails read · invariants · pass/fail each · Look: a few lines in the W6 run report · Where: Missive (read-only) + prod health endpoint | Nothing changes — pure read; a discrepancy between live-sent text and what the working tree would render is itself a finding (deployed code drift), escalated rather than papered over | Must NOT send, draft, or modify anything in Missive during the audit; must NOT audit only warmup/internal mail and call it done (client-facing sends are the subject); must NOT skip the audit because the tests are green — tests and reality are checked separately, that separation is the lesson of every past false-green | The audit READS history as ground truth — the touchpoint rows for the audited sends must match the Missive-visible sent text word-for-word (the parity bar checked against production reality). | Critique: A one-time audit proves last week; the next silent regression starts the week after. Suggestion: Fold a lightweight recurring version into W0's watchdog surface — e.g. the daily poll spot-checks the newest sent client email against the sign-off + leak rules and raises the standard banner on mismatch. One more tripwire on the existing alarm channel, no new UI. |
Each build work item derived for W6, and the scenario steps that prove it works when run/clicked through.
| Work item | What it delivers | Proven by (scenario · step) |
|---|---|---|
| WI-1 · Sign-off regression test | Hermetic test walking every client-facing send site: each rendered body ends with the canonical SIGNATURE_HU block; documented teeth check proves it catches the 2026-07-11 incident footer. | W6-1·1 · W6-1·2 · W6-1·3 |
| WI-2 · Phone/bank leak guard | Same test's second half: bank/phone literals allowed only at the three named payment-instruction sites (+ contract legal block); any other hit is a hard red. | W6-2·1 · W6-2·2 |
| WI-3 · Templates migration + locked payment block | Signed-contract delivery + payment-reminder ladder bodies become owner-editable in the Templates tab (verbatim migration, send behavior untouched — W1's); the payment-details block renders as a locked, non-editable unit inside the editor. | W6-3·1 · W6-3·2 · W6-3·3 |
| WI-4 · Copy-drift detector | scripts/detect_copy_drift.py: re-runnable table of every email/SMS body tagged EDITABLE/HARDCODED with file:line; catches future drift after any workstream's changes. | W6-4·1 · W6-4·2 |
| WI-5 · Variable-leak + greeting guard | Automated check that no rendered body (template or AI path) contains a raw {{token}} or a malformed greeting; verified on the Send-Emails modal, the AI-reply draft, and sent history. | W6-5·1 · W6-5·2 · W6-5·3 |
| WI-6 · Codex operating plan + proving pass | The Q21=B deliverable: exact-steps doc (prompts / access / handoff-back handling) + one full Codex pass over W6's copy surfaces run through it, findings triaged. | W6-6·1 · W6-6·2 |
| WI-7 · QA-trust gate doc | QA_TRUST_GATE.md: the one-page six-rule gate + fill-in evidence stub; evidence-backed GREENs pass, prose-only claims are rejected as UNVERIFIED. | W6-7·1 · W6-7·2 · W6-7·3 |
| WI-8 · Live prod copy audit | Read-only audit of real recently-sent client mail in Missive against all W6 invariants + prod health probe + history↔sent parity spot-check; dated audit note. | W6-8·1 |
{{tokens}} to malformed Hungarian greetings ("Kedves ,", double closings). Keep?