From b269622e7081ef15bc24eca866cc487cd28aaebd Mon Sep 17 00:00:00 2001 From: Jose Meneses Date: Sun, 23 Aug 2026 10:27:52 -0400 Subject: [PATCH 01/11] fix(bearings): expose truncated board text --- .../bearings/assets/board-template.html | 23 ++++--- tests/fm-bearings-board.test.sh | 66 +++++++++++++++++++ 2 files changed, 80 insertions(+), 9 deletions(-) diff --git a/.agents/skills/bearings/assets/board-template.html b/.agents/skills/bearings/assets/board-template.html index 786d14e4249..f2b87b785e0 100644 --- a/.agents/skills/bearings/assets/board-template.html +++ b/.agents/skills/bearings/assets/board-template.html @@ -434,7 +434,12 @@ if (text != null) n.textContent = text; return n; } - function badge(tone, text) { return el("span", "fm-badge fm-badge--" + tone, text); } + function titledEl(tag, cls, text) { + var n = el(tag, cls, text); + n.title = text; + return n; + } + function badge(tone, text) { return titledEl("span", "fm-badge fm-badge--" + tone, text); } function utf8ByteLength(text) { return new TextEncoder().encode(text).length; } var CHECK_SVG = ''; @@ -477,7 +482,7 @@ top.appendChild(badge(item.risk === "low" ? "neutral" : "warn", "risk " + item.risk)); } else { top.appendChild(badge("solid", "decision")); - top.appendChild(el("span", "bb-decision__repo", item.repo)); + top.appendChild(titledEl("span", "bb-decision__repo", item.repo)); } pad.appendChild(top); @@ -486,7 +491,7 @@ if (item.type === "merge") { if (item.detail) pad.appendChild(el("p", "bb-decision__detail", item.detail)); if (item.pr_url) { - var pr = el("a", "bb-decision__link", item.pr_url); + var pr = titledEl("a", "bb-decision__link", item.pr_url); pr.href = item.pr_url; pr.target = "_blank"; pr.rel = "noopener"; pad.appendChild(pr); } @@ -607,10 +612,10 @@ var row = el("div", "bb-row"); row.appendChild(badge(t.state === "working" ? "online" : "info", t.state)); var main = el("div", "bb-row__main"); - main.appendChild(el("div", "bb-row__title", t.doing)); + main.appendChild(titledEl("div", "bb-row__title", t.doing)); /* captain-facing rows name the repo; the internal task id shows only when no repo is known */ - main.appendChild(el("div", "bb-row__sub", t.kind + " · " + (t.repo || t.id))); + main.appendChild(titledEl("div", "bb-row__sub", t.kind + " · " + (t.repo || t.id))); row.appendChild(main); uw.appendChild(row); }); @@ -624,8 +629,8 @@ chk.innerHTML = ''; row.appendChild(chk); var main = el("div", "bb-row__main"); - main.appendChild(el("div", "bb-row__title", t.what)); - main.appendChild(el("div", "bb-row__sub", (t.repo || t.id) + " · " + t.owner)); + main.appendChild(titledEl("div", "bb-row__title", t.what)); + main.appendChild(titledEl("div", "bb-row__sub", (t.repo || t.id) + " · " + t.owner)); row.appendChild(main); if (t.pr_url) { var a = el("a", "bb-row__pr", "#" + t.pr_url.split("/").pop()); @@ -672,10 +677,10 @@ row.appendChild(el("span", "bb-pick-spacer")); } var main = el("div", "bb-row__main"); - main.appendChild(el("div", "bb-row__title", t.title)); + main.appendChild(titledEl("div", "bb-row__title", t.title)); /* second line keeps the title uncrowded in the half-width column */ var chSub = t.repo || t.id; - main.appendChild(el("div", "bb-row__sub", t.reason ? t.reason + " · " + chSub : chSub)); + main.appendChild(titledEl("div", "bb-row__sub", t.reason ? t.reason + " · " + chSub : chSub)); row.appendChild(main); if (t.reason) row.appendChild(badge("warn", "waiting")); ch.appendChild(row); diff --git a/tests/fm-bearings-board.test.sh b/tests/fm-bearings-board.test.sh index a0807d87a7e..8dea817aa02 100644 --- a/tests/fm-bearings-board.test.sh +++ b/tests/fm-bearings-board.test.sh @@ -13,6 +13,14 @@ TMP_ROOT=$(fm_test_tmproot fm-bearings-board) command -v jq >/dev/null 2>&1 || { echo "skip: jq not found"; exit 0; } +find_chrome() { + local candidate + for candidate in google-chrome google-chrome-stable chromium chromium-browser; do + command -v "$candidate" 2>/dev/null && return 0 + done + return 1 +} + make_home() { # local home="$TMP_ROOT/$1" fakebin mkdir -p "$home/state" "$home/data" @@ -226,6 +234,63 @@ test_build_injects_binds_then_arms() { pass "build injects the payload, binds any-origin, then arms the source" } +test_rendered_truncated_text_has_full_native_tooltips() { + local home data board chrome dom marker escaped + chrome=$(find_chrome) || { printf '%s\n' "skip: Chrome or Chromium not found for rendered tooltip assertions"; return; } + home=$(make_home tooltips) + data="$home/payload.json" + board="$home/.lavish/bearings-board.html" + marker='Long & exact "captain-facing" value ' + write_valid_payload "$data" + jq --arg marker "$marker" ' + .captains_call[0].repo = ($marker + "decision repo") | + .captains_call[1].pr_url = "https://github.com/example/sample/pull/12345678901234567890?view=full&mode=review" | + .underway = [{ + "id": ($marker + "underway id"), + "repo": "", + "kind": "ship", + "state": ($marker + "working badge"), + "doing": ($marker + "underway title") + }] | + .landed = [{ + "id": ($marker + "landed id"), + "repo": "", + "what": ($marker + "landed title"), + "owner": ($marker + "landed owner"), + "pr_url": "https://github.com/example/sample/pull/98765432109876543210?view=full&mode=review" + }] | + .charted[0].repo = "" | + .charted[0].id = ($marker + "charted id") | + .charted[0].title = ($marker + "charted title") | + .charted[0].reason = ($marker + "charted reason") + ' "$data" > "$data.tmp" && mv "$data.tmp" "$data" + + run_board "$home" build "$data" >/dev/null || fail "the long-text tooltip board did not build" + "$chrome" --headless --disable-gpu --no-sandbox --dump-dom "file://$board" > "$home/dom.html" 2>/dev/null \ + || fail "Chrome could not render the long-text tooltip board" + dom="$home/dom.html" + + for escaped in \ + 'Long & exact "captain-facing" value <must stay text> decision repo' \ + 'https://github.com/example/sample/pull/12345678901234567890?view=full&mode=review' \ + 'Long & exact "captain-facing" value <must stay text> working badge' \ + 'Long & exact "captain-facing" value <must stay text> underway title' \ + 'ship · Long & exact "captain-facing" value <must stay text> underway id' \ + 'Long & exact "captain-facing" value <must stay text> landed title' \ + 'Long & exact "captain-facing" value <must stay text> landed id · Long & exact "captain-facing" value <must stay text> landed owner' \ + 'https://github.com/example/sample/pull/98765432109876543210?view=full&mode=review' \ + 'Long & exact "captain-facing" value <must stay text> charted title' \ + 'Long & exact "captain-facing" value <must stay text> charted reason · Long & exact "captain-facing" value <must stay text> charted id'; do + grep -Fq "title=\"$escaped\"" "$dom" \ + || fail "the rendered board omitted or altered a full native tooltip: $escaped" + done + grep -Fq '<must stay text>' "$dom" \ + || fail "the tooltip payload was not HTML-escaped in the rendered board" + ! grep -Fq '' "$dom" \ + || fail "the tooltip payload became live markup in the rendered board" + pass "rendered truncation-prone text exposes exact escaped native tooltips" +} + test_registration_cannot_consume_before_any_origin_binding() { local home data runtime origin key hold board sid show home=$(make_home order-proof) @@ -373,6 +438,7 @@ test_build_refuses_a_template_without_exactly_one_slot() { test_path_is_stable_and_home_scoped test_build_refuses_malformed_payloads_before_touching_the_board test_build_injects_binds_then_arms +test_rendered_truncated_text_has_full_native_tooltips test_registration_cannot_consume_before_any_origin_binding test_build_does_not_bind_or_arm_when_session_start_fails test_rebuild_is_idempotent_and_does_not_double_arm From 0b71e57f735cf0b1c930b0dd9b783a16e304f4bc Mon Sep 17 00:00:00 2001 From: Jose Meneses Date: Sun, 23 Aug 2026 10:45:34 -0400 Subject: [PATCH 02/11] no-mistakes(review): guard null tooltips; assert them via headless-free renderer --- .../bearings/assets/board-template.html | 2 +- tests/fm-bearings-board.test.sh | 291 +++++++++++++++--- 2 files changed, 242 insertions(+), 51 deletions(-) diff --git a/.agents/skills/bearings/assets/board-template.html b/.agents/skills/bearings/assets/board-template.html index f2b87b785e0..aa3e9fd2cf4 100644 --- a/.agents/skills/bearings/assets/board-template.html +++ b/.agents/skills/bearings/assets/board-template.html @@ -436,7 +436,7 @@ } function titledEl(tag, cls, text) { var n = el(tag, cls, text); - n.title = text; + if (text != null) n.title = text; return n; } function badge(tone, text) { return titledEl("span", "fm-badge fm-badge--" + tone, text); } diff --git a/tests/fm-bearings-board.test.sh b/tests/fm-bearings-board.test.sh index 8dea817aa02..97a3920bda1 100644 --- a/tests/fm-bearings-board.test.sh +++ b/tests/fm-bearings-board.test.sh @@ -13,14 +13,6 @@ TMP_ROOT=$(fm_test_tmproot fm-bearings-board) command -v jq >/dev/null 2>&1 || { echo "skip: jq not found"; exit 0; } -find_chrome() { - local candidate - for candidate in google-chrome google-chrome-stable chromium chromium-browser; do - command -v "$candidate" 2>/dev/null && return 0 - done - return 1 -} - make_home() { # local home="$TMP_ROOT/$1" fakebin mkdir -p "$home/state" "$home/data" @@ -112,6 +104,134 @@ extract_payload() { # | sed '1d;$d' } +# Execute the shipped renderer from a built board page against a minimal DOM +# shim and print the rendered node state as JSON, so tooltip behavior is +# asserted from what the renderer actually produced rather than from a headless +# browser that the runner may not have. The board page is generated public +# output and the renderer is the real consumer under test. +render_board_nodes() { # + BOARD_PAGE="$1" node --input-type=module <<'JS' +import { readFileSync } from "node:fs"; + +const html = readFileSync(process.env.BOARD_PAGE, "utf8"); +const dataBlock = html.match( + /