From 888390ce4701dd487d0b142cd6d2722af0fa6874 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 8 Sep 2026 18:47:01 +0000 Subject: [PATCH] Repo health: say how many repos merge on their own green, and name the two dark-factory findings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit .github#390 added dark_factory per row and totals.gate_ready to the conformance snapshot, plus two findings: gate-absent (a caller whose contexts nothing on the default branch requires — green gates nothing) and arming-lane-absent (gated, but nothing arms the merge — green waits for a person). The section rendered the slugs raw and said nothing about the count. FINDING_COPY gains the two sentences. ciSummary appends "N merge on their own green (gated on the standard and the claim, with the arming lane)." only when totals.gate_ready is an integer, so a snapshot older than #390 prints nothing rather than a zero the lane never measured. Nothing re-counted. Closes #85 Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01CAE3i1NCfwwSVYScKWvEdd --- src/render.js | 8 +++++++- src/select.js | 4 ++++ test/render.test.mjs | 20 ++++++++++++++++++++ test/select.test.mjs | 15 +++++++++++++++ 4 files changed, 46 insertions(+), 1 deletion(-) diff --git a/src/render.js b/src/render.js index 372ffa2..0af03ed 100644 --- a/src/render.js +++ b/src/render.js @@ -691,7 +691,13 @@ function ciSummary(s) { 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}

`; + // The dark-factory count is printed only when the lane measured it: a snapshot + // older than .github#390 carries no `gate_ready`, and "0 of N" would be a claim + // the lane never made. + const dark = Number.isInteger(t.gate_ready) + ? ` ${esc(t.gate_ready)} merge on their own green (gated on the standard and the claim, with the arming lane).` + : ""; + 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.${dark}${gaps}${selftest}

`; } function overviewSection(s) { diff --git a/src/select.js b/src/select.js index 3a136ea..bdfaca5 100644 --- a/src/select.js +++ b/src/select.js @@ -272,6 +272,10 @@ export const FINDING_COPY = { "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", + // The dark-factory pair (.github-private#913 step 4): the first is green that + // decides nothing, the second is green that waits for a person. + "gate-absent": "calls the standard, but nothing on its default branch requires it — green gates nothing", + "arming-lane-absent": "is gated, but nothing arms the merge — green waits for a person", }; /** diff --git a/test/render.test.mjs b/test/render.test.mjs index b3b4657..937b004 100644 --- a/test/render.test.mjs +++ b/test/render.test.mjs @@ -731,3 +731,23 @@ test("a repo-health section with nothing flagged says so, and only then", () => assert.doesNotMatch(failed, /every standard run is green/); assert.match(failed, /feed responded 504/); }); + +// ── repo health: the dark-factory count (desk#85) ──────────────────────────── + +test("the repo-health summary says how many repos merge on their own green — only when the lane measured it", () => { + // A snapshot from before .github#390 carries no gate_ready: no sentence, no "0 of". + const before = renderOverview(overview(), AT, 60); + assert.doesNotMatch(before, /merge on their own green/); + // A measured one prints the lane's count, untouched. + const after = renderOverview(overview({ + sections: [section("issues", "issues.bounded.tools"), section("claims", "claims.bounded.tools"), section("prs", "prs.bounded.tools"), + ciSection({ totals: { ...ciSection().totals, gated: 43, arming_lane: 37, gate_ready: 37 } })], + }), AT, 60); + assert.match(after, /37 merge on their own green \(gated on the standard and the claim, with the arming lane\)\./); + // Zero is a measurement too, and prints as one. + const none = renderOverview(overview({ + sections: [section("issues", "issues.bounded.tools"), section("claims", "claims.bounded.tools"), section("prs", "prs.bounded.tools"), + ciSection({ totals: { ...ciSection().totals, gate_ready: 0 } })], + }), AT, 60); + assert.match(none, /0 merge on their own green/); +}); diff --git a/test/select.test.mjs b/test/select.test.mjs index b119f73..04f17db 100644 --- a/test/select.test.mjs +++ b/test/select.test.mjs @@ -414,3 +414,18 @@ test("a snapshot predating totals or repos degrades to empty rather than throwin assert.equal(r.totals.rows, null); assert.equal(r.standard, null); }); + +// ── the dark-factory findings read as sentences (desk#85) ──────────────────── + +test("selectCi renders the two dark-factory findings as sentences, not slugs", () => { + const r = selectCi(ciFeed([ + ciRepo({ repo: "bounded-systems/ungated", findings: ["gate-absent"] }), + ciRepo({ repo: "bounded-systems/unarmed", findings: ["arming-lane-absent"] }), + ])); + const by = Object.fromEntries(r.items.map((i) => [i.repo.split("/")[1], i.summary])); + assert.equal(by.ungated, FINDING_COPY["gate-absent"]); + assert.equal(by.unarmed, FINDING_COPY["arming-lane-absent"]); + assert.match(by.ungated, /green gates nothing/); + assert.match(by.unarmed, /green waits for a person/); + assert.doesNotMatch(by.ungated + by.unarmed, /gate-absent|arming-lane-absent/, "no slug leaks into the sentence"); +});