Skip to content

Commit 92c2f3d

Browse files
committed
fix(showcase/dashboard): correct starter staleness window to hourly cadence
The starter_smoke probe runs hourly (`schedule: "40 * * * *"`), but STARTER_STALE_AFTER_MS was set to 13h with a comment claiming a 6h cadence — ~13 missed hourly ticks before amber, defeating the intended two-miss flip. Re-derive the window from the 1h probe period to 2.5h: strictly > 2 periods (so two consecutive misses, last row ~3h old, flip amber) yet < 3h (so a single missed/slow-wake tick, last row ~2h old, stays green, absorbing a scale-to-zero cold-start). Reconcile both the staleness.ts and starter_smoke.yml comments to the same hourly basis + 2.5h window in lockstep (no more "6h" in the starter context). Extend the staleness tests with explicit hourly-tick boundaries (1 miss → green, 2 misses → amber). Also drop the inaccurate "hollow" from the not-supported ✗ comments — there is no hollow render variant; the state renders as grey ✗ text.
1 parent 2602ad9 commit 92c2f3d

5 files changed

Lines changed: 49 additions & 14 deletions

File tree

showcase/harness/config/probes/starter_smoke.yml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,11 @@
5151
# four checks are cheap HTTP round-trips, but each tick WAKES sleeping
5252
# scale-to-zero services, so we keep the cadence well above the 15-min smoke
5353
# rhythm to keep wake frequency (and idle cost) low. The dashboard's
54-
# `STARTER_STALE_AFTER_MS` (UI slot) is derived from THIS schedule at
55-
# strictly > 2 probe periods, so a single missed/slow-wake tick stays green
56-
# and only two consecutive misses flip amber. Keep this cron and that
57-
# staleness window in lockstep.
54+
# `STARTER_STALE_AFTER_MS` (UI slot) is derived from THIS hourly schedule
55+
# (1h probe period) at strictly > 2 probe periods — it is set to 2.5h, so a
56+
# single missed/slow-wake tick (last row ~2h old) stays green and only two
57+
# consecutive misses (last row ~3h old) flip amber. Keep this cron and that
58+
# 2.5h staleness window in lockstep.
5859
kind: starter_smoke
5960
id: starter_smoke
6061
schedule: "40 * * * *"

