Skip to content

Keep an article's publish date across a draft round-trip - #157

Merged
ssavutu merged 1 commit into
mainfrom
agent/publish-date-preservation-and-section-pinning
Aug 3, 2026
Merged

Keep an article's publish date across a draft round-trip#157
ssavutu merged 1 commit into
mainfrom
agent/publish-date-preservation-and-section-pinning

Conversation

@ssavutu

@ssavutu ssavutu commented Aug 3, 2026

Copy link
Copy Markdown
Member

Why

Rui Zhao's "Opposites" article lost its publish date during an editing session on Aug 2. The evidence from the live database is unambiguous:

id slug creation_date pub_date
10025 opposites 2026-07-24 13:00:00 2026-08-03 02:17:00
10024 madison-beer… 2026-07-24 13:00:00 2026-07-24 13:00:00
10026 horoscopes-7-24 2026-07-24 13:00:00 2026-07-24 13:00:00

Of 10,001 published articles, 10,000 have pub_date == creation_date and exactly one does not. Its new date is the exact second of a PATCH /v1/articles/opposites in the activity log.

A normal save of a live article was already safe — articlePatchDateColumns leaves pub_date alone — and there is now an integration test proving it. The hole was the draft round-trip: saving as Draft set pub_date = NULL irrecoverably, so the next publish invented today's date.

What changed

  • last_pub_date (new column, added idempotently in EnsureArticlesSchema alongside scheduled_pub_date and archived_at — no migration step). Going to draft parks the live date there; publishing without an explicit date reuses the article's own date, current first then parked, and only falls back to now for something genuinely never published.
  • Sections pin like authors in the editor. Checked sections and subsections sort to the top and stay visible through a section search, matching the existing byline behavior — unchecking something you can no longer see is how a section assignment silently gets dropped.
  • Integration tests (CMS_TEST_DSN, skipped without it) for the publish-date paths and for the scheduled status filter: on its own, combined with the title search, and confirming it stays invisible to anonymous callers.

Verification

go test ./..., tsc --noEmit, npm run lint (one pre-existing warning), npm run build. Integration tests run against a local MariaDB:

CMS_TEST_DSN='…/cms_patch_test?parseTime=true&multiStatements=true' go test ./internal/handlers/ -run ArticlePatchHTTP -v

Not included

The already-damaged row still needs a one-time fix on Delta; this PR does not touch production data:

UPDATE articles SET pub_date = '2026-07-24 13:00:00'
WHERE slug = 'opposites' AND pub_date = '2026-08-03 02:17:00';

Separately worth considering: the editor never displays the publish date of a published article (the date field only appears in Schedule mode), so an editor flipping to Draft and back has no way to see the date change underneath them.

🤖 Generated with Claude Code

Saving as Draft cleared pub_date outright, so the original publish date
was gone for good and the next publish stamped today's. That is how the
"Opposites" article lost its 2026-07-24 date during an editing session:
it is the only row in the database where pub_date no longer matches
creation_date, and its new date is the exact second of the PATCH that
republished it.

Draft now parks the live date in a new last_pub_date column, and
publishing without an explicit date reuses the article's own date --
current first, then the parked one -- so only something genuinely never
published gets stamped with now.

The column is added idempotently in EnsureArticlesSchema, matching how
scheduled_pub_date and archived_at were introduced; there is no
migration step.

Also pins checked sections and subsections to the top of the editor's
section list and keeps them visible through a search, the same way the
authors list already pins the byline -- unchecking something you can no
longer see is how a section assignment silently gets dropped.

Adds integration tests (CMS_TEST_DSN) covering the publish-date paths
and the scheduled status filter, including its combination with the
title search and the fact that it stays invisible to anonymous callers.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@ssavutu
ssavutu merged commit 3870a01 into main Aug 3, 2026
6 checks passed
@ssavutu
ssavutu deleted the agent/publish-date-preservation-and-section-pinning branch August 3, 2026 04:28
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