Skip to content

feat(messaging): capture email first code send once per project - #127187

Merged
trunk-io[bot] merged 29 commits into
email-v2/10-mcp-strict-email-bodiesfrom
email-v2/11-first-code-send-event
Nov 4, 2026
Merged

trunk-io[bot] merged 29 commits into
email-v2/10-mcp-strict-email-bodiesfrom
email-v2/11-first-code-send-event

Conversation

@Silthus

@Silthus Silthus commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

Moved from Silthus#583.

Re-cut on 2026-11-02 onto #127167, after the scope cut parked local templates and sends by key. This layer keeps only what still applies to a code send.

Part of the code-first rebuild of the email send stack; new in the rebuild, with no closed PR to replace: the event the launch drip campaign exits on.

Problem

  • The launch drip campaign for sending email from code has no signal that a project has started sending. A developer who already sends keeps getting the emails that tell them how to start.
  • The drip needs one event per project, on the person who set up the sending, and never twice.

Impact

Pays into Grow Workflows: the drip stops for projects that already send from code, so it talks only to those still getting started. In scope: Messaging, send/email/ sends with a project secret API key.

Merge Danger

Warning

🟡 Merge risk: medium · one-way door: the migration creates messaging_firstcodesend, and a captured event stays captured

  • New table messaging_firstcodesend: one row per project, with a unique constraint on the team. The migration is a plain CREATE TABLE. Its team key has no database constraint, so it takes no lock on posthog_team.
  • On the send path, outside delivery, behind workflows-email-from-code: the work runs in a commit callback after the send is queued, so it never changes the answer or the mail. After a project's first send, each send with a key that has a creator adds one root-team lookup and one exists() query. The insert runs once per project.
  • A capture that raises or that the client drops releases the claim, and the send still answers 202.

Changes

Kind Files Lines
Production 8 +116 −4
Tests 1 +219 −5
  • The first new send on send/email/ with a project secret API key captures email first code send for the person who created that key, with the organization and project as groups. Its properties are team_id and addressed_by: template_key for a code send, template_id for a send by id.
  • It captures at most once per project. The messaging_firstcodesend row decides it, through its unique constraint, not through a read and then a write. Retries, replays and parallel sends never capture a second time. A send from a project's other environments counts for the project.
  • A replay, a refused send, a personal API key, workflows-email-from-code off, or master's messaging_templates/{id}/send/ route captures nothing and claims nothing.
  • A capture that fails releases the claim, so the next send captures. That covers a capture that raises and one the analytics client drops and answers with None. The send still answers 202.
  • A key with no creator captures nothing and claims nothing. That includes a creator deleted between the send and its commit callback. The next send with a key that has a creator still captures, so the event always lands on a person.
  • One facade operation, report_first_code_send(team_id, capture), claims the row, captures, and releases the claim when the capture fails. The view only calls it after commit.
  • Known limit: posthoganalytics queues the event in memory, and an id back from capture() is its only acknowledgement. An event the client loses after that keeps the claim, and so does a process that dies between the claim and the capture. Then the project never gets the event. A durable outbox is outside this change.
  • After the first send, a lookup skips the insert. The insert and its constraint still settle a race.

How did you test this code?

Test seams: send/email/ through the DRF client (test_transactional_email.py), and report_first_code_send for the once-only rule.

Test rationale, each run red first:

  • The first send of a project that captures nothing, or captures for someone other than the key's creator.
  • A replay, a key with no creator, a personal API key, a refused send or the flag off that captures.
  • A creator deleted before the commit callback whose send claims the project's row, so a later send never captures.
  • A failed capture that fails the send, or that keeps the claim, so a later send never captures. Two rows: capture raises, and capture returns None.
  • A send on master's template route, or with only workflows-transactional-email-api on, that claims the event.
  • A second insert for the same project that succeeds, or one that raises instead of answering "already recorded".
  • A second environment of the same project that records again, or a send with an environment's key that groups the event under the environment instead of its project.
  • A later send that still runs the insert.

