Skip to content

feat(messaging): add template keys, owners and content hashes - #547

Closed
Silthus wants to merge 1 commit into
masterfrom
srv/templates-schema
Closed

Silthus wants to merge 1 commit into
masterfrom
srv/templates-schema

Conversation

@Silthus

@Silthus Silthus commented Oct 23, 2026 •

Copy link
Copy Markdown
Owner

Moved here from part 1 of 3 of #118924, closed on 2026-10-25 so this fork stack is the only copy. It opens upstream again after review.

Problem

Engineers, and the coding agents working for them, keep transactional emails as HTML files in their repository and want PostHog to send them (spec). A message template has nowhere to record that code owns it: no key code can name it by, no owner, no source file and no content hash. This part adds the columns, so the API part above can use them.

Merge Danger

Warning

🟡 Merge risk: medium · one-way door: the columns and the index stay after a revert, unused (merge-risk rubric)

  • Two migrations on posthog_messagetemplate. 0009 adds nullable columns only, so it does not rewrite the table.
  • 0010 builds the unique (team, key) index concurrently, for templates that are not deleted.
  • No code reads the columns until the API part above, which sits behind workflows-transactional-email-api.

Changes

Kind Files Lines
Production 5 +170 −1
Tests 1 +34 −0
Total (this layer) 6 +204 −1
  • A template gains key, managed_by (gui or code, null meaning gui), source_repository, source_path, source_ref, content_hash and superseded_content_hashes.
  • A key is unique per team among templates that are not deleted, so deleting a template releases its key.
  • TemplateManagedBy lives in the facade contracts, so the API and the frontend share one owner enum.
  • superseded_content_hashes has its readers and writers in the API part above (the template_superseded warning).

How did you test this code?

Test rationale: test_message_template_key.py drives the database seam: a duplicate key in one team fails, the same key in another team passes, and a deleted template's key can be reused. A unique index that ignored deleted, or one missing the team, fails it.

  • makemigrations messaging --check --dry-run on this head (ab6174d3f9d): "No changes detected in app 'messaging'".
  • Live run at the stack top 987b927630e, which holds this head unchanged, on a migrated clone of the local test database. The seed stored a template with the key password-reset, which then sent by key through POST messaging/send/email/ and was delivered (case 02).
  • Not exercised live: a key collision in one team, reuse across teams, and reuse after a delete. The test above covers them.
  • Local gate at 987b927630e, all green: pytest 1764, CDP jest 3690 (1 skipped), cdp-api serial 288, frontend jest 84, ClickHouse migration tests 460, MCP 5035, repo-wide mypy, nodejs and frontend tsc, and the HCL check.
  • Prod shape: gap, accepted until read-only production access arrives on 2026-11-02. The migration ran forward on local data only. Unknown: how long the concurrent index build takes on the largest team's template table.

Release status

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

Docs update

None in this part. The beta docs page /docs/workflows/transactional-email-api ships separately on posthog.com (Silthus/posthog#475).

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

Agent: Claude Code, Claude Opus 5.5 (claude-opus-5-5) wrote the code and the split. Reviews by GPT-6.1 Sol and Claude Opus 5.5.

  • This part comes from splitting #118924 for review, at most about 400 production lines per part. Each part started as one commit, and the three parts together equal feat(messaging): upload, send and lock code-managed email templates PostHog/posthog#118924 before the design-pass fixes.
  • Skills: /django-migrations, /tdd, /writing-tests, qa-swarm, /writing-pr-descriptions.
  • Public artifact: fixtures use invented data on example.com.
  • CodeRabbit is retired in this repository, so no CodeRabbit pass ran. CI on this fork PR waits for a maintainer to approve the workflow runs.

Decisions

The decisions for this change live on #118924. The split adds one:

Question Answer Who Where
How is PostHog#118924 split for review? Three parts by layer, one commit each, the top part's tree equal to PostHog#118924 🤖 Agent pending approval

Open: should managed_by default to gui instead of null for a template made in PostHog? It changes stored data and the read contract, which PostHog#120921 and the generated types state as "gui or null" (➡️ keep null: it is documented, and a later backfill to gui is additive).

Involvement: QA swarm design pass and round 1 (review and team lanes GPT-6.1 Sol high, fresh eyes Claude Opus 5.5 high). Round 1 found nothing to fix in this part, so no round 2 or final read ran on it. Live run captured at the stack top.

🤖 Generated with Claude Code

A message template gets a code key unique per team, an owner, a source and
a content hash, so code can upload and lock a template by key.

Part 1 of 3 of PostHog#118924.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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