Flake dashboard: row expiry, emoji streaks, createFailing rename
Three follow-ups from the first live week of the flake dashboard (#2580), agreed in discussion 2026-09-03.
Status#
Implemented and pushed; awaiting CI + review. All checklist items done: expiry
- emoji bar in the dashboard reducer/render (contract 0.2.0), createFailing rename across 10 files + docs. 20/20 dashboard tests, shared + renamed-callsite suites green, typecheck/lint/knip clean. Bugbot found the default-branch expiry judgment never fires for PR-only suites (specs/preview-e2e); reworked to a 3-run any-branch window, tests updated.
Known follow-up (pre-existing, out of scope): defaultBranchStreak — and
therefore transition proposals — also never fires for PR-only suites.
Motivation#
Renaming the chat-photos test in PR #2584 split its dashboard row in two: the
old name (multiple photos share a mosaic row; …) is frozen at 1 run / 100%
forever, next to the new name's row. More generally, deleted tests never leave
the table.
Decision: the test name is the identity, and rows expire by absence. No
rename detection, no lineage linking, no stable-id API. A rename resets the
streak — you touched the test, so fresh stats are honest — and history stays
readable in the issue's edit history and replayable from the immutable
/flakes event log. Expiry is a pure projection choice in the reducer/render:
nothing is deleted, so a transiently-absent row comes back with its full
history the next time it records.
Checklist#
- Row expiry (absence-based): a test row renders only if it appeared in
at least one of the last 3
run-recordedevents (any branch) of one of its suites. Originally specced as default-branch-judged with N=1; bugbot correctly flagged that specs/preview-e2e only run on PRs (cloudflare-previews.yml has no push trigger), so a default-branch reference point would never exist for them and the motivating chat-photos row could never retire. The 3-run window replaces the default-branch guard as the protection against one PR push hiding a row repo-wide. State: per-suiterecentRunOffsets(≤3) + per-testlastSeenOffset. - Expired rows are hidden from the table, not dropped from state. Footer
gains a one-liner:
_N retired tests hidden (no longer present in the latest default-branch run of their suite)._when N > 0. - Emoji streak column: each tracked test keeps its last 10 outcomes
from any branch (ring buffer in reduced state, oldest first).
Any-branch for the same reason as expiry: PR-only suites would
otherwise have permanently empty bars; matches the all-branch counts and
flake-rate columns.
defaultBranchStreakstays main-only. Render as arecentcolumn: 🟩 pass, 🟥 flake-fail, ❌ unexpected-error, oldest→newest. The existing numericdefault-branch streakcolumn stays (it carries the transition-threshold counts past 10). - Contract: new fields with defaults so existing prd state parses untouched; bump contract version 0.1.0 → 0.2.0.
- Harness tests in flake-dashboard.test.ts: rename splits then retires the old row on the next default-branch suite run; transient absence (PR-branch run without the test) does NOT retire; retired row revives with history intact when the name records again; emoji bar renders and caps at 10.
-
failing(…)→createFailing(…)rename for consistency withcreateFlake(both are two-level:createX(test, …)returns the registrar). Rename the export in packages/shared/src/test-support/failing-test.ts, update the docstring + log-message strings ("delete the createFailing() wrapper"), and all callsites (9 files, see notes). Filename stays failing-test.ts. No deprecated alias — few callsites, and an alias defeats the less-confusing goal. - Sweep doc references to
failing((docs/testing.md etc.).
Non-goals / explicitly rejected#
- Rename detection or a stable-id param on createFlake/createFailing.
- Deleting expired tests from reduced state (a lazy GC can come later if state size ever matters; the log is the source of truth regardless).
- Renaming the
switch-to-failingtransition enum value or any durable event shape — event values are append-only vocabulary.
Notes#
failing(callsites on main: failing-test.ts, failing-test.test.ts, flake-test.ts (docstring), userspace-facet-source-version.e2e.test.ts, userspace-facet-recycle-false-alarm.e2e.test.ts, project-create-concurrency.regression.e2e.test.ts, abandoned-project-goes-quiet.e2e.test.ts, oversized-settlement-isolate.e2e.test.ts, artifact-rpc-ownership / scheduler-rpc-ownership / guarantees-not-given / oversized-settlement-crash tests.- Conflict heads-up: PR #2575 (oversized-settlement-repro) edits
failing-test.ts (adds the vitest
retry: 0pin) and oversized-settlement-isolate.e2e.test.ts. This branch keeps its failing-test.ts diff to the mechanical rename only, so the merge either way is small; whichever lands second merges main and fixes up.