Proofs at 00f9015 (saved with fleet proof), after the merge of #127183 at b9cb59d, which carries #127181, #127167's fcf42b0, #127123's c81b9e0 and master:

  • Live run: Django at this head, cdp-api at feat(cdp): key a code send on the sender the caller chose #127129's head e88ee89, analytics captured locally. Every answer, mail and event matches the run at the previous head 71e6e79.
    • Migration 0016_first_code_send applies on top of 0015_transactional_email_key and master's 0012_email_hard_bounce, with nothing left unapplied.
    • A first send with a key that has no creator answers 202 and captures nothing.
    • The first send with a key that has a creator captures one email first code send on that creator, grouped under the project. A second send and a replay still leave one event. A send from an environment's key groups the event under the parent project.
  • Prod shape, read on 2026-11-04 and reused from 71e6e79: in both regions, every project secret API key that can send has a creator, so the event always has a person to land on. Counts stay in the private ledger.
  • Lane gate: the migration checks, repo-wide mypy and the messaging tests pass on the same Python at 258aef7. A fresh review of the changes since 71e6e79 (the base merges and the migration rename) found nothing.
  • CI report: the migration SQL is one CREATE TABLE with a unique constraint on team_id. Django also adds a plain team_id index, which repeats the unique index; on a table with one row per project it costs nothing, so the migration stays as proven. The Python and TypeScript duplication warnings name files outside this stack (warehouse sources and others) and come from master. Of the backend coverage gaps, only test_transactional_email.py lines 63 to 66 are this layer's: the field annotations of the test-only CapturedFirstCodeSend dataclass. Every other uncovered line sits outside this layer's hunks.
Earlier live run, 2026-10-31
  • Live run, 2026-10-31. Django at the top of the stack, which contains this head, with a project secret API key that has a creator. Analytics captured to a local file.

    Send email first code send events
    First code send in the project 1, with addressed_by: template_key and the project, organization and instance groups
    Second code send, new key still 1
    Retry of the first send, same key still 1, and replayed: true
  • Prod shape: a gap until read-only production access exists on 2026-11-02.

Release status

  • This change is behind a feature flag and is not available to users

Docs update

None.

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

Agent: Claude Code, Claude Opus 5.5 (claude-opus-5-5)

Question Answer Who Where
Which event ends the launch drip? email first code send, once per project, on the key's creator, with the org as a group 👤 Human map Silthus#461, 2026-10-25
How is "once" enforced? A unique constraint, not a read and then a write 👤 Human map Silthus#461, 2026-10-25
Does a send with a key that has no creator use up the project's event? No: it captures nothing and claims nothing, so a later send with a creator still captures 🤖 Agent pending approval
What if the analytics client loses a queued event? The claim stays; no durable outbox in this PR, a follow-up if missing exits show up 🤖 Agent QA swarm 2026-11-01
Where does the once-per-project row live? A messaging table with one row per project, not Team (a hot table) or a workflows config row 🤖 Agent pending approval

Involvement: one brief from Michael; review round 1 (GPT-6.1 Sol, Opus fresh eyes) fixed; dj3 round 1 (GPT-6.1 Sol) fixed, its final read found a dropped event that kept the claim, fixed; QA swarm on 2026-11-01 with two panel rounds (GPT-6.1 Sol review, Opus 5.5 fresh eyes): 3 findings fixed, 2 rejected; live run and shadow diff captured.

🤖 Generated with Claude Code

Silthus and others added 12 commits October 31, 2026 15:54
The analytics client returns None instead of raising when it drops an
event, so the claim now also goes back when capture queues nothing.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
…peration

The service claims the project, runs the capture, and releases the claim when
the capture does not queue, so the view no longer drives the claim protocol.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@github-actions

github-actions Bot commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

🤖 CI report

⚠️ Trunk lane — backend Python lane

