Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 26 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ build, so the template never becomes an incident.
id: controller-saving-down # stable, and what dismissal is keyed on — see below
severity: outage # info | warning | outage
title: Controller saving is down
summary: One or two sentences. Shown in the dashboard banner and the overlay's support panel.
summary: The symptom first, in a sentence that stands alone — see below
workaround: One line on what the reader can do right now. Optional.
links: # optional, up to 5, all https
- label: PlanetScale status
Expand Down Expand Up @@ -50,6 +50,25 @@ against the PR — has to pass before it can merge. Merging then runs the **Depl
the build fails, nothing is deployed and the previous version stays live** — which is also why the
build is worth running before you push.

### Writing a summary

The summary travels further than anything else in the file, and the surface that reaches the most
people shows the least of it. In the dashboard it is a toast: the title, then about three lines of
summary on desktop and two on a phone, clamped, with the rest a press away. The alert panel, the
"Report a Bug" interstitial, the card on this site and the overlay's support panel all show it
whole.

So lead with the symptom, in one sentence that stands on its own — what breaks, and where someone
sees it. Use the rest for what that sentence earns. Past about 200 characters the build says so,
as a note rather than a failure, because nothing about prose length should hold up a status page
mid-incident.

Two things don't belong in it:

- **The workaround.** Every surface that shows the summary renders `workaround` directly beneath
it, so a fix written into both reads twice.
- **The long version.** That is what the body is for, and it is the one field with no limit.

### When something is actually on fire

Repository admins can still push straight to `main`, and that is deliberate: this site exists to
Expand Down Expand Up @@ -106,10 +125,15 @@ Don't bother putting a CDN in front of this to speed it up; there is nothing to

| Reader | What it shows |
| --- | --- |
| Dashboard banner | The loudest active alert, with its workaround and first link |
| Dashboard toast | The loudest active alert: its title, and the first two or three lines of its summary |
| Dashboard alert panel | That same alert whole — summary, workaround and every link — one press from the toast |
| Dashboard "Report a Bug" | Every active alert, before the bug tracker opens |
| Overlay support panel | Every active alert, as health checks, and in the copied report |

A phone's toast opens the panel, since sending someone to another tab mid-task is too much. On
desktop it links here instead when the incident has a page of its own, and opens the panel when it
does not.

`alerts.json` is `{ "schema": "gpv-status/1", "alerts": [...] }`, and it carries only what a reader
could act on: every incident still open or still to come, plus anything resolved in the last 30 days.
The archive grows on the index page instead, which keeps every incident forever — no reason to make
Expand Down
38 changes: 36 additions & 2 deletions build.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,16 @@ const SEVERITY_LABEL = { info: 'Info', warning: 'Warning', outage: 'Outage' };

const LIMITS = { title: 120, summary: 500, workaround: 200, id: 64, label: 60, links: 5 };

/**
* Roughly how much of a summary the dashboard's toast shows: it clamps to three lines on desktop
* and two on a phone. Past this a summary is still shown whole everywhere else — the alert panel,
* the "Report a Bug" interstitial, the overlay's support panel and the card on this site — but the
* toast is where most people meet an incident, so a summary that needs its second half to make
* sense is one most readers will only half read. A note rather than a failure: nothing about prose
* length should hold up a status page mid-incident.
*/
const SUMMARY_CLAMPED_AT = 200;

/**
* How long a resolved incident stays in alerts.json. Nothing reading the feed ever shows a resolved
* alert — the dashboard and the overlay both check the window themselves — so this is slack for a
Expand All @@ -59,10 +69,21 @@ const FEED_RESOLVED_DAYS = 30;
const MAX_FEED_ALERTS = 50;

const problems = [];
const notes = [];

/** Collects every problem rather than stopping at the first, so one run tells you all of them. */
const fail = (file, message) => problems.push(`${file}: ${message}`);

/** Worth saying, but never worth refusing to publish over. Printed once the run is done. */
const note = (file, message) => notes.push(`${file}: ${message}`);

const printNotes = () => {
if (notes.length === 0) return;
console.warn('\nWorth a look, though none of this stopped the build:\n');
for (const message of notes) console.warn(` - ${message}`);
console.warn('');
};

const isHttps = (value) => {
try {
return new URL(value).protocol === 'https:';
Expand Down Expand Up @@ -92,7 +113,7 @@ function validate(file, data, body) {

const text = (value, field, max, required) => {
if (value === undefined || value === null || value === '') {
if (required) fail(file, `needs a ${field}`);
if (required) fail(file, `needs ${field}`);
return undefined;
}
if (typeof value !== 'string') {
Expand Down Expand Up @@ -129,7 +150,7 @@ function validate(file, data, body) {
}
}

return {
const alert = {
id: text(id, 'an id', LIMITS.id, true),
severity,
title: text(title, 'a title', LIMITS.title, true),
Expand All @@ -140,6 +161,17 @@ function validate(file, data, body) {
endsAt: endsAt === '' || endsAt === undefined ? null : endsAt,
body
};

if (alert.summary && alert.summary.length > SUMMARY_CLAMPED_AT) {
note(
file,
`the summary is ${alert.summary.length} characters and the dashboard's toast shows about ` +
`${SUMMARY_CLAMPED_AT}. Lead with the symptom in a sentence that stands on its own, and ` +
'leave the workaround to its own field and the long version to the body.'
);
}

return alert;
}

async function readAnnouncements() {
Expand Down Expand Up @@ -392,6 +424,7 @@ async function main() {

if (checkOnly) {
console.log(`${announcements.length} announcement(s) look fine.`);
printNotes();
return;
}

Expand Down Expand Up @@ -423,6 +456,7 @@ async function main() {
console.log(
`Built ${announcements.length} announcement(s), ${active} active, ${feed.length} in the feed, into dist/`
);
printNotes();
}

main().catch((error) => {
Expand Down
Loading