control board: show the model each seat is running (#1503)

Jason asked for the running model (sonnet, opus, gpt-6-astra) on the
board. readSession keeps model and provider from the log's latest
model_change entry or assistant turn, whichever is later, so a /model
switch shows on the next scan; scanAgent exposes both; the page shows the
model under the agent name with the provider in the hover and a Model
detail row. Blank when the log names none.

Live check on a scratch board against the real data root: all 42 rows
carried a model. Board 91/91. Sonnet review caught a double-escaped hover
title; fixed.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
2026-09-12 11:13:32 -05:00
co-authored by Claude Fable 5.1
parent d0fb5b0f20
commit 0bed9ba3db
8 changed files with 99 additions and 4 deletions
@@ -655,6 +655,15 @@ test("page.html: task and active project cells show their source via sourceTag()
"the detail list has a Registered row built by registeredText()",
);
assert.match(rowPair[0], /esc\(rec\.agent\) \+ "<\/button>" \+ modelTag\(rec\)/, "the agent cell shows the model under the name");
assert.match(rowPair[0], /<dt>Model<\/dt><dd>" \+ modelText\(rec\) \+ "<\/dd>/, "the detail list has a Model row");
for (const name of ["modelTag", "modelText"]) {
const line = html.split("\n").find((l) => l.includes(`function ${name}(rec)`));
assert.ok(line, `${name}() must exist in page.html`);
assert.match(line, /esc\(rec\.model\)/, `${name}() escapes the model`);
if (name === "modelText") assert.match(line, /esc\(rec\.provider\)/, "modelText() escapes the provider");
}
const sourceLabelMatch = html.match(/var SOURCE_LABEL = (\{.*\});/);
assert.ok(sourceLabelMatch, "SOURCE_LABEL must exist in page.html");
const sourceLabel = JSON.parse(sourceLabelMatch[1]);