This PR is assigned to the backend Python lane. It runs backend Python tests and may merge in parallel with PRs in other lanes.

⚠️ Duplication (Python) — 2 new duplicated blocks (worst 90 tokens)

New Python code duplication introduced by this branch. Fails at 70+ tokens in app code, or 150+ tokens when both copies live in test files. Advisory while the gate proves itself: extract a shared helper instead of copying.

First copy Second copy Lines Tokens
products/warehouse_sources/sources/folk/folk.py:144 products/warehouse_sources/sources/select_star/select_star.py:156 18 90
products/warehouse_sources/sources/ruddr/ruddr.py:127 products/warehouse_sources/sources/tempo/tempo.py:136 20 72
⚠️ Duplication (TypeScript) — 11 new duplicated blocks (worst 195 tokens)

New TypeScript code duplication introduced by this branch. Fails at 70+ tokens in app code, or 150+ tokens when both copies live in test files. Advisory while the gate proves itself: extract a shared helper instead of copying.

First copy Second copy Lines Tokens
packages/agent/packages/agent/src/adapters/claude/claude-agent.slash-command.test.ts:49 packages/agent/packages/agent/src/adapters/claude/claude-agent.task-notification.test.ts:53 38 195
products/replay_vision/frontend/components/QuillScanList.tsx:31 products/replay_vision/frontend/components/ScanList.tsx:30 34 191
products/replay_vision/frontend/components/ChapterFilmstrip.tsx:26 products/replay_vision/frontend/components/QuillChapterFilmstrip.tsx:27 31 174
products/replay_vision/frontend/components/QuillScanList.tsx:203 products/replay_vision/frontend/components/ScanList.tsx:179 15 172
frontend/src/scenes/session-recordings/player/sidebar/PlayerSidebarOverviewDeviceTab.tsx:11 frontend/src/scenes/session-recordings/player/watch/QuillOverviewDeviceCard.tsx:11 14 144
products/replay_vision/frontend/components/QuillScanList.tsx:87 products/replay_vision/frontend/components/ScanList.tsx:81 27 138
frontend/src/scenes/session-recordings/player/sidebar/PlayerSidebarOverviewSessionTab.tsx:14 frontend/src/scenes/session-recordings/player/watch/QuillOverviewSessionCard.tsx:14 12 102
products/replay_vision/frontend/components/QuillScanList.tsx:257 products/replay_vision/frontend/components/ScanList.tsx:231 24 101
products/replay_vision/frontend/components/ObservationProgressBar.tsx:12 products/replay_vision/frontend/components/QuillObservationProgressBar.tsx:10 19 87
frontend/src/scenes/session-recordings/player/sidebar/PlayerSidebarOverviewPersonTab.tsx:15 frontend/src/scenes/session-recordings/player/watch/QuillOverviewPersonCard.tsx:16 14 84
products/replay_vision/frontend/components/ObservationProgressBar.tsx:45 products/replay_vision/frontend/components/QuillObservationProgressBar.tsx:43 13 70
ℹ️ MCP UI apps size — 35 app(s), 29126.5 KB JS

Built size of each MCP UI app (main.js + styles.css).

