The local-fill EOI pathway creates fresh Documenso envelopes via
createDocument, which (unlike the template pathway that inherits
template 8's all-false emailSettings) used Documenso's defaults — every
email event defaults to true on both the v1 and v2.13 APIs. So Documenso
fired its OWN unbranded "Waiting for others to complete signing." and
"Signing Complete!" emails (signed PDF attached, reply-to sales@),
bypassing EMAIL_REDIRECT_TO and duplicating the CRM's branded sends.
Force emailSettings to all-false (DOCUMENSO_SILENT_EMAIL_SETTINGS) on
every createDocument call (v1 JSON + v2 multipart). The CRM stays the
sole sender of signing comms. Verified against the live v2.13 OpenAPI +
template 8's stored meta.
Also stop the EMAIL_REDIRECT_TO gate from appending "(was: <email>)" to
the recipient NAME: a "Name" field auto-fills from it into the signed
PDF, so the annotation overlapped the signature. Redirect the email
only; the original is still logged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bundles the prior autonomous-session output that was sitting unstaged:
- Em-dash sweep across src/ + tests/ (en-dash/em-dash to hyphen, ~2280 instances)
- country-flag-icons rollout (CountryFlag component, replaces emoji glyphs that
never rendered on Windows; lazy-loads the 3x2 SVG index as a single chunk
after the per-subpath dynamic-import approach silently failed in webpack)
- Admin IA Phase 1+2: 7-domain regroup, 41 to 38 pages, /admin/berths index,
redirects (ocr to ai, reports to dashboard, invitations to users),
docs/admin-ia-proposal.md
- Per-template email tester (registry + endpoint + UI on Email admin page)
- Cancel-document mode picker (delete-from-Documenso vs keep-for-audit)
- Dashboard PDF report: 25 widgets, SVG charts, date-range picker, 11 resolvers
- Customize-widgets per-region sortables at xl+ (charts/rails/feed); single
flat sortable below xl when the layout stacks; per-viewport saved orders
- Audit doc updates capturing each shipped item
- Lint fixes: react-compiler immutability in DonutChart (reduce instead of
let-reassign), set-state-in-effect disables in CountryFlag and
UploadForSigning preview-bytes effect, unused 'confirm' destructures in
interest contract + reservation tabs, unescaped apostrophe in test-template
card copy
A pre-import audit caught three places where outbound comms could escape
even with EMAIL_REDIRECT_TO set. Plugged each, added unit tests so the
behavior can't silently regress, and shipped a live smoke script the
operator can run before any production data import.
Leak 1: email-compose.service.ts (per-account user composer)
Built its own nodemailer transporter and called sendMail() directly,
bypassing the centralized sendEmail()'s redirect. Now mirrors the same
redirect: when EMAIL_REDIRECT_TO is set, "to" is rewritten, "cc" is
dropped, and the subject is prefixed with "[redirected from <orig>]".
Leak 2: documenso-client.sendDocument()
Tells Documenso to actually email the document. Recipient emails were
rerouted at create-time (in pass-3) but a document created BEFORE the
redirect was turned on could still trigger a real-client email. Now
short-circuited when the redirect is set — returns the existing doc
shape so downstream code doesn't see an unexpected null.
Leak 3: documenso-client.sendReminder()
Same shape as sendDocument: emails a stored recipient address that may
predate the redirect. Now short-circuits with a warn-level log.
Tests (tests/unit/comms-safety.test.ts):
- createDocument rewrites recipients
- generateDocumentFromTemplate rewrites both v1.13 formValues.*Email
keys AND v2.x recipients[] arrays
- sendDocument is short-circuited (no /send call)
- sendReminder is short-circuited (no /remind call)
- createDocument passes through unchanged when redirect unset
- sendEmail rewrites to + subject for single recipient
- sendEmail handles array of recipients (joined into subject prefix)
- sendEmail passes through unchanged when redirect unset
- Webhook worker reads process.env.EMAIL_REDIRECT_TO at dispatch time
(no module-level caching that could miss a runtime flip)
Live smoke (scripts/smoke-test-redirect.ts):
Monkey-patches nodemailer.createTransport, calls the real sendEmail()
with a fake real-client address, verifies the captured outbound has
the right "to" + subject. Run: `pnpm tsx scripts/smoke-test-redirect.ts`.
Exits non-zero if the redirect failed for any reason — drop-in for a
pre-deploy check.
Verification:
pnpm exec tsc --noEmit — 0 errors
pnpm exec vitest run — 936/936 (was 926, +10 new safety tests)
pnpm tsx scripts/smoke-test-redirect.ts — PASS
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>