Repository navigation
Conversation
This was referenced Oct 27, 2026
The tier sweep subtracts email_sandbox_sent from email_sent per day, and the sending allowance subtracts it per workflow, both floored at zero. No code records email_sandbox_sent yet, so the tier does not change until the worker metric lands in its own PR. Refs Silthus/personal#199 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…subtraction The allowance case now gives one workflow more sandbox sends than sends, so a team-wide subtraction would erase the other workflow's own sends. Refs Silthus/personal#199 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Silthus
force-pushed
the
workflows-sandbox-delivery/tier-exclusion
branch
from
October 28, 2026 10:00
f7e9df3 to
3425dd7
Compare
Silthus
force-pushed
the
workflows-sandbox-delivery/tenant-pause-gate
branch
from
October 28, 2026 10:00
0e5e737 to
35ce760
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
A team's sending tier grows with the email it sends through its own domain.
Sandbox sends go out through a shared PostHog identity, but they record
email_sent, which the tier history and the team's sending allowance read.So sandbox sends could lift a team's own tier, or use up its allowance.
This PR makes the tier and the allowance subtract sandbox sends.
Part of Silthus/personal#199 (sandbox delivery). Stacked on #608.
Replaces the Python half of #606, which mixed Python with Node. Originally #454.
Stack
workflows-sandbox-delivery/tenant-pause-gate(feat(workflows): skip sandbox sends while the shared ses tenant is paused #608). Lands after feat(workflows): skip sandbox sends while the shared ses tenant is paused #608 and every PR below it.07b56bedb66: tier and reputation suites 92 passed, ruff clean. Pre-pushhogli ci:preflight --strictpassed. Review: GPT-6.1 Sol (high) read the conflict resolutions of the master merge, no findings.07b56bedb66:1bb35bd788e: a real Django server,cdp-apiand email worker, with SES, the SNS certificate host, MX and personhog stubbed. The tier history left the sandbox sends out (sent=0) and still counted their bounces and complaints.cigap (exit 3). Fork CI cannot run Semgrep or the Depot jobs, and the lane check counts the layers below, because it diffs againstmaster.Impact
Grow Workflows, activation rate: the tier keeps measuring a team's own sending once sandbox sends exist.
No production team sees this yet:
WORKFLOWS_SANDBOX_SENDER_ENABLEDstays off.Merge Danger
Warning
🟡 Merge risk: medium · two-way door (merge-risk rubric)
email_sandbox_sentfromemail_sentper day, and the allowance per workflow, floored at zero.email_sandbox_sentuntil the worker metric (feat(workflows): record sandbox deliveries as their own metric #610) deploys, so the subtraction is zero and tier results do not change.Production lines: 17 changed (
git diff --numstatagainst #608, tests and docs excluded).Changes
_history_from_daily)email_sentemail_sentminusemail_sandbox_sentper day, floored at zeroemail_sentHow did you test this code?
Live run on the slice tip
Live run of the V3 tip
7e95863f7a0on a PostHog preview. Stubs: a local SES endpoint that records each call, andexample.comseeded as a valid MX domain; a local SNS signing certificate, so a signed delivery webhook verifies; the trigger event produced straight onto the CDP events topic; personhog pointed at the persons database.SES tenant
DISABLED, then the Django sync: the mirror row readsDISABLED, and the next sandbox send is skipped with "the sandbox sender is paused right now".ENABLEDand a sync: the next send goes out. The worker picked up each change without a restart.A live workflow send on the sandbox sender recorded
email_sentandemail_sandbox_sentonce each, the pair the sending tier subtracts.A signed SES
Deliverynotification for that message on the sandbox configuration set returned 200 and capturedworkflows sandbox email delivered. The same envelope with a forged signature returned 403.Lane gate on HEAD: the pre-push
hogli ci:preflight --strictpasses. The lane classifier puts this PR on the Python side only.Red: on feat(workflows): skip sandbox sends while the shared ses tenant is paused #608's code, 4 tier history cases in
test_email_sending_tier.pyand 1 allowance case intest_email_reputation.pyfail.Review fix: the allowance case fails on a mutant that subtracts sandbox sends team-wide.
Green on HEAD:
test_email_sending_tier.py,test_email_sending_tier_notifications.py,test_email_reputation.pyandtest_workflow_email_health.py, 128 passed.Test seams: the tier sweep over seeded daily metrics, and the email reputation API's allowance.
Proofs owed (
fleet proof gate):f7e9df3170c(the allowance case now pins the per-workflow floor); final read: no findings.69afe3e4b1f), recorded on every delivery head. One real email worker process (cdp-cyclotron-worker-email) on scratch Postgres, with real Redis, Kafka and ClickHouse: a member received the email, a non-member was skipped, a paused tenant was skipped, the daily team cap held, and each delivered send recordedemail_sandbox_sent, which the tier's own ClickHouse read then left out. Stubbed: the SES endpoint (a local HTTP stub behind the real SESv2 client), the MX verdict for the reservedexample.comdomain, and the tenant-state rows the Django sync would write.master, so it reads as mixed until the Node layers below land. Land the stack bottom-first, not with onetrunk mergeon the top layer.Silthus/posthog.Test rationale: the regressions that matter are sandbox sends lifting a team over the use bar, one day's sandbox excess erasing another day's own sends, and sandbox bounces disappearing from the rates.
Release status
Docs update
docs/internal/workflows-email-sending-tiers.mddocuments the tier subtraction.🤖 Agent context
Autonomy: Human-driven (agent-assisted)
Agent: Claude Code, Claude Opus 5.5 (implementation). Review by Codex, GPT-6.1 Sol.
Split out of #605 and #606 so each PR passes upstream's merge rules: a migration does not share a PR with
nodejs/, and Python does not share a PR with Node. The code is carved by file from those reviewed heads without changes; the stack's tip has the same tree as #606.Skills:
tdd,/writing-tests,pr-description,merge-risk.Public artifact: all test data is invented.
Decisions
master, so each layer passes once the layers below it landemail_sentfor the workflow metrics tab, addemail_sandbox_sent, and subtract it in the tier per day and in the allowance per workflow, floored at zeroInvolvement: no grilling rounds; design pass carried from #605 and #606 (the code is unchanged); review (GPT-6.1 Sol, 1 round and a final read, 1 finding fixed); proof captured as test output and a live run; master merge on 2026-10-31: review of its conflict resolutions (GPT-6.1 Sol, no findings) and a fresh live run at the slice tip.
🤖 Generated with Claude Code