App JS CSS
debug 844.4 KB 211.6 KB
explain-chart 1119.1 KB 211.6 KB
action 767.8 KB 211.6 KB
action-list 835.4 KB 211.6 KB
cohort 766.8 KB 211.6 KB
cohort-list 834.4 KB 211.6 KB
email-template 766.6 KB 211.6 KB
error-details 771.7 KB 211.6 KB
error-issue 769.0 KB 211.6 KB
error-issue-list 836.8 KB 211.6 KB
experiment 832.5 KB 211.6 KB
experiment-list 836.1 KB 211.6 KB
experiment-results 834.3 KB 211.6 KB
feature-flag 834.8 KB 211.6 KB
feature-flag-list 838.5 KB 211.6 KB
feature-flag-testing 770.0 KB 211.6 KB
inline-scan 767.2 KB 211.6 KB
insight-actors 833.8 KB 211.6 KB
llm-costs 831.1 KB 211.6 KB
session-recording 769.0 KB 211.6 KB
survey 768.4 KB 211.6 KB
survey-global-stats 831.2 KB 211.6 KB
survey-list 836.1 KB 211.6 KB
survey-stats 831.2 KB 211.6 KB
trace-span 767.1 KB 211.6 KB
trace-span-list 835.3 KB 211.6 KB
vision-observation-list 834.5 KB 211.6 KB
workflow 767.1 KB 211.6 KB
workflow-list 834.7 KB 211.6 KB
workflow-setup-status 766.8 KB 211.6 KB
query-results 1090.8 KB 211.6 KB
render-ui 1195.0 KB 211.6 KB
visual-review-snapshots 771.6 KB 211.6 KB
workflow-data-suggestions 769.4 KB 211.6 KB
workflow-sending-rules 768.1 KB 211.6 KB
⚠️ Backend coverage — 97.0% of changed backend lines covered — 4 uncovered

🧪 Backend test coverage

Patch coverage — changed backend lines (products + core): ███████████████████░ 97.0% (168 / 172)

File Patch Uncovered changed lines
products/messaging/backend/tests/api/test_transactional_email.py 96.5% 63–66

🤖 Agents: add a test only if an uncovered line exposes a realistic regression that existing tests miss. Otherwise explain why no new test is needed under "How did you test this code?". Gap list: the patch-coverage artifact on this run (gh run download 382526494170323 -n patch-coverage), or the coverage-data block at the end of this comment.

