Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
41 changes: 34 additions & 7 deletions src/render.js
Original file line number Diff line number Diff line change
Expand Up @@ -165,9 +165,11 @@ const row = ({ marker, markerLabel = "", title, url, repo, number, noun, suffix
? `<span class="row__subject">${esc(c.subject)}</span> <span class="row__delta">${esc(c.delta)}</span>`
: esc(title)
}</span>
<span class="row__where">${vh(" — ")}${esc(shortRepo(repo))}<span aria-hidden="true"> · </span>${vh(
noun + " ",
)}${esc(number)}${suffix ? `<span aria-hidden="true"> · </span>${esc(suffix)}` : ""}</span>
<span class="row__where">${vh(" — ")}${esc(shortRepo(repo))}${
number == null
? ""
: `<span aria-hidden="true"> · </span>${vh(noun + " ")}${esc(number)}`
}${suffix ? `<span aria-hidden="true"> · </span>${esc(suffix)}` : ""}</span>
</a>
</li>`;
};
Expand Down Expand Up @@ -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 `<p class="muted">${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}</p>`;
}

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 <section> points at, so each section is a
// NAMED landmark rather than three anonymous regions a reader has to count.
const heading = `<div class="sec__head">
<h2 id="${esc(s.key)}-h"><a href="https://${esc(s.host)}">${esc(copy.title)}</a></h2>
<h2 id="${esc(s.key)}-h"><a href="${esc(link)}">${esc(copy.title)}</a></h2>
<p class="sec__n">${s.ok ? `${esc(s.count)} ${esc(copy.label)}` : "unreadable"}</p>
</div>`;

Expand All @@ -689,7 +715,7 @@ function overviewSection(s) {
${heading}
<div class="stamp stamp--stale">
<strong>This section could not be read.</strong> It is not empty — the feed behind
<a href="https://${esc(s.host)}">${esc(s.host)}</a> did not answer in a way this page can stand behind,
<a href="${esc(link)}">${esc(s.host)}</a> did not answer in a way this page can stand behind,
so nothing is shown rather than a count that would be made up.
</div>
<p class="muted mono">${esc(s.reason)}</p>
Expand Down Expand Up @@ -726,12 +752,13 @@ function overviewSection(s) {
const more = !s.count
? ""
: s.count > s.items.length
? `<p class="muted sec__more">Showing the first ${esc(s.items.length)} of ${esc(s.count)} — the rest are at <a href="https://${esc(s.host)}">${esc(s.host)}</a>.</p>`
: `<p class="muted sec__more">All of them, in full, at <a href="https://${esc(s.host)}">${esc(s.host)}</a>.</p>`;
? `<p class="muted sec__more">Showing the first ${esc(s.items.length)} of ${esc(s.count)} — the rest are at <a href="${esc(link)}">${esc(s.host)}</a>.</p>`
: `<p class="muted sec__more">All of them, in full, at <a href="${esc(link)}">${esc(s.host)}</a>.</p>`;

return `<section class="sec" id="${esc(s.key)}" aria-labelledby="${esc(s.key)}-h">
${heading}
<p class="muted">${esc(copy.blurb)}</p>
${s.key === "ci" ? ciSummary(s) : ""}
${body}
${more}
</section>`;
Expand Down
101 changes: 98 additions & 3 deletions src/select.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -246,8 +247,90 @@ 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,
// 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,
})),
};
}

/**
* 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
Expand All @@ -266,7 +349,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 }
Expand Down Expand Up @@ -296,6 +379,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
Expand Down
7 changes: 6 additions & 1 deletion src/worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
select,
selectClaims,
selectPrs,
selectCi,
selectOverview,
DEFAULT_LIMIT,
FeedError,
Expand Down Expand Up @@ -1335,16 +1336,20 @@ 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.
const overview = selectOverview({
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
Expand Down
39 changes: 39 additions & 0 deletions test/render.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
});
Expand Down Expand Up @@ -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&#39;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/);
});
Loading
Loading