Prisma database schema and migrations
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
WessCoby ae0b627085 Newsletter series: scope the issue number to its publication (MAI-197)
`issue_number` was UNIQUE across every issue ever written. That reads as
"two drafts cannot claim the same number", which is the right rule, but
the scope was one level too high: the second publication could never
have an "Issue 1", because the first one already did. Nothing was broken
while a single letter existed and it would have blocked the second
outright.

Scoping the constraint to `template_key` instead would have looked
correct and enforced nothing. A composed issue has no template key, and
Postgres treats NULLs as DISTINCT in a unique index — so every
editor-authored issue would have been free to reuse any number, silently,
which is the half of the table a human actually types numbers into.

So a `newsletter_series` table rather than a discriminator column. A
coded series could read its name from the registry in
`emails/newsletters/`, but that registry is code and an editor-invented
series is data, so a composed series would have had nowhere to keep one
and the name would have ended up hardcoded in two frontends.

`newsletter_issues.slug` stays globally unique. It is a URL that outlives
the send, and links in already-delivered mail resolve through it.

The migration is hand-written because Prisma's generated one adds a
required column to a populated table and fails. Ordering is the point:
add the column nullable, backfill it, and only then make it NOT NULL, in
one file so it cannot half-apply. The backfill is deliberately generic —
one series per distinct template key plus one for composed issues —
because this also runs on environments this repo cannot see, and
`available-on-the-gse` is named explicitly rather than derived, since the
derived form is reader-facing on the public archive.

The foreign key is RESTRICT, not CASCADE: deleting a series must never
delete issues, least of all sent ones, or the send log would outlive the
record of what was sent.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-19 16:12:08 +00:00
migrations Newsletter series: scope the issue number to its publication (MAI-197) 2026-09-19 16:12:08 +00:00
schema Newsletter series: scope the issue number to its publication (MAI-197) 2026-09-19 16:12:08 +00:00
.gitignore Add schema.dbml to .gitignore 2026-07-02 19:41:03 +00:00