From 2e849a0bc6b0fc9a2b3a063617f2346f9babd776 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 4 Sep 2026 20:45:54 +0000 Subject: [PATCH 1/2] feat(desk): a Repo health section on the overview, from the repo-standard conformance snapshot (#81) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The desk answers what to pick up, what is spoken for and what is open. It could not answer which repos run the standard CI, and whether it passes — the question .github-private#912 asked, and the one #485 filed for the fleet feed and never started. That answer now exists as a public snapshot, published daily by bounded-systems/.github's repo-standard-conformance lane. A fourth section, the same shape as the other three: selectCi refuses any feed not named repo-standard-conformance or without a parseable generated_at, carries the lane's totals through untouched, and lists the repos with findings worst first, each finding as a sentence. The Worker reads CI_FEED_URL once per overview request beside the other two feeds and fails closed per section: an unreadable snapshot keeps its slot, says why, and the page still serves 5xx. The summary line says the denominator out loud — how many repos call the standard, how many are green, how many could not be measured — because "42 with findings" alone hides the fact that matters. Rows are repos, not issues, so row() learned to omit the number tail when there is none, rather than printing null. No host of its own yet; the section links to the snapshot. Nothing here ranks, scores, or re-counts. Rendered against the live feeds before commit: 200, section present. Closes #81 Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01CAE3i1NCfwwSVYScKWvEdd --- README.md | 4 ++ src/render.js | 41 +++++++++++++++--- src/select.js | 98 +++++++++++++++++++++++++++++++++++++++-- src/worker.js | 7 ++- test/render.test.mjs | 39 +++++++++++++++++ test/select.test.mjs | 101 ++++++++++++++++++++++++++++++++++++++++--- test/worker.test.mjs | 62 ++++++++++++++++++++++++-- wrangler.jsonc | 6 +++ 8 files changed, 338 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 9a4e427..cdfc18e 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,10 @@ board **at request time**, across four hosts. | [`prs.bounded.tools`](https://prs.bounded.tools) | what is open and awaiting a check | `selectPrs` | | [`desk.bounded.tools`](https://desk.bounded.tools) | all three at a glance — the front door | `selectOverview` | +The front door also carries a fourth section, **Repo health** (#81): which public repos run +the org's standard CI and whether it passes, read from the snapshot `bounded-systems/.github` +publishes daily (`selectCi`). It has no host of its own yet, so it links to the snapshot. + One Worker, selected by hostname, because the selection rules **are** the product: four Workers would be four deploys, four broker entries, and four chances for "claimable" to come to mean four different things. diff --git a/src/render.js b/src/render.js index 0822f19..372ffa2 100644 --- a/src/render.js +++ b/src/render.js @@ -165,9 +165,11 @@ const row = ({ marker, markerLabel = "", title, url, repo, number, noun, suffix ? `${esc(c.subject)} ${esc(c.delta)}` : esc(title) } - ${vh(" — ")}${esc(shortRepo(repo))}${vh( - noun + " ", - )}${esc(number)}${suffix ? `${esc(suffix)}` : ""} + ${vh(" — ")}${esc(shortRepo(repo))}${ + number == null + ? "" + : `${vh(noun + " ")}${esc(number)}` + }${suffix ? `${esc(suffix)}` : ""} `; }; @@ -663,20 +665,44 @@ const SECTION_COPY = { markerLabel: "Status", noun: "issue" }, prs: { title: "PRs", label: "open", blurb: "changes awaiting a check", markerLabel: "Pull request", noun: "pull request" }, + ci: { title: "Repo health", label: "with findings", + blurb: "which public repos run the standard CI, and whether it passes — measured daily by the standard's own repo", + markerLabel: "Findings", noun: "repo" }, }; const EMPTY_COPY = { issues: "Nothing claimable right now.", claims: "Nothing is claimed right now.", prs: "No open pull requests.", + ci: "Every public repo calls the standard CI, and every standard run is green.", }; +/** + * The repo-health denominator, said out loud (desk#81). "42 with findings" on + * its own hides the fact that matters most — how many repos call the standard + * at all, and whether the ones that do are green — so the section carries the + * lane's own totals and this turns them into one sentence. Gaps are named as + * gaps: "could not be measured" is not "healthy", and the lane keeps them in a + * separate field for exactly this reason. + */ +function ciSummary(s) { + const t = s.totals; + if (!t || !t.caller || !t.standard_run) return ""; + const rows = t.rows ?? "?"; + const gaps = t.gaps ? ` ${esc(t.gaps)} could not be measured.` : ""; + const selftest = s.standard ? ` The standard's own selftest is ${esc(s.standard)}.` : ""; + return `

${esc(t.caller.present)} of ${esc(rows)} public repos call the standard: ${esc(t.standard_run.green)} green, ${esc(t.standard_run.red)} red. ${esc(t.caller.absent)} do not call it.${gaps}${selftest}

`; +} + function overviewSection(s) { const copy = SECTION_COPY[s.key] || { title: s.key, label: "", blurb: "", markerLabel: "", noun: "item" }; + // A section with no host of its own links wherever its feed lives (repo + // health links to the snapshot itself until a `ci.` host exists). + const link = s.href || `https://${s.host}`; // The heading carries the id the
points at, so each section is a // NAMED landmark rather than three anonymous regions a reader has to count. const heading = `
-

${esc(copy.title)}

+

${esc(copy.title)}

${s.ok ? `${esc(s.count)} ${esc(copy.label)}` : "unreadable"}

`; @@ -689,7 +715,7 @@ function overviewSection(s) { ${heading}
This section could not be read. It is not empty — the feed behind - ${esc(s.host)} did not answer in a way this page can stand behind, + ${esc(s.host)} did not answer in a way this page can stand behind, so nothing is shown rather than a count that would be made up.

${esc(s.reason)}

@@ -726,12 +752,13 @@ function overviewSection(s) { const more = !s.count ? "" : s.count > s.items.length - ? `

Showing the first ${esc(s.items.length)} of ${esc(s.count)} — the rest are at ${esc(s.host)}.

` - : `

All of them, in full, at ${esc(s.host)}.

`; + ? `

Showing the first ${esc(s.items.length)} of ${esc(s.count)} — the rest are at ${esc(s.host)}.

` + : `

All of them, in full, at ${esc(s.host)}.

`; return `
${heading}

${esc(copy.blurb)}

+ ${s.key === "ci" ? ciSummary(s) : ""} ${body} ${more}
`; diff --git a/src/select.js b/src/select.js index d459187..9acc18e 100644 --- a/src/select.js +++ b/src/select.js @@ -5,7 +5,8 @@ // issues.bounded.tools what is worth picking up select() // claims.bounded.tools what someone is already on selectClaims() // prs.bounded.tools what is open and awaiting a check selectPrs() -// desk.bounded.tools all three at a glance selectOverview() +// desk.bounded.tools all three at a glance, plus selectOverview() +// repo health from the standard CI selectCi() // // THE RANK IS THE BOARD'S. Nothing here scores. `Score` is carried through // unchanged and only sorted on; a ranking computed here would be a different @@ -246,8 +247,87 @@ export function selectPrs(feed) { }; } +// ── repo health (desk#81) ──────────────────────────────────────────────────── + +/** + * Where the repo-standard conformance snapshot is published — `.github`#381's + * lane, daily, as main plus one API commit on a branch of the repo that owns + * the standard. Until a host of its own exists, the section links here. + */ +export const CI_SNAPSHOT_URL = + "https://raw.githubusercontent.com/bounded-systems/.github/repo-standard-conformance/repo-standard-conformance.json"; +export const CI_SECTION_HOST = "github.com/bounded-systems/.github"; + +/** + * The finding codes the conformance lane emits, as sentences. A code this file + * does not know passes through AS WRITTEN rather than being dropped: the lane + * may grow a finding before this page learns its name, and an unnamed finding + * is still a finding. + */ +export const FINDING_COPY = { + "caller-absent": "does not call the standard CI", + "pin-not-sha": "calls the standard at a ref that is not a commit SHA", + "pull-request-missing": "the caller has no pull_request trigger", + "pull-request-filtered": "the caller's pull_request trigger is path-filtered, so it does not report on every PR", + "pull-request-no-synchronize": "the caller's pull_request trigger does not re-run on a push", + "test-lane-absent": "carries a toolchain but no test lane — its tests, if any, gate nothing", + "standard-run-red": "the latest standard run on its default branch is red", +}; + /** - * Compose the three selections into the front door — desk.bounded.tools. + * Reduce the conformance snapshot to the repos with findings — the fourth + * section of desk.bounded.tools. + * + * NOTHING IS RE-COUNTED. `totals` are the lane's own and are carried through + * as published; this page sorts and truncates. A FINDING is the repo's (no + * caller, an unpinned ref, a red run); a GAP is the lane's (a listing it could + * not read). The lane keeps them in separate fields and never sums them, and + * neither does this — `count` is repos with findings, and the gaps ride along + * in `totals` for the summary line to say out loud. + * + * Worst first: most findings, then name — the same order the lane publishes. + */ +export function selectCi(feed) { + requireBoardFeed( + feed, + "repo-standard-conformance", + "Only the conformance snapshot may be rendered as repo health — any other feed is the wrong page's data.", + ); + + const repos = Array.isArray(feed.repos) ? feed.repos : []; + const t = feed.totals && typeof feed.totals === "object" ? feed.totals : {}; + const flagged = repos.filter((r) => Array.isArray(r.findings) && r.findings.length > 0); + const sorted = [...flagged].sort( + (a, b) => b.findings.length - a.findings.length || String(a.repo).localeCompare(String(b.repo)), + ); + + return { + generated_at: feed.generated_at, + href: CI_SNAPSHOT_URL, + count: sorted.length, + totals: { + rows: t.rows ?? null, + caller: t.caller ?? null, + standard_run: t.standard_run ?? null, + test_lane: t.test_lane ?? null, + findings: t.findings ?? null, + gaps: t.gaps ?? null, + }, + // The one "is the org CI good" signal that exists today: the standard's + // own selftest on main (`.github`#382 is what it still lacks). + standard: feed.standard?.selftest?.state ?? null, + items: sorted.map((r) => ({ + repo: r.repo, + url: `https://github.com/${r.repo}`, + findings: r.findings, + summary: r.findings.map((f) => FINDING_COPY[f] || f).join("; "), + standard_run: r.standard_run?.state ?? null, + })), + }; +} + +/** + * Compose the four selections into the front door — desk.bounded.tools. * * TAKES OUTCOMES, NOT FEEDS, and that is the point: each section is fetched and * selected independently, so this function is where "one of the three could not @@ -266,7 +346,7 @@ export function selectPrs(feed) { * make one page's "12 claimed" mean the same as another's is for both to be the * same expression. */ -export function selectOverview({ issues, claims, prs }, head = OVERVIEW_HEAD) { +export function selectOverview({ issues, claims, prs, ci }, head = OVERVIEW_HEAD) { const section = (key, host, outcome, shape) => outcome?.ok ? { key, host, ok: true, ...shape(outcome.value), generated_at: outcome.value.generated_at } @@ -296,6 +376,18 @@ export function selectOverview({ issues, claims, prs }, head = OVERVIEW_HEAD) { repo: i.repo, number: i.number, title: i.title, url: i.url, note: `#${i.number}`, })), })), + // Repo health (desk#81). No host of its own yet, so `href` points at the + // snapshot; rows are repos, not issues, so they carry no number. + section("ci", CI_SECTION_HOST, ci, (d) => ({ + count: d.count, + shown: d.items.length, + href: d.href, + totals: d.totals, + standard: d.standard, + items: d.items.slice(0, head).map((i) => ({ + repo: i.repo, number: null, title: i.summary, url: i.url, note: String(i.findings.length), + })), + })), ]; // The OLDEST readable stamp, not the newest. The page shows three feeds side diff --git a/src/worker.js b/src/worker.js index 98a04a7..994facc 100644 --- a/src/worker.js +++ b/src/worker.js @@ -34,6 +34,7 @@ import { select, selectClaims, selectPrs, + selectCi, selectOverview, DEFAULT_LIMIT, FeedError, @@ -1335,9 +1336,12 @@ export default { // No destination is baked in: where the filtered feeds are published is a // maintainer decision (see site#241), so they arrive as configuration and // an absent one is reported rather than guessed at. - const [board, prsFeed] = await Promise.all([ + const [board, prsFeed, ciFeed] = await Promise.all([ readFeed(env.FEED_URL, "FEED_URL"), readFeed(env.PRS_FEED_URL, "PRS_FEED_URL"), + // Repo health (desk#81): the conformance snapshot the standard's own + // repo publishes. Fails closed per section like the other two. + readFeed(env.CI_FEED_URL, "CI_FEED_URL"), ]); // Both issue-side sections read the SAME feed — one origin read, and the // two pages can never disagree about which snapshot they are describing. @@ -1345,6 +1349,7 @@ export default { issues: selected(board, (f) => select(f, limit)), claims: selected(board, selectClaims), prs: selected(prsFeed, selectPrs), + ci: selected(ciFeed, selectCi), }); const status = overview.ok ? 200 : 502; return wantsJson diff --git a/test/render.test.mjs b/test/render.test.mjs index b61f2fb..b3b4657 100644 --- a/test/render.test.mjs +++ b/test/render.test.mjs @@ -308,12 +308,27 @@ const section = (key, host, o = {}) => ({ items: [{ repo: "bounded-systems/prx", number: 434, title: `${key} row`, url: "https://e/1", note: "n" }], ...o, }); +// Repo health (desk#81): rows are repos, not issues — no number — and the +// section carries the lane's totals for the summary sentence. +const ciSection = (o = {}) => section("ci", "github.com/bounded-systems/.github", { + href: "https://raw.example/ci.json", + totals: { + rows: 90, caller: { present: 49, absent: 40, unreadable: 1 }, + standard_run: { green: 49, red: 0, other: 0, none: 0, unreadable: 0 }, findings: 42, gaps: 3, + }, + standard: "green", + count: 42, shown: 1, + items: [{ repo: "bounded-systems/bare", number: null, title: "does not call the standard CI", + url: "https://github.com/bounded-systems/bare", note: "1" }], + ...o, +}); const overview = (o = {}) => ({ ok: true, generated_at: "2026-08-25T12:00:00Z", head: 5, sections: [ section("issues", "issues.bounded.tools"), section("claims", "claims.bounded.tools"), section("prs", "prs.bounded.tools"), + ciSection(), ], ...o, }); @@ -692,3 +707,27 @@ test("no motion ships without a reduced-motion guard", async () => { } assert.equal(declared, guarded, `${declared} motion declarations, ${guarded} inside a reduced-motion guard`); }); + +// ── repo health on the overview (desk#81) ──────────────────────────────────── + +test("the repo-health section links to its snapshot, says its denominator, and rows carry no number", () => { + const html = renderOverview(overview(), AT, 60); + assert.match(html, /Repo health/); + assert.ok(html.includes('href="https://raw.example/ci.json"'), "links to the href, not a host"); + assert.match(html, /49 of 90 public repos call the standard: 49 green, 0 red\. 40 do not call it\. 3 could not be measured\. The standard's own selftest is green\.|49 of 90 public repos call the standard: 49 green, 0 red\. 40 do not call it\. 3 could not be measured\. The standard's own selftest is green\./); + assert.match(html, /does not call the standard CI/); + assert.match(html, /42 with findings/); + assert.doesNotMatch(html, /repo null/); + assert.doesNotMatch(html, /issue null/); +}); + +test("a repo-health section with nothing flagged says so, and only then", () => { + const html = renderOverview(overview({ sections: [ciSection({ count: 0, items: [] })] }), AT, 60); + assert.match(html, /every standard run is green/); + const failed = renderOverview(overview({ + ok: false, + sections: [{ key: "ci", host: "github.com/bounded-systems/.github", ok: false, reason: "feed responded 504", count: null, items: [] }], + }), AT, 60); + assert.doesNotMatch(failed, /every standard run is green/); + assert.match(failed, /feed responded 504/); +}); diff --git a/test/select.test.mjs b/test/select.test.mjs index f5117b4..45714b5 100644 --- a/test/select.test.mjs +++ b/test/select.test.mjs @@ -1,8 +1,8 @@ import { test } from "node:test"; import assert from "node:assert/strict"; import { - select, selectPrs, selectClaims, selectOverview, - FeedError, DEFAULT_LIMIT, OVERVIEW_HEAD, + select, selectPrs, selectClaims, selectOverview, selectCi, + FeedError, DEFAULT_LIMIT, OVERVIEW_HEAD, CI_SNAPSHOT_URL, FINDING_COPY, } from "../src/select.js"; const item = (o = {}) => ({ @@ -256,20 +256,50 @@ const boardFeed = feed([ item({ number: 2, fields: { Status: "Todo", Score: 9 } }), claimed({ number: 3, fields: { Status: "In Progress", Score: 1 } }), ]); +// A conformance snapshot the way `.github`'s lane publishes it (desk#81): +// totals are the lane's, rows carry findings (the repo's) and gaps (the lane's). +const ciRepo = (o = {}) => ({ + repo: "bounded-systems/x", findings: [], gaps: [], + caller: { state: "present" }, standard_run: { state: "green" }, extra: [], + ...o, +}); +const ciFeed = (repos = [ + ciRepo({ repo: "bounded-systems/bare", findings: ["caller-absent"], caller: { state: "absent" }, standard_run: null }), + ciRepo({ repo: "bounded-systems/clean" }), +], o = {}) => ({ + feed: "repo-standard-conformance", generated_at: "2026-09-04T20:38:14Z", + totals: { + rows: 2, caller: { present: 1, absent: 1, unreadable: 0 }, + standard_run: { green: 1, red: 0, other: 0, none: 0, unreadable: 0 }, + test_lane: { present: 1, absent: 1, "n/a": 0, unmeasured: 0 }, findings: 1, gaps: 0, + }, + standard: { selftest: { state: "green" } }, + repos, ...o, +}); const outcomes = (o = {}) => ({ issues: ok(select(boardFeed)), claims: ok(selectClaims(boardFeed)), prs: ok(selectPrs(prFeed([prItem({ number: 4 })]))), + ci: ok(selectCi(ciFeed())), ...o, }); -test("the overview carries all three sections, in reading order", () => { +test("the overview carries all four sections, in reading order", () => { const r = selectOverview(outcomes()); - assert.deepEqual(r.sections.map((s) => s.key), ["issues", "claims", "prs"]); + assert.deepEqual(r.sections.map((s) => s.key), ["issues", "claims", "prs", "ci"]); assert.deepEqual(r.sections.map((s) => s.host), [ - "issues.bounded.tools", "claims.bounded.tools", "prs.bounded.tools", + "issues.bounded.tools", "claims.bounded.tools", "prs.bounded.tools", "github.com/bounded-systems/.github", ]); assert.equal(r.ok, true); + // Repo health has no host of its own yet, so it says where its feed lives. + assert.equal(r.sections[3].href, CI_SNAPSHOT_URL); +}); + +test("a missing repo-health outcome fails the overview closed, like any other section", () => { + const r = selectOverview(outcomes({ ci: undefined })); + const ci = r.sections.find((s) => s.key === "ci"); + assert.equal(ci.ok, false); + assert.equal(r.ok, false); }); // The whole point of composing rather than re-counting: the overview's number @@ -280,6 +310,7 @@ test("every count comes from the selector that owns it", () => { assert.equal(by.issues.count, select(boardFeed).items.length); assert.equal(by.claims.count, selectClaims(boardFeed).count); assert.equal(by.prs.count, 1); + assert.equal(by.ci.count, selectCi(ciFeed()).count); }); test("a section that could not be read keeps its slot and its reason", () => { @@ -301,12 +332,13 @@ test("the overview's age is the OLDEST readable stamp, not the newest", () => { issues: ok(select(feed([], { generated_at: "2026-08-25T10:00:00Z" }))), claims: ok(selectClaims(feed([], { generated_at: "2026-08-25T10:00:00Z" }))), prs: ok(selectPrs(prFeed([], { generated_at: "2026-08-27T10:00:00Z" }))), + ci: ok(selectCi(ciFeed([], { generated_at: "2026-08-26T10:00:00Z" }))), }); assert.equal(r.generated_at, "2026-08-25T10:00:00Z"); }); test("an overview with nothing readable states no age rather than inventing one", () => { - const r = selectOverview({ issues: bad("x"), claims: bad("x"), prs: bad("x") }); + const r = selectOverview({ issues: bad("x"), claims: bad("x"), prs: bad("x"), ci: bad("x") }); assert.equal(r.generated_at, null); assert.equal(r.ok, false); }); @@ -316,9 +348,66 @@ test("each section shows only its head, and says how many it counted", () => { item({ number: n, fields: { Status: "Todo", Score: n } }))); const r = selectOverview({ issues: ok(select(many)), claims: ok(selectClaims(many)), prs: ok(selectPrs(prFeed([]))), + ci: ok(selectCi(ciFeed())), }); const issues = r.sections.find((s) => s.key === "issues"); assert.equal(issues.items.length, OVERVIEW_HEAD); assert.equal(issues.count, 12); assert.equal(r.head, OVERVIEW_HEAD); }); + +// ── selectCi — repo health (desk#81) ───────────────────────────────────────── + +test("selectCi refuses any feed that is not repo-standard-conformance", () => { + assert.throws(() => selectCi(feed([])), FeedError); + assert.throws(() => selectCi(prFeed([])), FeedError); + assert.throws(() => selectCi(ciFeed([], { feed: "front-desk-public" })), /expected the 'repo-standard-conformance' feed/); +}); + +test("selectCi refuses a snapshot it cannot date", () => { + assert.throws(() => selectCi(ciFeed([], { generated_at: "yesterday" })), FeedError); +}); + +test("selectCi lists only repos with findings, worst first, as sentences", () => { + const r = selectCi(ciFeed([ + ciRepo({ repo: "bounded-systems/clean" }), + ciRepo({ repo: "bounded-systems/b", findings: ["caller-absent"] }), + ciRepo({ repo: "bounded-systems/a", findings: ["pin-not-sha", "pull-request-filtered"] }), + ])); + assert.equal(r.count, 2); + assert.deepEqual(r.items.map((i) => i.repo), ["bounded-systems/a", "bounded-systems/b"]); + assert.equal(r.items[1].summary, FINDING_COPY["caller-absent"]); + assert.match(r.items[0].summary, /not a commit SHA; .*path-filtered/); + assert.equal(r.items[0].url, "https://github.com/bounded-systems/a"); + assert.equal(r.href, CI_SNAPSHOT_URL); +}); + +test("selectCi carries the lane's totals through, and never re-counts them", () => { + // The totals say 5 findings; the rows carry 1. The page shows the lane's + // number as the lane's number and its own count as its own — two facts, + // not one reconciled by this file. + const r = selectCi(ciFeed(undefined, { totals: { ...ciFeed().totals, findings: 5, gaps: 3 } })); + assert.equal(r.totals.findings, 5); + assert.equal(r.totals.gaps, 3); + assert.equal(r.count, 1); + assert.deepEqual(r.totals.caller, { present: 1, absent: 1, unreadable: 0 }); + assert.equal(r.standard, "green"); +}); + +test("a finding code this page does not know passes through as written", () => { + const r = selectCi(ciFeed([ciRepo({ repo: "bounded-systems/n", findings: ["something-new"] })])); + assert.equal(r.items[0].summary, "something-new"); +}); + +test("a snapshot with no findings is a real answer, not an error", () => { + const r = selectCi(ciFeed([ciRepo({ repo: "bounded-systems/clean" })])); + assert.equal(r.count, 0); + assert.deepEqual(r.items, []); +}); + +test("a snapshot predating totals or repos degrades to empty rather than throwing", () => { + const r = selectCi({ feed: "repo-standard-conformance", generated_at: "2026-09-04T20:38:14Z" }); + assert.equal(r.count, 0); + assert.equal(r.totals.rows, null); + assert.equal(r.standard, null); +}); diff --git a/test/worker.test.mjs b/test/worker.test.mjs index ade31b8..8aa61a6 100644 --- a/test/worker.test.mjs +++ b/test/worker.test.mjs @@ -27,9 +27,27 @@ const PRS = { items: [{ repo: "bounded-systems/prx", number: 7, title: "a change", url: "https://e/7", labels: [], claimed: false }], }; +// The repo-standard conformance snapshot (desk#81), the way `.github`'s lane +// publishes it: one repo with a finding, one clean. +const CI = { + feed: "repo-standard-conformance", + generated_at: new Date().toISOString(), + totals: { + rows: 2, caller: { present: 1, absent: 1, unreadable: 0 }, + standard_run: { green: 1, red: 0, other: 0, none: 0, unreadable: 0 }, + test_lane: { present: 1, absent: 1, "n/a": 0, unmeasured: 0 }, findings: 1, gaps: 0, + }, + standard: { selftest: { state: "green" } }, + repos: [ + { repo: "bounded-systems/bare", findings: ["caller-absent"], gaps: [], caller: { state: "absent" }, standard_run: null, extra: [] }, + { repo: "bounded-systems/prx", findings: [], gaps: [], caller: { state: "present" }, standard_run: { state: "green" }, extra: [] }, + ], +}; + const ENV = { FEED_URL: "https://feed.example/board.json", PRS_FEED_URL: "https://feed.example/prs.json", + CI_FEED_URL: "https://feed.example/ci.json", DESK_LIMIT: "25", }; @@ -37,9 +55,9 @@ let realFetch; /** Serve each configured feed, or fail the one the test names. */ function stubFeeds({ fail = null, status = 500 } = {}) { globalThis.fetch = async (url) => { - const which = url === ENV.FEED_URL ? "board" : "prs"; + const which = url === ENV.FEED_URL ? "board" : url === ENV.CI_FEED_URL ? "ci" : "prs"; if (fail === which) return new Response("nope", { status, statusText: "Server Error" }); - return new Response(JSON.stringify(which === "board" ? BOARD : PRS), { + return new Response(JSON.stringify(which === "board" ? BOARD : which === "ci" ? CI : PRS), { status: 200, headers: { "content-type": "application/json" }, }); }; @@ -141,7 +159,7 @@ test("/board.json serves that host's selection", async () => { const claims = await (await get("claims.bounded.tools", "/board.json")).json(); assert.deepEqual(claims.items.map((i) => i.number), [2]); const overview = await (await get("desk.bounded.tools", "/board.json")).json(); - assert.deepEqual(overview.sections.map((s) => s.key), ["issues", "claims", "prs"]); + assert.deepEqual(overview.sections.map((s) => s.key), ["issues", "claims", "prs", "ci"]); }); test("/healthz does not touch the feed, so it answers when the feed does not", async () => { @@ -190,6 +208,44 @@ test("the overview renders what it could read and 502s on what it could not", as assert.doesNotMatch(html, /No open pull requests/); }); +// ── repo health on the overview (desk#81) ──────────────────────────────────── + +test("the overview's repo-health section reads CI_FEED_URL and names the repos with findings", async () => { + const res = await get("desk.bounded.tools"); + assert.equal(res.status, 200); + const html = await res.text(); + assert.match(html, /Repo health/); + assert.match(html, /1 of 2 public repos call the standard: 1 green, 0 red\. 1 do not call it\./); + assert.match(html, /does not call the standard CI/); + assert.ok(html.includes("https://github.com/bounded-systems/bare"), "the row links to the repo"); + assert.doesNotMatch(html, /repo null/); +}); + +test("an unreadable conformance feed keeps its slot and 502s the overview", async () => { + stubFeeds({ fail: "ci" }); + const res = await get("desk.bounded.tools"); + assert.equal(res.status, 502); + const html = await res.text(); + assert.match(html, /pick me up/); // the board sections survived + assert.match(html, /This section could not be read/); + assert.doesNotMatch(html, /every standard run is green/); // never the empty sentence +}); + +test("the board feed served as CI_FEED_URL is refused, not rendered as repo health", async () => { + const inner = globalThis.fetch; + globalThis.fetch = async (url, init) => + url === ENV.CI_FEED_URL ? new Response(JSON.stringify(BOARD), { status: 200 }) : inner(url, init); + const res = await get("desk.bounded.tools"); + assert.equal(res.status, 502); + assert.match(await res.text(), /expected the 'repo-standard-conformance' feed/); +}); + +test("a missing CI_FEED_URL is the Worker's own fault, and says which var", async () => { + const res = await get("desk.bounded.tools", "/", { ...ENV, CI_FEED_URL: "" }); + assert.equal(res.status, 502); + assert.match(await res.text(), /CI_FEED_URL is not configured/); +}); + test("the overview reads the board feed once for both board-side sections", async () => { const seen = []; const inner = globalThis.fetch; diff --git a/wrangler.jsonc b/wrangler.jsonc index b727ff8..c3d4155 100644 --- a/wrangler.jsonc +++ b/wrangler.jsonc @@ -78,6 +78,12 @@ // (scripts/prs.sh) — the Worker refuses to render it anywhere but the prs // host, and refuses any feed that does not name itself front-desk-prs-public. "PRS_FEED_URL": "https://raw.githubusercontent.com/bounded-systems/front-desk-feed/feed/front-desk-prs.json", + // Repo health (desk#81): the repo-standard conformance snapshot, published + // daily by bounded-systems/.github (its #381 lane) as main plus one API + // commit on a branch of the repo that owns the standard. The Worker refuses + // to render it anywhere but the overview's repo-health section, and refuses + // any feed that does not name itself repo-standard-conformance. + "CI_FEED_URL": "https://raw.githubusercontent.com/bounded-systems/.github/repo-standard-conformance/repo-standard-conformance.json", "DESK_LIMIT": "25" // NOT HERE, and deliberately: SESSION_SECRET, the key desk login's session From 00a04573769aa08d7329302ab580e2da10a93aa8 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 4 Sep 2026 21:05:48 +0000 Subject: [PATCH 2/2] test(desk): assert the repo-health link as an href attribute; selectCi reads finding copy with hasOwn MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CodeQL flagged the bare-URL includes() in worker.test.mjs as incomplete URL substring sanitization (js/incomplete-url-substring-sanitization, high). The assertion meant the link target, so it now matches the href attribute, as the render tests already do — stronger, and no longer a sanitizer shape. Alongside: a finding code named like an Object.prototype member would have resolved to a function rather than passing through as text. hasOwn, and a case that pins it. Claim-issue: bounded-systems/desk#81 Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01CAE3i1NCfwwSVYScKWvEdd --- src/select.js | 5 ++++- test/select.test.mjs | 3 +++ test/worker.test.mjs | 4 +++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/select.js b/src/select.js index 9acc18e..3a136ea 100644 --- a/src/select.js +++ b/src/select.js @@ -320,7 +320,10 @@ export function selectCi(feed) { repo: r.repo, url: `https://github.com/${r.repo}`, findings: r.findings, - summary: r.findings.map((f) => FINDING_COPY[f] || f).join("; "), + // hasOwn, not a bare lookup: a code named like an Object.prototype member + // ("constructor", "toString") must read as the text it is, not resolve to + // a function whose source becomes the sentence. + summary: r.findings.map((f) => (Object.hasOwn(FINDING_COPY, f) ? FINDING_COPY[f] : String(f))).join("; "), standard_run: r.standard_run?.state ?? null, })), }; diff --git a/test/select.test.mjs b/test/select.test.mjs index 45714b5..b119f73 100644 --- a/test/select.test.mjs +++ b/test/select.test.mjs @@ -397,6 +397,9 @@ test("selectCi carries the lane's totals through, and never re-counts them", () test("a finding code this page does not know passes through as written", () => { const r = selectCi(ciFeed([ciRepo({ repo: "bounded-systems/n", findings: ["something-new"] })])); assert.equal(r.items[0].summary, "something-new"); + // …including one that happens to name an Object.prototype member. + const p = selectCi(ciFeed([ciRepo({ repo: "bounded-systems/p", findings: ["constructor", "toString"] })])); + assert.equal(p.items[0].summary, "constructor; toString"); }); test("a snapshot with no findings is a real answer, not an error", () => { diff --git a/test/worker.test.mjs b/test/worker.test.mjs index 8aa61a6..b3a0b2d 100644 --- a/test/worker.test.mjs +++ b/test/worker.test.mjs @@ -217,7 +217,9 @@ test("the overview's repo-health section reads CI_FEED_URL and names the repos w assert.match(html, /Repo health/); assert.match(html, /1 of 2 public repos call the standard: 1 green, 0 red\. 1 do not call it\./); assert.match(html, /does not call the standard CI/); - assert.ok(html.includes("https://github.com/bounded-systems/bare"), "the row links to the repo"); + // The attribute, not the bare URL: this asserts the LINK TARGET, and a bare + // URL substring is what CodeQL reads as sanitization (js/incomplete-url-substring-sanitization). + assert.ok(html.includes('href="https://github.com/bounded-systems/bare"'), "the row links to the repo"); assert.doesNotMatch(html, /repo null/); });