Announcements shown in the Noctua landing page, the Standard Annotation Editor and the Visual Pathway Editor — planned outages, updates, new feature documentation, getting started links, and anything else worth putting in front of active Noctua users.
Posting an announcement? → AUTHORS.md
The most recent active announcement shows as a banner with its title, first paragraph, and a link to more detail. A bell shows how many announcements there are; clicking it opens the panel.
The panel lists every active announcement in full.
announcements/*.md → GitHub Action → announcements.json → Noctua apps
(what you edit) (validates) (published to Pages)
One Markdown file per announcement in announcements/. Each has
a YAML frontmatter block for the settings and Markdown below it for the text.
Commit to main and a GitHub Action validates every file, builds them into a
single JSON feed, and publishes it to GitHub Pages.
If validation fails, nothing is published and the previous feed stays live. The deploy step only runs when the build succeeds, so a malformed commit can't take the banner down — it just doesn't update. GitHub emails the author with what went wrong.
Feed URL: https://geneontology.github.io/noctua-announcements/announcements.json
Served by GitHub Pages with Access-Control-Allow-Origin: *, so it's fetchable
from the browser. Pages purges its CDN on deploy, but browsers cache for 10
minutes — fetch with cache: 'no-store' if you want changes to land promptly.
The feed is a flat array, newest first:
[
{
"id": "2026-03-14-march-maintenance",
"title": "Scheduled maintenance Friday",
"level": "danger",
"type": "maintenance",
"pinned": false,
"testing": false,
"apps": ["landing-page", "sae", "vpe"],
"starts": "2026-03-10",
"expires": "2026-03-16",
"description": "Noctua will be down for about 30 minutes on Friday at 4:00 PM PST.",
"body": "<p>Noctua will be down for about 30 minutes…</p>",
"descriptionUrl": null
}
]levelis severity (colour);typeis category (icon). Both are always present,typedefaulting toannouncement.pinnedis always present. A pinned announcement sorts first, should always show its banner, and must not be dismissible in the consumer.descriptionis the first paragraph as plain text — banner copy.bodyis the full text as sanitized HTML — panel copy. Sanitizing happens at build time; links are rewritten withtarget="_blank" rel="noopener noreferrer".starts/expiresarenull, a date, or an absolute instant:"2026-03-14"means the whole of that day in the viewer's own timezone.startsis its first moment,expiresits last — an announcement expiring on the 14th is still showing all through the 14th and gone on the 15th. Resolve these locally, never in UTC, or the day flips at the wrong moment."2026-03-14T23:00:00.000Z"is an exact moment, already converted from the author's timezone at build time. Compare it against the clock as it is.- The build leaves out anything that expired more than two days ago, so the feed stays small however many announcements accumulate. Consumers must still filter: the feed carries ones that have not started, ones ending today, and ones that ended since the last build.
- A consumer showing a timed announcement should re-check when the next
startsorexpiresfalls due, not only when it refetches, or a window will stay open past its end.
appsis always present, defaulting to all three:landing-page,sae(Standard Annotation Editor),vpe(Visual Pathway Editor). Consumers must filter on this too.testingis always present, defaulting tofalse.truemeans the announcement is a draft: consumers must show it only in a non-production build — the dev site — and never in production.expiresis exclusive: an announcement stops showing on that date.- Order is pinned first, then newest first.
Treat a failed fetch as "no announcements" and never block app render on it.
npm install
npm run build # writes dist/announcements.json
npm test # node's own test runner, no framework
The build prints each announcement with its level and active window, and lists the expired ones it left out — a quick way to sanity-check scheduling before committing.
Frontmatter rules live in schema.json; the build is
scripts/build.mjs and the dates and times it understands
are in scripts/schedule.mjs. CI runs the tests before
the build, so a broken script fails before anything is published.