Skip to content

Raise the breaking-news banner from the article that is breaking - #227

Merged
ssavutu merged 1 commit into
mainfrom
feat/breaking-news-banner-from-articles
Sep 3, 2026
Merged

Raise the breaking-news banner from the article that is breaking#227
ssavutu merged 1 commit into
mainfrom
feat/breaking-news-banner-from-articles

Conversation

@ssavutu

@ssavutu ssavutu commented Sep 3, 2026

Copy link
Copy Markdown
Member

Any editor could already tick Breaking news on their article, and the flag did nothing — it was stored, returned by the API, and read by no one. The banner it should have driven was a separate admin-only setting with hand-typed text, so raising one meant messaging an admin and having them retype the headline.

This wires the flag to the banner, and derives the banner rather than storing a second copy of the decision.

How it resolves

GetBreakingNewsState resolves on read from the newest published article flagged breaking, falling back to the manual banner when there is none:

WHERE breaking_news = 1
  AND pub_date IS NOT NULL AND pub_date <= UTC_TIMESTAMP()
  AND archived_at IS NULL
ORDER BY pub_date DESC, id DESC LIMIT 1

Deriving it is what makes scheduling work. That published predicate is the same one every other public read uses, so a story scheduled for 11am starts driving the banner at the instant it starts being readable. There is no publish hook that could fire early, late, or not at all, and unticking the flag takes the banner down just as directly. The public cache is 60s and the scheduler ticks at 60s, so the banner lands within a tick of the story itself.

Decisions

  • An article wins over the manual banner. It is the more specific and more recent signal, and "this story is the breaking story now" is the flow the flag exists for. When two are live, the newest holds it — there is only one banner to hold.
  • The manual banner is kept separate in the API response (manual) so Settings can still edit it while an article overrides it. Binding the form to the effective value would have overwritten an admin's text with a headline on their next save.
  • The banner carries the article's slug, not a path. The public site owns its URL shape and already composes links this way for developing stories. Scalene PR: Link the breaking-news banner to the article that raised it Scalene#105.
  • An optional time limit, off by default. Admins can set one in Settings, after which a flagged article stops holding the homepage; unset, a banner stays up until an editor unticks it, so nothing takes one down on their behalf unless someone asked for that. window_hours: 0 is the meaningful "no limit" value — note INTERVAL 0 HOUR would exclude every article, so the age clause is omitted from the query rather than passed as a sentinel.

Added idx_articles_breaking_news (breaking_news, pub_date) — the lookup runs on every homepage render and would otherwise scan the corpus to find nothing. Non-fatal at boot like the other index builders.

Testing

10 new tests (6 database, 4 handler) pass against a real MariaDB 11.7, covering the scheduled case, newest-wins, archived/unflagged exclusion, the unlimited default, an admin-set window, and the settings round-trip. Full Go suite, tsc, and the frontend tests are green.

Not included

Scalene needs DrexelTriangle/Scalene#105 for the banner to become clickable, but nothing here depends on it — /v1/homepage still returns breaking_news: {enabled, text} with article_slug merely added, so the public site picks up the article-driven banner with no deploy of its own.

🤖 Generated with Claude Code

https://claude.ai/code/session_01ES8gPZxe6sb5xSLrBZ7AGk

Any editor could already tick "Breaking news" on their article, and the
flag did nothing: it was stored, returned by the API, and read by no one.
The banner it should have driven was a separate admin-only setting with
hand-typed text, so raising one meant messaging an admin and having them
retype the headline.

Wire the flag to the banner, and derive the banner rather than storing a
second copy of the decision. GetBreakingNewsState resolves it on read from
the newest published article flagged breaking, falling back to the manual
banner when there is none.

Deriving it is what makes scheduling work. The published predicate is the
same one every other public read uses, so a story scheduled for 11am
starts driving the banner at the instant it starts being readable -- there
is no publish hook that could fire early, late, or not at all, and
unticking the flag takes the banner down just as directly. The public
cache is 60s and the scheduler ticks at 60s, so the banner lands within a
tick of the story.

An article wins over the manual banner: it is the more specific and more
recent signal, and "this story is the breaking story now" is the flow the
flag exists for. The manual banner is kept separate in the API response so
Settings can still edit it while an article overrides it -- binding the
form to the effective value would overwrite an admin's text with a
headline on their next save.

The banner carries the article's slug, not a path: the public site owns
its URL shape and already composes links this way for developing stories.

Admins can also set a limit after which a flagged article stops holding
the homepage. It is off by default, so nothing takes a banner down on an
editor's behalf unless someone asked for that.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ES8gPZxe6sb5xSLrBZ7AGk
@ssavutu
ssavutu merged commit fca3320 into main Sep 3, 2026
7 checks passed
@ssavutu
ssavutu deleted the feat/breaking-news-banner-from-articles branch September 3, 2026 10:29
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