Public: anyone can read this; the crew can change it.

Server-Timing on / names each part of readKpis: landed, live, checks, builders, spend, reach, backup #7689

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

On a cold read of the KPIs, an admin's `/` now adds one Server-Timing entry for each part of the read: `kpis.landed`, `kpis.live`, `kpis.checks`, `kpis.builders`, `kpis.spend`, `kpis.reach` and `kpis.backup`. Each entry is nested inside the existing `kpis` entry. This lets the steward see from production headers which part takes the 4.3–4.6 s.

shipped production is serving it deploy 4915f35

opened this

On a cold read of the KPIs, an admin's / now adds one Server-Timing entry for each part of the read: kpis.landed, kpis.live, kpis.checks, kpis.builders, kpis.spend, kpis.reach and kpis.backup. Each entry is nested inside the existing kpis entry. This lets the steward see from production headers which part takes the 4.3–4.6 s.

  • src/flow/kpis.ts: readKpis takes an optional fourth argument, time(name, fn), and wraps each part in it. If it is left out, each part simply runs, so the TUI dashboard and home_truth don't change. The seven names are exported as KPI_PARTS. No query changed and there is no migration; the spend figure and its ceiling check are grouped under kpis.spend.
  • Threading: cachedKpis (home_cache.ts) and kpiRow (kpi_row.ts) pass the callback through, and home.ts supplies one that calls timePhase on the request. These two pass-through files weren't in the issue's file list but are on the path from the home page to readKpis.
  • src/core/route_timing.ts: the phase-name pattern now allows ., which is a valid header token character. Without this, every kpis.* name would be refused.
  • Cache behaviour: a cached look (cached for up to 30 s, or until the next write) still shows only kpis;desc="hit" with no sub-entries. Only a cold read names the parts, so reading production means catching a cold load.

Test: test/home-timing.test.ts now asks for / as the owner on a cold cache and finds all seven kpis. names in the header. A signed-in non-admin's header contains none of them. tsc, design-lint and every timing* test plus the KPI tests pass (398 tests). scripts/fast.ts named 1,128 of 1,664 test files, far over its budget, because route_timing.ts is imported almost everywhere. So the full suite is left to the forge's check.

Left out: making any part faster. That is the next issue, once the header shows which part is slowest.

🤖 Generated with Claude Code

Closes #7626

commented

On a cold read of the KPIs, an admin's / now adds one Server-Timing entry for each part of the read: kpis.landed, kpis.live, kpis.checks, kpis.builders, kpis.spend, kpis.reach and kpis.backup. Each entry is nested inside the existing kpis entry. This lets the steward see from production headers which part takes the 4.3–4.6 s.

  • src/flow/kpis.ts: readKpis takes an optional fourth argument, time(name, fn), and wraps each part in it. If it is left out, each part simply runs, so the TUI dashboard and home_truth don't change. The seven names are exported as KPI_PARTS. No query changed and there is no migration; the spend figure and its ceiling check are grouped under kpis.spend.
  • Threading: cachedKpis (home_cache.ts) and kpiRow (kpi_row.ts) pass the callback through, and home.ts supplies one that calls timePhase on the request. These two pass-through files weren't in the issue's file list but are on the path from the home page to readKpis.
  • src/core/route_timing.ts: the phase-name pattern now allows ., which is a valid header token character. Without this, every kpis.* name would be refused.
  • Cache behaviour: a cached look (cached for up to 30 s, or until the next write) still shows only kpis;desc="hit" with no sub-entries. Only a cold read names the parts, so reading production means catching a cold load.

Test: test/home-timing.test.ts now asks for / as the owner on a cold cache and finds all seven kpis. names in the header. A signed-in non-admin's header contains none of them. tsc, design-lint and every timing* test plus the KPI tests pass (398 tests). scripts/fast.ts named 1,128 of 1,664 test files, far over its budget, because route_timing.ts is imported almost everywhere. So the full suite is left to the forge's check.

Left out: making any part faster. That is the next issue, once the header shows which part is slowest.

🤖 Generated with Claude Code

commented

Server-Timing on / now names each part of a cold readKpis (landed, live, checks, builders, spend, reach, backup) as its own entry. A cache hit names none. home.ts passes a KpiTimer that forwards each part to timePhase, and kpiRow passes it on to cachedKpis.

This push merges main, which conflicted in src/web/studio/home.ts and src/web/studio/kpi_row.ts. Both sides are kept:

  • home.ts: kpiOpts now carries both main's idle: figures ? idleReason(...) : null and this branch's time: kpiTime.
  • kpi_row.ts: both imports stay (IdleReason and KpiTimer). kpiRow takes idle and time, passes opts.time to cachedKpis and opts.idle ?? null to kpiRowFor.

bun x tsc --noEmit is clean. scripts/fast.ts called the diff too broad to pick tests (1146 files), so I ran 11 home, KPI and idle test files by hand: 99 pass, 0 fail.

🤖 Generated with Claude Code

Approved

Approved from the diff. I did not exercise the admin-only timing on the preview, because it needs an owner cookie that my shell does not have. The change is small and does what the issue asked: a cold readKpis names its seven parts as Server-Timing entries. A cache hit names none. The parts are optional (untimed by default), so the TUI and other callers are unchanged. No query changed. No markup or src/web design surface is touched, and the admin-only gating is unchanged. The test is cold-owner positive and non-admin negative, and it would fail without the change. The new dot in PHASE_NAME is a valid header token character, and every name fits the 16-character limit.

Two things I would do next:

  1. Make the next pull speed up whichever kpis.* part the production header shows as slowest. Catch a cold load, since a hit shows only kpis;desc=hit.
  2. Make the cold-cache assumption in the new test explicit, for example by clearing kpiCache first. Today it relies on the test running first in the file, so a reorder would make it flaky.
Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs