yatsu-score-badge¶
The at-a-glance scenario vocabulary on score.jsx — a per-scenario COUNT (✓ satisfied / total) on each node tile, a ringed circle on every eval-tab row, and each scenario's classification TAG CHIPS, so a board sweep reads how many of a node's scenarios are satisfied, which are blind spots, and what each one is.
The board carries every node's eval readings AND its declared scenarios (yatsu-eval-tab folds both onto
/api/board). This node spends that data on a glance: a small count on the node tile — ✓ satisfied
of total — that says, without opening anything, how many of a node's scenarios are measured-and-passing
and how many are still outstanding (failing, stale, or never measured). A score is execution, like an
issue count — so it rides beside the node, never as node state: the git-derived status dot keeps its own
authority, and the count is drawn deliberately UNLIKE it.
raw source¶
Put each node's yatsu score on its tile as a per-scenario count — ✓ satisfied / total — not one fuzzy
collapsed verdict. satisfied is the scenarios that are a fresh pass; the gap up to total is the
outstanding loss (a fresh fail, a stale reading, or a scenario never measured), so the number itself says how
far the node is from zero loss. The count's COLOUR carries the worst-first state — green when every scenario
is a fresh pass, red when any is a fresh fail, grey when the rest is only stale or blind — so the loudest
problem still reads at a glance. NO badge at all when the node declares no scenarios (no yatsu.md). The eval
tab keeps the per-reading circle (a ring whose colour is freshness, whose ✓/✗ is the verdict), and the
count reuses that same colour vocabulary, so tile and tab still speak ONE language.
expanded spec¶
One vocabulary, two surfaces. The scoring lives once in score.jsx: readingScore maps ONE reading to a
circle state; scenarioStates joins the node's DECLARED scenarios (the folded node.scenarios) to their
latest reading so a never-measured scenario is still seen — a unit of loss, not an absence; aggregateState
folds those per-scenario states to one worst-first colour; nodeScore is that aggregate; ScenarioCount
renders the tile/stat-bar count; ScoreBadge draws the per-reading ring. The node tile (node-graph) and
the node-info stat bar render ScenarioCount; the focus-panel renders the same per-scenario states as a
list; the eval tab (yatsu-eval-tab) renders the per-reading circle.
Tags are the second at-a-glance adornment. Beside the satisfaction count, a scenario carries
classification yatsu-core tags; score.jsx exports the one shared TagChips element that renders
them as a compact wrapping row of chips. It is the SAME element wherever a scenario surfaces — the
focus-panel row, the search palette (session-board-search), the eval tab's declared-scenario row —
so a tag looks identical everywhere and reads off the same .tag-chip vocabulary the other chips use.
scenarioStates already threads each scenario's tags through (it spreads the scenario), so the consumers
need no extra wiring. This node owns TagChips + its .tag-chip style slice; it does NOT own the tag
vocabulary or its validation — that is yatsu-core's schema. Count says how satisfied, tags say what
kind — two orthogonal glances on one scenario.
The aggregate is a worst-first fold over the per-scenario states: any fresh fail makes it red ✗ (the loudest current signal); else any stale scenario makes it grey (fail-flavoured if any stale scenario last-failed, else pass-flavoured — the node remembers its last verdict but admits it's out of date); else any scenario with no current score — never measured (its own state, counted toward the total), or only a note/legacy reading — makes it the empty blind-spot colour the scoreboard exists to surface; else every declared scenario is a fresh pass and it is green ✓. Because the fold now ranges over DECLARED scenarios, not just the readings that happen to exist, a node with an unmeasured scenario reads as the blind spot it is.
Freshness is the same live signal the tab and spex yatsu scan use (freshness): it arrives on each
reading's fresh flag — this node never recomputes it. A scenario's freshness can be scoped to its own
code files (yatsu-core); this node just reads the resulting per-reading fresh. A note and a legacy
pre-verdict reading carry no ✓/✗, so they read as a blind spot here while their textual verdict badge still
names them in the tab.
This node owns only its score slice of the shared node tile (SpecNode.jsx) and of the shared stylesheet
(its .score-badge + .scenario-count rules, sanctioned by node-graph's shared-stylesheet contract) —
exactly as dashboard-issues owns only its issue badge there — so a co-owner's churn in those files is that
feature, not this node's drift. Out of scope: what a score MEANS or how it is measured (that is spec-yatsu
/ yatsu-core); and the deep per-reading timeline, which is the eval tab's job.