evals-feed¶
The Evals page's feed — the project's current measured loss as a feed, the left list of the master-detail ([[evals-view]]). Latest reading per scenario, fresh AND stale mixed newest-first (freshness is never a filter — no stale control exists); one kind dropdown (video | image | all) as the only filter, the shared control the issues drain wears; title-only rows, media strictly lazy.
raw source¶
The Evals page (evals-view) is where a human reads the project's current measured loss — the leading
review surface, a top-level page of its own (evals outrank issues, so they get the leading page and the
f / ⌥F doors). This feed is its left list, and its outer container never scrolls — the list scrolls
internally. A feed of every reading ever filed grows without bound; a feed of the project's current loss
does not. The unit of this feed is the scenario, not the reading: yatsu already defines the latest
reading per scenario as the current score, so the feed is bounded by declared scenarios (structural,
slow-growing), never by measurement count. Review attends to what still counts.
expanded spec¶
Default view: latest reading per scenario, newest first — fresh and stale MIXED, always. Freshness is
never a filter: a stale reading is real measured loss and stays in the time-ordered feed, its row carrying
the muted ✓/✗ that marks it stale (so it reads as stale without being removed) — hiding it was the bug that
let a just-filed screenshot vanish while newer work looked absent, and the stale-only toggle once offered on
top of the always-mixed default proved redundant, so the head carries no stale control at all. The ONE
filter control is the evidence-kind dropdown — exactly three options, video | image | all — rendered
by the SAME shared FilterSelect control the issues-view drain's store filter uses (one implementation,
one look, never two page-local forks). It defaults to video, falling back to image when no reading
contains a video and to all when neither media kind is present. The dropdown lives in this group's sticky
head, on its control row beside the shell's fold toggle — evals-view owns the fold state but hands
the anchored button in as lead, so the head wears the same control-row grammar as the issues drain and
nothing floats over the list — and the filter is this group's own state — evals-view owns the page shell
(split, selection, j/k), never this
group's filter. The head's chip row carries the live-session-filter "N live" toggle (this feed is that
feature's second surface: it narrows to readings whose filer session is alive, the same one-judgment join the
originator chip renders). One deliberate exception rides that ownership the RIGHT way round: a deep-linked eval the
current filter would hide (evals-view's canonical #/evals/<node>/<scenario> address) is handed down
as a mustShow key, and the group widens its own dropdown to all so the address always renders its
eval — the page never reaches into the group's filter state, and an address naming no real eval changes
nothing. The widen is one-shot per arrival: once the target is visible the key clears, so a dropdown pick
that hides the current selection is the human's filter decision and always wins — the selection falls to
the first visible row instead of the filter snapping back.
Kinds are honest — and a reading carries a SET of them. Evidence is a LIST, so a reading's kinds are
every entry it holds: video/image/transcript (a legacy scalar blob with no recorded kind is an image —
every legacy capture was one), and note when it holds no blob at all (a verdict filed with prose only). A
MIXED reading (images + a video) belongs to EVERY media filter it contains — it shows under both the
video and image picks — and its row tag lists its media kinds, video-first (e.g. vid·img). A reading
never advertises media it lacks. But note and transcript are data-level kinds only, never filter
options: the dropdown stays video | image | all, so a transcript-only or blob-less reading surfaces under
all alone, and a blob-less row simply carries no media tag.
Rows are title-only, always — verdict mark · scenario · node · evidence-kind tag · relative time —
no media request of any kind in the list. Selecting a row opens it in the page's DETAIL pane as the
event-detail — media loads there, a <video> element exists only there. The group reports its visible
rows upward so the page's j/k walk the feed; history drills down per scenario
(the node's yatsu-eval-tab scaffold), not in the list.
One data path, one computation. The board nodes arrive as a prop from the app's single board
poll + SSE subscription — the section fetches nothing of its own — and latest-per-scenario is
scenarioStates, the same computation behind the node badge, the focus panel, and the eval tab; the feed
never re-derives the current score its own way. At scale the board fold itself converges to the same
semantics — latest reading per scenario plus a history count, the full timeline served per node on
demand — one convergence shared by this feed, the node eval tab, and board-lean;
clean --keep-latest already aligns the evidence bytes with it.