Skip to content

feat(emails): verify-the-send building block — hasDeliveredEmail (DRAFT, needs design call)#732

Closed
sweetmantech wants to merge 2 commits into
mainfrom
feat/verify-email-delivered
Closed

feat(emails): verify-the-send building block — hasDeliveredEmail (DRAFT, needs design call)#732
sweetmantech wants to merge 2 commits into
mainfrom
feat/verify-email-delivered

Conversation

@sweetmantech

@sweetmantech sweetmantech commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Draft. Ships the verifiable building block + tests; the enforcement decision is open (below). Stacked on the email_send_log PR (#731) — merge that first.

What

hasDeliveredEmail(chatId) (and countDeliveredEmails) — reads email_send_log to answer "did this run actually deliver a non-empty email?" That's the check needed for the issue's verify-the-send item: a report task that finishes without delivering should be flagged, not reported as a silent success.

Why

The baseline showed ~30% of report-task runs made zero /api/emails attempts (e.g. Fat Beats weeklies: 13/15 runs sent nothing) yet the runs completed "successfully." Nothing checks delivery.

What's here (safe, tested)

  • lib/supabase/email_send_log/countDeliveredEmails.ts — counts status='sent' rows for a chat (best-effort; 0 on error).
  • lib/emails/hasDeliveredEmail.tscount > 0.
  • Unit tests (delivered / not-delivered).

Open design decision (why this is a draft)

The building block is safe; where and how to enforce is not obvious and shouldn't be guessed:

  1. How do we know a task was supposed to email? Not every headless run must. Options: a flag on the scheduled action / a marker in the run input / infer from the prompt. Needs a decision.
  2. Warn vs. fail? A hard failure risks mis-failing legitimate non-email runs. Proposal: start with a structured warning/metric (no_email_delivered) emitted after a natural finish in runAgentWorkflow when the run was email-mandated and hasDeliveredEmail(chatId) is false; escalate to a run failure only once the signal proves clean.
  3. Where to hook? runAgentWorkflow post-finish step vs. a post-run sweep over email_send_log.

I've deliberately not wired a hard guarantee into runAgentWorkflow yet — that's the call to make on review.

Depends on

Part of recoupable/chat#1829 (last item in the PR matrix). Base test.

🤖 Generated with Claude Code


Summary by cubic

Adds a durable email_send_log and logs every /api/emails attempt, plus hasDeliveredEmail(chatId) to verify that a run actually delivered an email. This supports the verify-the-send requirement in recoupable/chat#1829; enforcement wiring will follow.

  • New Features

    • Reads/writes public.email_send_log (types added) with a typed insert helper.
    • sendEmailHandler records all outcomes (sent, send_failed, rejected) via logEmailAttempt, capturing the raw body (via request.clone()), parse flag, truncated raw_body (10k), lengths, and resend_id. Best-effort; never throws.
    • countDeliveredEmails(chatId) and hasDeliveredEmail(chatId) to answer “did this run deliver at least one email?”
    • Unit tests for logging, handler integration, and delivery check.
  • Migration

    • The email_send_log table is migrated in the recoupable/database repo (mono/database). Ensure that migration is deployed there before relying on this.

Written for commit 5ac5c57. Summary will update on new commits.

Review in cubic

@vercel

vercel Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
api Ready Ready Preview Jun 30, 2026 8:35pm

Request Review

@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 87dfe259-c9b6-43c0-a151-9037c4b4def9

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/verify-email-delivered

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

sweetmantech and others added 2 commits June 30, 2026 15:29
…-day observability)

When an empty/footer-only email went out we could not recover what the API
received: safeParseJson discards malformed bodies, the route logged nothing,
and the sandbox that built the request is ephemeral. This records every
attempt — sent, send_failed, and rejected (empty/malformed) — so a send is
debuggable days later.

- lib/supabase/email_send_log/insertEmailSendLog.ts: typed insert.
- lib/emails/logEmailAttempt.ts: builds the row (computes body_parsed, truncates
  raw_body to 10k); best-effort — never throws, so logging can't break a send.
- sendEmailHandler: capture the raw body via request.clone() and log on every
  outcome (rejected attempts keep the raw body, so a malformed send is visible).
- types/database.types.ts: email_send_log added so the typed insert compiles.

The migration lives in the recoupable/database repo (mono/database) — applied
via its CI — not here.

Tests: logEmailAttempt unit (parsed/malformed/truncation/swallow-errors) +
handler asserts the sent/rejected/send_failed log calls. Full lib/emails: 150.

Ref: recoupable/chat#1829

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…d guarantee

Reads email_send_log to answer "did this run actually deliver a non-empty
email?" — the check a report task needs so it can fail/flag instead of
reporting a silent success when the agent never sent (the observed 30%
no-send case). Best-effort (returns false on error). Stacked on the
email_send_log PR.

This PR intentionally ships ONLY the verifiable building block + tests; the
enforcement point (where/whether to fail vs warn, and how to detect that a
task was "supposed to email") is the open design question — see PR body.

Ref: recoupable/chat#1829

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@sweetmantech sweetmantech force-pushed the feat/verify-email-delivered branch from bf4fafa to 5ac5c57 Compare June 30, 2026 20:34
@sweetmantech sweetmantech changed the base branch from test to main June 30, 2026 20:34
@sweetmantech

Copy link
Copy Markdown
Contributor Author

Closing — superseded + deferred.

The observability half of this draft (logEmailAttempt / sendEmailHandler / insertEmailSendLog / types + tests) was built on the old 13-column email_send_log schema (body_parsed, to_count, html_length, text_length, 10k truncation, error). We deliberately simplified that to 7 columns, full raw body, no truncation in database#37/#38 + api#731 (merged). Rebasing this branch would reintroduce the dropped columns and the inserts would fail — a regression of #731.

The only net-new content is the verify-the-send building block (hasDeliveredEmail + countDeliveredEmails), and it's dead code until we decide the enforcement (what to do when a run delivers no email). Per the tracker decision (2026-07-01), we're deferring verify-the-send to focus on making the emails that do go out correct. When we pick it back up we'll open a fresh, tightly-scoped PR against current main for just the building block + wiring — not this stale branch.

Tracked under recoupable/chat#1829 (verify-the-send deferred).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant