Two commits landed on feat/about-and-blog after PR #140 had already merged, so they never reached main. Pushing to a merged branch does not reopen its PR. Cherry-picked here onto current main.
Without this, staging cannot start. It is currently failing on boot with:
error: relation "payload.users" does not exist (code 42P01)
1. The initial Payload migration (e2c63a9)
main has payload.config.ts but no migrations/ directory and no prodMigrations, so a deployed container connects to an empty schema and fails its first query. The adapter cannot self-create tables: @payloadcms/db-postgres/dist/connect.js:110 gates push on NODE_ENV !== 'production', so it is inert in a deployed container no matter how it is configured.
The generated migration was not sufficient on its own. Every statement is schema-qualified to "payload", but schemaName only tells Payload where to put tables — it does not create the schema. It ran against the throwaway database used to generate it only because that schema had been created there by hand, so every real environment would have failed on the first statement.
CREATE SCHEMA IF NOT EXISTS "payload" is therefore hand-added at the top of up(). That makes it exactly the kind of edit a future regeneration discards silently, so __tests__/payload/migrations.test.ts asserts it is present and ordered before the first CREATE TABLE.
Tables created: users, users_sessions, posts, _posts_v (the versions table drafts require), media, and Payload's bookkeeping tables.
payload-types.ts is now committed rather than ignored. Ignoring it was a mistake: CI typechecked against looser types than a developer with a generated copy, which is how a Record<string, unknown> cast passed CI and then failed locally the moment the file appeared. The post page now uses the generated Post and Media types and narrows heroImage rather than asserting it, since that field is an id at shallow depth.
2. Em dashes removed from the site's prose (3f3c595)
The em dash is one of the clearest tells of machine-written text, which is the exact impression this whole body of work exists to remove. Rewritten rather than substituted: where a dash carried a real aside the sentence is split or recast, not patched with a comma.
Covers the About page, the two Callout labels an editor sees in the admin panel, and PUBLISHING.md, which defines the house style and should follow it. The rule is recorded in that house style and in the spec's voice rules so it outlives this branch. Code comments are left alone; they are not copy.
Verification
399 tests pass
npm run typecheck clean
npm run build succeeds with DATABASE_URL and PAYLOAD_SECRET both unset, which is how CI builds it
CREATE SCHEMA present and correctly ordered; prodMigrations wired
No em dashes remain in the About page copy
After merge
The migration files are imported by payload.config.ts, so they are compiled into the bundle — a redeploy of the existing image will not pick them up. This needs the fresh staging build that merging triggers. On first boot prodMigrations creates the schema and tables automatically.
Then, to get the staging gate green:
Seed the admin account: npx payload create-first-user
Write the first post — the blog e2e journey asserts one exists
Add nextjs-app/public/brand/tudor.jpg — /about shows a broken image until then
Two commits landed on `feat/about-and-blog` after PR #140 had already merged, so they never reached `main`. Pushing to a merged branch does not reopen its PR. Cherry-picked here onto current `main`.
**Without this, staging cannot start.** It is currently failing on boot with:
```
error: relation "payload.users" does not exist (code 42P01)
```
## 1. The initial Payload migration (`e2c63a9`)
`main` has `payload.config.ts` but no `migrations/` directory and no `prodMigrations`, so a deployed container connects to an empty schema and fails its first query. The adapter cannot self-create tables: `@payloadcms/db-postgres/dist/connect.js:110` gates `push` on `NODE_ENV !== 'production'`, so it is inert in a deployed container no matter how it is configured.
**The generated migration was not sufficient on its own.** Every statement is schema-qualified to `"payload"`, but `schemaName` only tells Payload where to put tables — it does not create the schema. It ran against the throwaway database used to generate it only because that schema had been created there by hand, so every real environment would have failed on the first statement.
`CREATE SCHEMA IF NOT EXISTS "payload"` is therefore hand-added at the top of `up()`. That makes it exactly the kind of edit a future regeneration discards silently, so `__tests__/payload/migrations.test.ts` asserts it is present **and** ordered before the first `CREATE TABLE`.
Tables created: `users`, `users_sessions`, `posts`, `_posts_v` (the versions table drafts require), `media`, and Payload's bookkeeping tables.
**`payload-types.ts` is now committed rather than ignored.** Ignoring it was a mistake: CI typechecked against looser types than a developer with a generated copy, which is how a `Record<string, unknown>` cast passed CI and then failed locally the moment the file appeared. The post page now uses the generated `Post` and `Media` types and narrows `heroImage` rather than asserting it, since that field is an id at shallow depth.
## 2. Em dashes removed from the site's prose (`3f3c595`)
The em dash is one of the clearest tells of machine-written text, which is the exact impression this whole body of work exists to remove. Rewritten rather than substituted: where a dash carried a real aside the sentence is split or recast, not patched with a comma.
Covers the About page, the two Callout labels an editor sees in the admin panel, and `PUBLISHING.md`, which defines the house style and should follow it. The rule is recorded in that house style and in the spec's voice rules so it outlives this branch. Code comments are left alone; they are not copy.
## Verification
- 399 tests pass
- `npm run typecheck` clean
- `npm run build` succeeds with `DATABASE_URL` and `PAYLOAD_SECRET` both unset, which is how CI builds it
- `CREATE SCHEMA` present and correctly ordered; `prodMigrations` wired
- No em dashes remain in the About page copy
## After merge
The migration files are imported by `payload.config.ts`, so they are compiled into the bundle — a redeploy of the existing image will not pick them up. This needs the fresh staging build that merging triggers. On first boot `prodMigrations` creates the schema and tables automatically.
Then, to get the staging gate green:
- [ ] Seed the admin account: `npx payload create-first-user`
- [ ] Write the first post — the blog e2e journey asserts one exists
- [ ] Add `nextjs-app/public/brand/tudor.jpg` — `/about` shows a broken image until then
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_017YmbBhr8s7GusjDE12hrZM
The em dash is one of the clearest tells of machine-written text, which
is the exact impression this work exists to remove. Rewritten rather
than substituted: where a dash was carrying a real aside the sentence is
split or recast, not patched with a comma.
Covers the About page, the two Callout labels an editor sees in the
admin panel, and PUBLISHING.md, which defines the house style and should
follow it. The rule is now recorded in that house style and in the
spec's voice rules, so it survives this branch.
Code comments are left alone: they are not copy, and the surrounding
codebase uses the same punctuation throughout.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017YmbBhr8s7GusjDE12hrZM
Staging failed on boot with 42P01, relation "payload.users" does not
exist. The schema was empty because no migration existed, and the
adapter cannot create tables itself: db-postgres/connect.js gates push
on NODE_ENV !== 'production', so it is inert in a deployed container
regardless of config.
The generated migration is schema-qualified to "payload" throughout but
does not create that schema — schemaName says where tables go, it does
not create anything. It only worked against the throwaway database used
to generate it because the schema was created there by hand, so every
real environment would have failed on the first statement. CREATE SCHEMA
IF NOT EXISTS is hand-added at the top of up(), which makes it exactly
the kind of edit a regeneration discards silently; a test asserts it is
present and ordered before the first CREATE TABLE.
payload-types.ts is now committed rather than ignored. Ignoring it meant
CI typechecked against looser types than a developer with a generated
copy, which is how a Record<string, unknown> cast passed CI and then
failed locally the moment the file appeared. The post page uses the
generated Post and Media types instead, and narrows heroImage rather
than asserting it, since the field is an id at shallow depth.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017YmbBhr8s7GusjDE12hrZM
This PR ships the initial Payload CMS migration (with a hand-added CREATE SCHEMA statement and a regression test guarding it), wires prodMigrations into payload.config.ts so production containers actually get tables instead of relying on schema push, switches payload-types.ts from gitignored/generated to committed, tightens types in the blog post page (dropping unsafe casts), and does a copy pass removing em dashes from prose and docs. The migration/schema-creation logic checks out against the Postgres adapter's behavior (push disabled in production, prodMigrations required), CI runs typecheck and the new migration test, and the blog page's type narrowing is safe given the _status: published query filter.
🟡 Minor
nextjs-app/payload-types.ts: This generated file is now committed (and un-ignored in .gitignore) instead of being generated fresh on every build, with no CI step verifying it stays in sync with payload.config.ts. A future collection/field change that forgets to re-run payload generate:types would silently ship stale types that still typecheck.
## 🤖 AI Code Review (Claude Code)
This PR ships the initial Payload CMS migration (with a hand-added CREATE SCHEMA statement and a regression test guarding it), wires prodMigrations into payload.config.ts so production containers actually get tables instead of relying on schema push, switches payload-types.ts from gitignored/generated to committed, tightens types in the blog post page (dropping unsafe casts), and does a copy pass removing em dashes from prose and docs. The migration/schema-creation logic checks out against the Postgres adapter's behavior (push disabled in production, prodMigrations required), CI runs typecheck and the new migration test, and the blog page's type narrowing is safe given the `_status: published` query filter.
### 🟡 Minor
- **nextjs-app/payload-types.ts**: This generated file is now committed (and un-ignored in .gitignore) instead of being generated fresh on every build, with no CI step verifying it stays in sync with payload.config.ts. A future collection/field change that forgets to re-run `payload generate:types` would silently ship stale types that still typecheck.
tudor
merged commit 17e5371e9c into main2026-09-02 17:52:17 +00:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Two commits landed on
feat/about-and-blogafter PR #140 had already merged, so they never reachedmain. Pushing to a merged branch does not reopen its PR. Cherry-picked here onto currentmain.Without this, staging cannot start. It is currently failing on boot with:
1. The initial Payload migration (
e2c63a9)mainhaspayload.config.tsbut nomigrations/directory and noprodMigrations, so a deployed container connects to an empty schema and fails its first query. The adapter cannot self-create tables:@payloadcms/db-postgres/dist/connect.js:110gatespushonNODE_ENV !== 'production', so it is inert in a deployed container no matter how it is configured.The generated migration was not sufficient on its own. Every statement is schema-qualified to
"payload", butschemaNameonly tells Payload where to put tables — it does not create the schema. It ran against the throwaway database used to generate it only because that schema had been created there by hand, so every real environment would have failed on the first statement.CREATE SCHEMA IF NOT EXISTS "payload"is therefore hand-added at the top ofup(). That makes it exactly the kind of edit a future regeneration discards silently, so__tests__/payload/migrations.test.tsasserts it is present and ordered before the firstCREATE TABLE.Tables created:
users,users_sessions,posts,_posts_v(the versions table drafts require),media, and Payload's bookkeeping tables.payload-types.tsis now committed rather than ignored. Ignoring it was a mistake: CI typechecked against looser types than a developer with a generated copy, which is how aRecord<string, unknown>cast passed CI and then failed locally the moment the file appeared. The post page now uses the generatedPostandMediatypes and narrowsheroImagerather than asserting it, since that field is an id at shallow depth.2. Em dashes removed from the site's prose (
3f3c595)The em dash is one of the clearest tells of machine-written text, which is the exact impression this whole body of work exists to remove. Rewritten rather than substituted: where a dash carried a real aside the sentence is split or recast, not patched with a comma.
Covers the About page, the two Callout labels an editor sees in the admin panel, and
PUBLISHING.md, which defines the house style and should follow it. The rule is recorded in that house style and in the spec's voice rules so it outlives this branch. Code comments are left alone; they are not copy.Verification
npm run typecheckcleannpm run buildsucceeds withDATABASE_URLandPAYLOAD_SECRETboth unset, which is how CI builds itCREATE SCHEMApresent and correctly ordered;prodMigrationswiredAfter merge
The migration files are imported by
payload.config.ts, so they are compiled into the bundle — a redeploy of the existing image will not pick them up. This needs the fresh staging build that merging triggers. On first bootprodMigrationscreates the schema and tables automatically.Then, to get the staging gate green:
npx payload create-first-usernextjs-app/public/brand/tudor.jpg—/aboutshows a broken image until then🤖 Generated with Claude Code
https://claude.ai/code/session_017YmbBhr8s7GusjDE12hrZM
🤖 AI Code Review (Claude Code)
This PR ships the initial Payload CMS migration (with a hand-added CREATE SCHEMA statement and a regression test guarding it), wires prodMigrations into payload.config.ts so production containers actually get tables instead of relying on schema push, switches payload-types.ts from gitignored/generated to committed, tightens types in the blog post page (dropping unsafe casts), and does a copy pass removing em dashes from prose and docs. The migration/schema-creation logic checks out against the Postgres adapter's behavior (push disabled in production, prodMigrations required), CI runs typecheck and the new migration test, and the blog page's type narrowing is safe given the
_status: publishedquery filter.🟡 Minor
payload generate:typeswould silently ship stale types that still typecheck.