docs(dashboard): add health-check reference section

This commit is contained in:
2026-08-24 10:18:43 +02:00
parent 08d1c44ca8
commit 12a0eca44a
2 changed files with 484 additions and 233 deletions
+57 -18
View File
@@ -45,8 +45,8 @@ CRITIC_C, ACTOR_C = "#1f77b4", "#ff7f0e"
ALPHA_C = "#9467bd" # alpha line on the losses panel
RELOAD_JS = ('<script type="text/javascript"><![CDATA[ '
'setTimeout(function(){ location.reload(); }, 60000); ]]></script>')
W, H = 1400, 1720
TITLE_H, GUIDE_H = 80, 80
W, H = 1400, 1850
HEALTH_H = 205 # bottom "Reading the signs" cheat-sheet band
# rows: (header_y, panel_top_y, panel_bottom_y, x_left, x_right)
# panel_top sits low enough to leave room under header+sub for the
# per-panel how-to-read guide (3 italic lines, see panel_guide)
@@ -79,9 +79,36 @@ GUIDES = {
"X = eval cycle number; Y = best score achieved.",
"higher is better; a rising trend means the bot is improving."),
}
GUIDE_LINES = [
"This file reloads itself in Chrome every sixty seconds.",
"Regenerate anytime with tools/watch_dashboard.sh or the python command.",
# bottom health-check cheat-sheet: one column per subsection; each bullet is
# a tuple of pre-wrapped text lines (first line gets the bullet marker)
SIGNS_COLUMNS = [
("Healthy patterns ✅", [
("Both critic and actor losses trending down over time",),
("Alpha decaying slowly from ~1.0,", "then plateauing — this is normal"),
("Win rates appearing and increasing in the eval panel",),
("Max scores rising in the max-score panel",),
("Alpha plateauing is NOT a problem — it means",
"exploration level is stable"),
]),
("Warning signs ⚠️", [
("Losses exploding (suddenly jumping to", "millions or billions)"),
("Win rates staying at 0% for a long time",
"after the first ~20 eval cycles"),
("Alpha reaching 0 — bot stops exploring entirely", "(gets stuck)"),
("Max scores flatlining (no improvement",
"over many eval cycles)"),
("Any single loss value above 1e6",),
]),
("What each metric means (brief)", [
("Critic loss: how wrong the bot's value",
"estimates are — should go down"),
("Actor loss: how well the bot's action policy",
"is doing — should go down overall", "(some bumps are normal)"),
("Alpha: exploration-exploitation tradeoff — starts",
"high, settles at a positive value (NOT zero)"),
("Max score: best score achieved per eval cycle —",
"rising trend = learning"),
]),
]
@@ -280,16 +307,23 @@ def map_fn(p0, p1, vmin, vmax, log=False):
return f
def guide_block(w, h, lines):
"""Plain-English "How to read" band at the bottom of the canvas."""
y = H - GUIDE_H
s = f'<rect x="0" y="{y}" width="{w}" height="{GUIDE_H}" fill="#f2f2f2"/>\n'
s += (f'<text x="16" y="{y + 21}" font-size="15" font-weight="bold">'
f"How to read</text>\n"
f'<text x="{w - 16}" y="{y + 20}" text-anchor="end" font-size="11" '
f'fill="#666">Regenerate anytime: python3 tools/plot_progress.py</text>\n')
for i, ln in enumerate(lines):
s += f'<text x="16" y="{y + 43 + i * 17}" font-size="13">{esc(ln)}</text>\n'
def signs_block():
"""Health-check cheat-sheet band along the bottom of the canvas."""
y0 = H - HEALTH_H
s = f'<rect x="0" y="{y0}" width="{W}" height="{HEALTH_H}" fill="#f2f2f2"/>\n'
s += (f'<text x="16" y="{y0 + 21}" font-size="15" font-weight="bold">'
"Reading the signs</text>\n"
f'<text x="{W - 16}" y="{y0 + 20}" text-anchor="end" font-size="11" '
'fill="#666">Regenerate anytime: python3 tools/plot_progress.py</text>\n')
for (head, bullets), x in zip(SIGNS_COLUMNS, (16, 500, 985)):
s += (f'<text x="{x}" y="{y0 + 43}" font-size="13" font-weight="bold">'
f"{esc(head)}</text>\n")
y = y0 + 61
for lines in bullets:
for j, ln in enumerate(lines):
s += (f'<text x="{x}" y="{y}" font-size="12">'
f"{esc(('• ' if j == 0 else ' ') + ln)}</text>\n")
y += 14
return s
@@ -496,7 +530,7 @@ def build_dashboard(campaign, metrics_f, out):
s += panel_guide(geo[3], geo[0] + 33, guide)
s += drawer() or "" # panels bare-return None on their no-data path
s += guide_block(W, H, GUIDE_LINES)
s += signs_block()
s += RELOAD_JS + "\n"
s += "</svg>\n"
return write_svg(out, s)
@@ -552,8 +586,13 @@ def selftest():
for t in PANEL_TITLES:
assert esc(t) in text, f"panel title missing: {t}"
assert text.count(PANEL_TITLES[0]) == 1
assert "How to read" in text, "reading guide missing"
assert 'width="1400"' in text and 'height="1720"' in text
assert "Reading the signs" in text, "health-check section missing"
for head, bullets in SIGNS_COLUMNS:
assert esc(head) in text, f"health-check column missing: {head}"
for lines in bullets:
assert esc("• " + lines[0]) in text, f"bullet missing: {lines[0]}"
assert text.count("• ") == sum(len(b) for _, b in SIGNS_COLUMNS)
assert 'width="1400"' in text and 'height="1850"' in text
# every panel carries its own What/Axes/Better how-to-read note
assert text.count("What:") == len(PANEL_TITLES), text.count("What:")
for g in GUIDES.values():