Per-product line coverage (touched products)
Product Coverage Lines
demo ██████████████░░░░░░ 71.2% 1,979 / 2,778
batch_exports ████████████████░░░░ 82.2% 23,523 / 28,601
engineering_analytics █████████████████░░░ 84.9% 11,753 / 13,841
commands █████████████████░░░ 85.4% 1,621 / 1,898
mcp_analytics █████████████████░░░ 86.3% 5,812 / 6,733
posthog_ai ██████████████████░░ 87.9% 4,304 / 4,896
notebooks ██████████████████░░ 88.8% 15,454 / 17,407
mcp_registry ██████████████████░░ 89.0% 1,596 / 1,794
dashboards ██████████████████░░ 89.4% 8,048 / 9,003
product_tours ██████████████████░░ 89.7% 1,355 / 1,510
warehouse_suggestions ██████████████████░░ 90.1% 3,836 / 4,257
cohorts ██████████████████░░ 90.3% 9,203 / 10,195
signals ██████████████████░░ 90.7% 102,114 / 112,573
today ██████████████████░░ 91.2% 3,949 / 4,330
canvas ██████████████████░░ 91.2% 8,199 / 8,988
tasks ██████████████████░░ 91.3% 83,321 / 91,241
managed_warehouse ██████████████████░░ 91.4% 12,928 / 14,151
data_warehouse ██████████████████░░ 91.4% 15,913 / 17,416
data_modeling ██████████████████░░ 91.5% 11,714 / 12,806
autoresearch ██████████████████░░ 91.5% 11,717 / 12,805
cdp ██████████████████░░ 91.5% 10,433 / 11,399
metrics ██████████████████░░ 91.8% 11,380 / 12,399
streamlit_apps ██████████████████░░ 91.9% 3,139 / 3,415
business_knowledge ██████████████████░░ 92.0% 10,556 / 11,476
wizard ██████████████████░░ 92.2% 7,002 / 7,594
review_hog ██████████████████░░ 92.3% 17,768 / 19,256
conversations ██████████████████░░ 92.3% 31,368 / 33,969
error_tracking ███████████████████░ 92.5% 20,375 / 22,018
early_access_features ███████████████████░ 92.6% 1,339 / 1,446
alerts_platform ███████████████████░ 92.6% 8,643 / 9,329
slack_app ███████████████████░ 92.7% 22,444 / 24,215
notifications ███████████████████░ 92.8% 1,218 / 1,312
stamphog ███████████████████░ 93.0% 10,414 / 11,193
alerts ███████████████████░ 93.0% 11,701 / 12,575
surveys ███████████████████░ 93.3% 7,256 / 7,780
web_analytics ███████████████████░ 93.4% 26,472 / 28,347
approvals ███████████████████░ 93.7% 5,004 / 5,341
marketing_analytics ███████████████████░ 93.8% 22,066 / 23,535
workflows ███████████████████░ 93.8% 27,870 / 29,712
context_layer ███████████████████░ 93.8% 3,984 / 4,247
exports ███████████████████░ 93.9% 4,324 / 4,607
cross_project_dashboards ███████████████████░ 93.9% 858 / 914
mcp_store ███████████████████░ 93.9% 9,193 / 9,789
ai_observability ███████████████████░ 94.0% 33,124 / 35,252
authorization ███████████████████░ 94.0% 671 / 714
billing_alerts ███████████████████░ 94.0% 2,322 / 2,470
customer_analytics ███████████████████░ 94.1% 30,760 / 32,679
messaging ███████████████████░ 94.2% 7,825 / 8,308
replay_vision ███████████████████░ 94.4% 33,353 / 35,345
logs ███████████████████░ 94.5% 19,437 / 20,572
reminders ███████████████████░ 94.8% 782 / 825
experiments ███████████████████░ 94.8% 45,081 / 47,546
warehouse_sources ███████████████████░ 94.8% 135,154 / 142,521
actions ███████████████████░ 94.9% 997 / 1,051
growth ███████████████████░ 95.0% 11,563 / 12,176
endpoints ███████████████████░ 95.0% 9,665 / 10,177
annotations ███████████████████░ 95.1% 1,097 / 1,153
skills ███████████████████░ 95.2% 7,633 / 8,018
access_control ███████████████████░ 95.4% 8,461 / 8,871
data_catalog ███████████████████░ 95.7% 4,714 / 4,928
subscriptions ███████████████████░ 95.7% 14,017 / 14,651
product_analytics ███████████████████░ 95.8% 30,525 / 31,863
feature_flags ███████████████████░ 95.8% 29,678 / 30,973
revenue_analytics ███████████████████░ 95.9% 1,877 / 1,958
data_quality ███████████████████░ 96.0% 8,561 / 8,915
tracing ███████████████████░ 96.5% 4,572 / 4,737
account_audits ███████████████████░ 96.7% 770 / 796
pulse ███████████████████░ 97.3% 2,036 / 2,093
analytics_platform ████████████████████ 98.0% 2,883 / 2,943
groups ████████████████████ 98.2% 1,604 / 1,634

Report-only. Patch coverage = changed backend lines covered vs origin/master. Sorted lowest first.
Known gaps: lines covered only by Temporal tests show as uncovered; core line numbers may drift if master changed the same file.

⚠️ Django migration SQL — 1 new migration to review

We've detected new migrations on this PR. Review the SQL output for each migration:

products/messaging/backend/migrations/0016_first_code_send.py

/home/runner/work/tmp/tool_cache/Python/3.14.7/x64/lib/python3.14/site-packages/anyio/from_thread.py:119: SyntaxWarning: 'return' in a 'finally' block
  return result
2026-11-04T19:03:08.737060Z [error    ] Path must be a valid database or directory containing databases. [posthog.exceptions_capture] pid=5778 tid=139890638388096
Traceback (most recent call last):
  File "/home/runner/work/posthog/posthog/posthog/geoip.py", line 15, in <module>
    geoip: Optional[GeoIP2] = GeoIP2(cache=8)
                              ~~~~~~^^^^^^^^^
  File "/home/runner/work/tmp/tool_cache/Python/3.14.7/x64/lib/python3.14/site-packages/django/contrib/gis/geoip2.py", line 114, in __init__
    raise GeoIP2Exception(
        "Path must be a valid database or directory containing databases."
    )
