a desktop audit was not a desktop audit
`formFactor: 'desktop'` changes which thresholds a metric is scored against and NOTHING else. Throttling and the user agent keep their defaults, which are mobile — so every desktop run was a desktop-sized window on a 4x-slowed CPU over slow 4G, announced as a moto-g. It read 86 where the same page reads 100. That gap was reported repeatedly as network latency to the host. It was not latency; it was this. Measured after the fix: desktop 100, LCP 0.4s against 2.2s. Nine more from the same review: runtimeError was never checked, so a page that failed to load reported "nothing failing" — the most reassuring possible output for a site that is down --json exited 0 before the gate, so the documented non-zero exit was true for a person reading the table and false for the only caller that could act on it scoreDisplayMode 'error' was filtered out while still nulling its category, so a category could read 0 with an empty findings list a missing metric serialised as 0, which a script reads as a perfect measurement `source` is a string on some audits and an object on others; String() on the object printed [object Object] the watchdog's surrender was permanent — the health branch sat below the check, so once it stopped trying it also stopped noticing, and a server fixed by hand left it asleep until the platform restarted a failed restart leaves no process at all, because startServer deletes before it starts. status null then reads as "somebody stopped it", which the watchdog leaves alone — one bad restart would have turned it off and taken the server with it, silently the skill claimed the panel uses this engine. Nothing but the CLI does and gave `bun run build:site` with no cwd, which only works from the website checkout — api/static-site.ts documents that and the skill did not Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+33
-10
@@ -78,28 +78,34 @@ let timer: ReturnType<typeof setInterval> | null = null;
|
||||
async function check(): Promise<void> {
|
||||
state.lastCheckedAt = new Date().toISOString();
|
||||
|
||||
if (state.surrendered) return;
|
||||
|
||||
// Inside the cooldown: it was just restarted and is probably still starting.
|
||||
if (state.lastRestartAt && Date.now() - Date.parse(state.lastRestartAt) < COOLDOWN_MS) return;
|
||||
|
||||
const servers = await readServers().catch(() => null);
|
||||
const dev = servers?.find((one: ServerStatus) => one.name === 'development');
|
||||
if (!dev || dev.port === null) return;
|
||||
|
||||
/*
|
||||
Only the lie. `online` and answering is fine; anything else is somebody's decision.
|
||||
── Answering again clears everything, INCLUDING having given up ──
|
||||
|
||||
`reachable` is null when there is no port to try, which is not a failure — it is a server that has
|
||||
never been configured, and starting that would be inventing an intention nobody had.
|
||||
This branch used to sit below the `surrendered` check, which made surrender permanent: once it
|
||||
stopped trying it also stopped noticing, so a server somebody fixed by hand left a watchdog that
|
||||
would never watch again until the whole platform restarted. Reading the state first and clearing
|
||||
it here is what makes giving up a pause rather than a death.
|
||||
|
||||
`online` and answering is fine; anything else is somebody's decision — see the note at the top.
|
||||
`reachable` is null when there is no port to try, which is a server nobody has configured.
|
||||
*/
|
||||
if (dev.status !== 'online' || dev.reachable !== false) {
|
||||
if (state.strikes) console.log('[fofos-admin] dev server answering again');
|
||||
if (state.strikes || state.surrendered) console.log('[fofos-admin] dev server answering again');
|
||||
state.strikes = 0;
|
||||
state.consecutiveRestarts = 0;
|
||||
state.surrendered = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (state.surrendered) return;
|
||||
|
||||
// Inside the cooldown: it was just restarted and is probably still starting.
|
||||
if (state.lastRestartAt && Date.now() - Date.parse(state.lastRestartAt) < COOLDOWN_MS) return;
|
||||
|
||||
state.strikes += 1;
|
||||
console.log(`[fofos-admin] dev server is online but not answering (${state.strikes}/${STRIKES})`);
|
||||
if (state.strikes < STRIKES) return;
|
||||
@@ -122,7 +128,24 @@ async function check(): Promise<void> {
|
||||
output: error instanceof Error ? error.message : String(error),
|
||||
}));
|
||||
|
||||
if (!result.ok) console.warn('[fofos-admin] the restart failed:', result.output.slice(0, 400));
|
||||
/*
|
||||
A failed start is the dangerous outcome, not the loud one.
|
||||
|
||||
`startServer` DELETES the pm2 entry before starting it. If the start then fails there is no
|
||||
process at all — and `status: null` reads, one tick later, as "a server somebody stopped", which
|
||||
this deliberately leaves alone. So a single bad restart would turn the watchdog off and take the
|
||||
server with it, silently.
|
||||
|
||||
Counted as a failure so `GIVE_UP_AFTER` still bounds the retries, and said loudly, because a
|
||||
development server that no longer exists is not something to find out about tomorrow.
|
||||
*/
|
||||
if (!result.ok) {
|
||||
console.error(
|
||||
'[fofos-admin] the dev server FAILED to start and no process is left — `pm2 start` was preceded ' +
|
||||
'by a delete. Start it from the panel, or `pm2 logs`:',
|
||||
result.output.slice(0, 400),
|
||||
);
|
||||
}
|
||||
|
||||
if (state.consecutiveRestarts >= GIVE_UP_AFTER) {
|
||||
state.surrendered = true;
|
||||
|
||||
Reference in New Issue
Block a user