showcase/shell-dashboard/src/components/__tests__/cell-matrix.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -769,7 +769,7 @@ describe("CellMatrix — Starter row-group", () => {
769769
it("not-supported ✗ for an unmapped column, tooltip 'no starter for this integration'", () => {
770770
const { getByTestId } = renderMatrix(new Map());
771771
const cell = getByTestId("starter-cell-ag2-health");
772-
// grey/hollow ✗ — the chip carries the tooltip title.
772+
// grey ✗ — the chip carries the tooltip title.
773773
expect(cell.textContent).toContain("✗");
774774
const chip = cell.querySelector("[title]");
775775
expect(chip?.getAttribute("title")).toBe("no starter for this integration");

showcase/shell-dashboard/src/lib/live-status.test.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -956,6 +956,36 @@ describe("buildStarterBadge — 5-state cell vocabulary (§d)", () => {
956956
expect(b.label).toBe("✓");
957957
});
958958

959+
// Hourly-cadence derivation (starter_smoke.yml `schedule: "40 * * * *"`,
960+
// 1h probe period; STARTER_STALE_AFTER_MS = 2.5h). These pin the window to
961+
// the hourly basis: a single missed tick (last row ~2h old) MUST stay green;
962+
// two consecutive misses (last row ~3h old) MUST flip amber.
963+
const ONE_HOUR_MS = 60 * 60 * 1000;
964+
965+
it("window is strictly > 2 hourly probe periods and < 3 (derived 2.5h)", () => {
966+
expect(STARTER_STALE_AFTER_MS).toBeGreaterThan(2 * ONE_HOUR_MS);
967+
expect(STARTER_STALE_AFTER_MS).toBeLessThan(3 * ONE_HOUR_MS);
968+
});
969+
970+
it("single missed hourly tick (~2h old) stays green", () => {
971+
const oneMiss = row("starter:agno/agent", "starter", "green", {
972+
observed_at: new Date(NOW - 2 * ONE_HOUR_MS).toISOString(),
973+
});
974+
const b = buildStarterBadge("agent", true, oneMiss, NOW, "live");
975+
expect(b.tone).toBe("green");
976+
expect(b.label).toBe("✓");
977+
});
978+
979+
it("two consecutive missed hourly ticks (~3h old) flip amber ~", () => {
980+
const twoMisses = row("starter:agno/agent", "starter", "green", {
981+
observed_at: new Date(NOW - 3 * ONE_HOUR_MS).toISOString(),
982+
});
983+
const b = buildStarterBadge("agent", true, twoMisses, NOW, "live");
984+
expect(b.tone).toBe("amber");
985+
expect(b.label).toBe("~");
986+
expect(b.row?.state).toBe("degraded");
987+
});
988+
959989
it("gray ?: supported column, no row yet → gray ? (not-yet-run)", () => {
960990
const b = buildStarterBadge("interaction", true, null, NOW, "live");
961991
expect(b.tone).toBe("gray");

showcase/shell-dashboard/src/lib/live-status.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -430,7 +430,7 @@ const STARTER_LEVEL_DESCRIPTION: Readonly<Record<StarterLevel, string>> = {
430430
* Build a `BadgeRender` for one starter sub-cell, applying the FULL 5-state
431431
* cell vocabulary (§d):
432432
*
433-
* - not-supported → grey/hollow "✗", mapping-derived (`!isSupported`),
433+
* - not-supported → grey "✗", mapping-derived (`!isSupported`),
434434
* tooltip "no starter for this integration". Distinct
435435
* from the red smoke-failed ✗. Resolved FIRST so it can
436436
* never collide with the gray `?` initial state.
@@ -453,7 +453,7 @@ export function buildStarterBadge(
453453
connection: ConnectionStatus,
454454
): BadgeRender {
455455
if (!isSupported) {
456-
// Mapping-derived: this column has no starter (§a). Grey/hollow ✗,
456+
// Mapping-derived: this column has no starter (§a). Grey ✗,
457457
// visually distinct from a red smoke-failed ✗. NOT data-derived, so it
458458
// renders identically before and after the first probe tick.
459459
return {

showcase/shell-dashboard/src/lib/staleness.ts

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,18 @@ export const LIVENESS_STALE_AFTER_MS = 45 * 60 * 1000;
5050
* rows written by the harness `starter_smoke` probe family). Per spec §d, the
5151
* window is derived from the probe cadence and MUST be strictly greater than
5252
* two probe periods, so a single late/missed/slow-wake tick stays green and
53-
* only two consecutive misses flip amber. The probe ships on a 6h cadence
54-
* (mirroring the retired `test_smoke-starter.yml` cron the live signal
55-
* replaces); 13h is > 2×6h with headroom to absorb the scale-to-zero
56-
* cold-start latency (§c) folded into a single tick. Keep this in lockstep
57-
* with S2's `starter_smoke.yml` `schedule` — if the cadence changes, this
58-
* window must be re-derived (> 2× the new period). Not env-tunable.
53+
* only two consecutive misses flip amber. The probe ships on an HOURLY cadence
54+
* (`schedule: "40 * * * *"`, see harness/config/probes/starter_smoke.yml), so
55+
* the probe period is 1h. We set the window to 2.5h: > 2×1h (so two consecutive
56+
* missed hourly ticks, which age the last row to ~2h, then ~3h, flip amber on
57+
* the SECOND miss) yet < 3h (so a single missed/slow-wake tick — last row ~2h
58+
* old — stays green, absorbing a scale-to-zero cold-start wake folded into one
59+
* tick, §c). 2.5h is the tightest window satisfying ">2 periods" that still
60+
* trips on the second miss. Keep this in lockstep with S2's `starter_smoke.yml`
61+
* `schedule` — if the cadence changes, this window must be re-derived
62+
* (> 2× the new period). Not env-tunable.
5963
*/
60-
export const STARTER_STALE_AFTER_MS = 13 * 60 * 60 * 1000;
64+
export const STARTER_STALE_AFTER_MS = 2.5 * 60 * 60 * 1000;
6165

6266
/**
6367
* Determine whether a row's `observed_at` is older than `maxAgeMs` relative

0 commit comments

Comments
 (0)