django.contrib.gis.geoip2.GeoIP2Exception: Path must be a valid database or directory containing databases.
/home/runner/work/tmp/tool_cache/Python/3.14.7/x64/lib/python3.14/site-packages/sshtunnel.py:1040: SyntaxWarning: 'return' in a 'finally' block
  return (ssh_host,
System check identified some issues:

WARNINGS:
?: (axes.W001) You are using the django-axes cache handler for login attempt tracking. Your cache configuration is however invalid and will not work correctly with django-axes. This can leave security holes in your login systems as attempts are not tracked correctly. Reconfigure settings.AXES_CACHE and settings.CACHES per django-axes configuration documentation.
?: (staticfiles.W004) The directory '/home/runner/work/posthog/posthog/frontend/dist' in the STATICFILES_DIRS setting does not exist.
BEGIN;
--
-- Create model FirstCodeSend
--
CREATE TABLE "messaging_firstcodesend" ("id" uuid NOT NULL PRIMARY KEY, "created_at" timestamp with time zone NOT NULL, "team_id" integer NOT NULL, CONSTRAINT "unique_first_code_send_per_team" UNIQUE ("team_id"));
CREATE INDEX "messaging_firstcodesend_team_id_4e029583" ON "messaging_firstcodesend" ("team_id");
COMMIT;

Last updated: 2026-11-04 19:03 UTC (e876d99)

✅ Django migration risk — migration analysis complete

We've analyzed your migrations for potential risks.

Summary: 1 Safe | 0 Needs Review | 0 Blocked

✅ Safe

Brief or no lock, backwards compatible

messaging.0016_first_code_send
  └─ #1 ✅ CreateModel
     Creating new table is safe
     model: FirstCodeSend
  │
  └──> ℹ️  INFO:
       ℹ️  Skipped operations on newly created tables (empty tables
       don't cause lock contention).

Last updated: 2026-11-04 19:04 UTC (e876d99)

Silthus and others added 3 commits November 2, 2026 10:35
The first send on send/email/ through a project secret API key that a
person made captures `email first code send` for that person. A project
claims the event once. The claim goes back when capture raises or drops
the event, so a later send reports it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
…first-code-send-event

The scope cut parks local templates. This merge takes the tree of this
layer's own commits replayed onto #127167, so the branch
drops the parked layers that sat below it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@Silthus
Silthus removed this pull request from stack #127192 November 2, 2026 20:06
@Silthus
Silthus added this pull request to stack #127772 November 2, 2026 20:08
@Silthus
Silthus removed this pull request from stack #127772 November 2, 2026 20:08
@Silthus
Silthus added this pull request to stack #127774 November 2, 2026 20:08
Silthus and others added 4 commits November 2, 2026 21:00
A key made in an environment captured the first code send under the
environment, while the claim it takes belongs to the project. The event
now carries the project's id and group.

A code send already checked the code-send flag before it went out, so
only a send by template id checks it again. The key's creator check now
runs before the flag check. The claim uses get_or_create, and the
creator comes from one query.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@Silthus
Silthus removed this pull request from stack #127774 November 3, 2026 08:22
@Silthus
Silthus added this pull request to stack #127977 November 3, 2026 08:22
Silthus and others added 4 commits November 4, 2026 08:40
…d helpers

The environment test passes its team by keyword to the key helper.
The first code send tests send with the current send scope, so the
legacy scope fallback captures nothing and the capture mock fails only
the first code send event.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@Silthus

Silthus commented Nov 4, 2026

Copy link
Copy Markdown
Contributor Author

@PostHog review

@posthog

posthog Bot commented Nov 4, 2026

Copy link
Copy Markdown
Contributor

@Silthus PostHog Review is reviewing this pull request.

@posthog posthog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PostHog Review · Standard · reviewed 71e6e79: no findings.

@Silthus Silthus added the stamphog Request AI approval (no full review) label Nov 4, 2026
stamphog[bot]

This comment was marked as outdated.

Silthus and others added 4 commits November 4, 2026 13:09
…irst-code-send-event

Brings in #127123's master merge, which adds messaging migration 0012_email_hard_bounce and renumbers the code-send key migration to 0015_transactional_email_key. The first code send migration becomes 0016_first_code_send on top of it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
…irst-code-send-event

Brings 03's fcf42b0 through layers 08 and 10. The first code send migration
stays 0016_first_code_send on top of 0015_transactional_email_key.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@stamphog
stamphog Bot dismissed their stale review November 4, 2026 14:47

A new stamphog review started for this PR — the fresh verdict replaces this approval.

@stamphog stamphog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not approved yet — waiting on the conditions below.

The Migration risk check has not finished for this commit, so stamphog cannot tell a safe migration from a risky one yet. The review runs again on the next push, or you can re-request it once the check reports.

Gate mechanics and policy version
Gate Result
prerequisites ✓ all clear
deny-list ✗ matches: migrations
size ✓ 118L, 7F substantive, 344L/9F incl. docs/generated/snapshots — within ceiling
tier ✗ classified as T2-never: T2-never (344L, 9F, two-areas, feat)
stamphog 2.7.0 .stamphog/policy.yml @ unknown · reviewed head 00f9015

@Silthus

Silthus commented Nov 4, 2026

Copy link
Copy Markdown
Contributor Author

@PostHog review

@posthog

posthog Bot commented Nov 4, 2026

Copy link
Copy Markdown
Contributor

@Silthus PostHog Review is reviewing this pull request.

@posthog posthog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PostHog Review · Standard · reviewed 00f9015: no findings.

@Silthus

Silthus commented Nov 4, 2026

Copy link
Copy Markdown
Contributor Author

/trunk merge

@trunk-io

trunk-io Bot commented Nov 4, 2026 •

Copy link
Copy Markdown

😎 Stack merged successfully - details.

@Silthus

Silthus commented Nov 4, 2026

Copy link
Copy Markdown
Contributor Author

/trunk cancel

@Silthus

Silthus commented Nov 4, 2026

Copy link
Copy Markdown
Contributor Author

/trunk merge

1 similar comment
@Silthus

Silthus commented Nov 4, 2026

Copy link
Copy Markdown
Contributor Author

/trunk merge

@stamphog stamphog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not approved yet — waiting on the conditions below.

The Migration risk check has not finished for this commit, so stamphog cannot tell a safe migration from a risky one yet. The review runs again on the next push, or you can re-request it once the check reports.

Gate mechanics and policy version
Gate Result
prerequisites ✓ all clear
deny-list ✗ matches: migrations
size ✓ 118L, 7F substantive, 344L/9F incl. docs/generated/snapshots — within ceiling
tier ✗ classified as T2-never: T2-never (344L, 9F, two-areas, feat)
stamphog 2.7.0 .stamphog/policy.yml @ unknown · reviewed head e876d99

@Silthus

Silthus commented Nov 4, 2026

Copy link
Copy Markdown
Contributor Author

/trunk merge

@trunk-io
trunk-io Bot merged commit a60a02e into master Nov 4, 2026
281 of 282 checks passed
@trunk-io
trunk-io Bot deleted the email-v2/11-first-code-send-event branch November 4, 2026 20:20
@deployment-status-posthog

deployment-status-posthog Bot commented Nov 4, 2026 •

Copy link
Copy Markdown

Deploy status

Environment Status Deployed At Workflow
dev ✅ Deployed 2026-11-04 21:00 UTC Run
prod-us ✅ Deployed 2026-11-04 21:42 UTC Run
prod-eu ✅ Deployed 2026-11-04 21:35 UTC Run

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

Labels

stamphog Request AI approval (no full review)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants