From d9b496c4ae4dad50d3584104c7bb549953626255 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 28 Aug 2026 07:51:07 +0000 Subject: [PATCH] feat(pm): re-enable H22 behind a dated closure floor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The half-state patrol shipped with its closed-card reader fully off (`PM_SWEEP_CLOSED_WINDOW_PAGES: '0'`). The measurement behind that hold stands: 815 closed cards here carry `pm:dispatched`, and ~347 of the 400 issues in the window carry some `pm:*` residue (~87%). At that density H22 reports the convention rather than a defect and exhausts the anchor body budget. The card framed the way out as two-way — backfill ~815 cards, or drop the row. The dated floor is the third option: judge only cards closed on/after the cutover. Historical `pm:*` on an old closed card is inert (the loop reads state on open cards only), so the row gets its whole value at zero backfill and zero historical noise. No bulk label backfill was run and none is owed; the sweeper writes no label under any code path. The floor is upstream code, ported — not a new hand divergence. A re-sync that replaces the sweeper with upstream's keeps it. What diverges is the wiring, and divergence 1 in the workflow header now documents a floor instead of an off-switch. The pinned suite's `is switched off by the workflow` case is REPLACED rather than respelled: it encoded the superseded ruling that stripping `pm:*` on close is not this lane's convention. --- .github/workflows/half-state-patrol.yml | 56 ++++-- scripts/__tests__/check-half-states.test.ts | 110 +++++++++--- scripts/pm/check-half-states.mjs | 189 +++++++++++++++++++- 3 files changed, 310 insertions(+), 45 deletions(-) diff --git a/.github/workflows/half-state-patrol.yml b/.github/workflows/half-state-patrol.yml index ae3e427aa3..acc7895b11 100644 --- a/.github/workflows/half-state-patrol.yml +++ b/.github/workflows/half-state-patrol.yml @@ -11,16 +11,39 @@ name: Half-State Patrol # Every divergence from upstream is listed here, once, so a future re-sync knows # exactly what it must not clobber: # -# 1. `PM_SWEEP_CLOSED_WINDOW_PAGES: 0` on the sweep step — H22's closed-card -# reader is OFF in this install. Measured 2026-08-24: 815 closed cards here -# carry `pm:dispatched`, and ~347 of the 400 issues in upstream's window -# carry some `pm:*` residue label (~87%, against the 26% upstream measured). -# Stripping `pm:*` on close was never this lane's practice, so H22 here -# reports the CONVENTION, not a defect — ~347 rows that would consume the -# whole body budget and trim every other predicate out of the anchor. The -# sweeper says "UNREAD, not clean" in its summary rather than reporting 0. -# ⛔ Re-enabling is a BACKFILL decision (strip the residue first), never a -# quiet default flip. The script's default is still upstream's 4. +# 1. `PM_SWEEP_CLOSED_FLOOR` on the sweep step — H22's closed-card reader is +# ON here, but judges only cards closed on/after the cutover date. It read +# `PM_SWEEP_CLOSED_WINDOW_PAGES: 0` (reader fully OFF) until 2026-08-28. +# +# The measurement that forced the hold, taken 2026-08-24: 815 closed cards +# here carry `pm:dispatched`, and ~347 of the 400 issues in upstream's +# window carry some `pm:*` residue label (~87%, against the 26% upstream +# measured). At that density H22 reports the CONVENTION, not a defect — +# ~347 rows that consume the whole body budget and trim every other +# predicate out of the anchor. +# +# objectui#5985 recorded that as a two-way choice: either stripping is the +# rule (and ~815 closed cards need a BACKFILL first) or the row is simply +# not wanted here. The dated floor is the third option both readings omit. +# `pm:*` on a card closed before the convention existed is inert history — +# the dispatch loop reads state on OPEN cards only — so judging from the +# cutover forward buys the row's whole value (residue produced from now +# on, while the paired write is still a live duty) at zero backfill and +# zero historical noise. ⛔ NO bulk label backfill of closed cards was run +# and none is owed; the sweeper writes no label under any code path. +# +# The cutover is 2026-08-28, the date the strip-on-close convention was +# written into the pm-dispatch protocol's label-discipline section. ⛔ Do +# not move it EARLIER without re-measuring: every day it moves back pulls +# in cards closed under no convention at all. `resolveClosureFloor` in the +# sweeper refuses a malformed value (exit 2) rather than degrading to "no +# floor", because a silent degrade here restores the 87% flood four times +# a day and a flooded anchor reads exactly like a working patrol. +# +# ⚠️ The floor is UPSTREAM code, not a fourth hand divergence: a re-sync +# that replaces the sweeper with upstream's keeps it. What diverges is +# this WIRING — upstream sets no floor, because its own board measured 26% +# and it treats recent closed residue as a live duty. # 2. `scripts/invoked-as.mjs` is in the `paths:` filter below — the sweeper # imports it, and it was ported alongside. # 3. `scripts/pm/check-half-states.mjs` carries `DEFAULT_SWEEP_REPO = @@ -209,11 +232,14 @@ jobs: # workflow — the two agree by construction and a copy of this file # cannot end up sweeping the repo it was copied FROM. PM_SWEEP_REPO: ${{ github.repository }} - # objectui#5791 — H22's closed-card reader is OFF here. See divergence - # (1) in this file's header for the measurement that decided it, and - # `resolveClosedWindowPages` in the sweeper for what 0 means. The - # rendered body reports the surface as UNREAD, never as clean. - PM_SWEEP_CLOSED_WINDOW_PAGES: '0' + # objectui#5985 — H22's closed-card reader is ON, with a DATED FLOOR: + # only cards closed on/after this date are judged. See divergence (1) + # in this file's header for the measurement and the reasoning, and + # `resolveClosureFloor` in the sweeper for the parsing and the loud + # refusal. The page window is deliberately absent: it is back to the + # script's own upstream default of 4, and the floor — not a zeroed + # window — is what keeps the ~815 historical carriers out. + PM_SWEEP_CLOSED_FLOOR: '2026-08-28' PROVENANCE: >- run [${{ github.run_id }}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) · commit `${{ github.sha }}` · trigger `${{ github.event_name }}` diff --git a/scripts/__tests__/check-half-states.test.ts b/scripts/__tests__/check-half-states.test.ts index 0b3da5cd25..a4968401b2 100644 --- a/scripts/__tests__/check-half-states.test.ts +++ b/scripts/__tests__/check-half-states.test.ts @@ -11,6 +11,7 @@ import { DEFAULT_SWEEP_REPO, h22ClosedCardPmResidue, resolveClosedWindowPages, + resolveClosureFloor, resolveSweepRepo, summaryLine, } from '../pm/check-half-states.mjs'; @@ -81,17 +82,28 @@ describe('check-half-states — sweeps THIS board (objectui#5791 adaptation)', ( }); }); -describe('check-half-states — H22 closed-card reader is OFF here (objectui#5791)', () => { +describe('check-half-states — H22 runs here behind a DATED CLOSURE FLOOR (objectui#5985)', () => { /** - * The measurement that decided this, re-measured 2026-08-24 for the port: - * 815 closed cards in objectui carry `pm:dispatched`, and ~347 of the 400 + * ⚠️ REPLACED PIN, not a respelled one. Until 2026-08-28 this block pinned the + * opposite arrangement: the reader fully OFF via `PM_SWEEP_CLOSED_WINDOW_PAGES: + * '0'`, under a ruling that stripping `pm:*` on close was not this lane's + * convention. That ruling is superseded — fleet practice now strips `pm:*` in + * the landing/close stroke, and the convention is written into the pm-dispatch + * protocol's label-discipline section as of 2026-08-28. + * + * The measurement that forced the original hold still stands and is why the + * re-enable is FLOORED rather than plain (re-measured 2026-08-24 for the + * port): 815 closed cards here carry `pm:dispatched`, and ~347 of the 400 * issues inside upstream's window carry some `pm:*` residue label (~87%, - * against the 26% upstream measured on its own board). Stripping `pm:*` on - * close was never this lane's practice, so H22 here reports the CONVENTION - * rather than a defect — ~347 rows that would exhaust the body budget and - * trim every other predicate out of the anchor on day one. + * against the 26% upstream measured on its own board). An unfloored re-enable + * would report the CONVENTION rather than defects — ~347 rows that exhaust + * the body budget and trim every other predicate out of the anchor on day one. + * + * ⛔ The floor is what makes the re-enable cheap: no backfill of the 815 + * historical carriers was run, and none is owed. The sweeper writes no label + * under any code path, so no bulk rewrite is even reachable from here. */ - it('keeps upstream\'s default in the script, so the port stays a straight copy', () => { + it('keeps upstream\'s page default in the script, so the port stays a straight copy', () => { // The divergence lives in the WORKFLOW, not in the script's default. This // is what lets the predicate file be re-synced verbatim. expect(CLOSED_ISSUE_WINDOW_PAGES).toBe(4); @@ -99,45 +111,91 @@ describe('check-half-states — H22 closed-card reader is OFF here (objectui#579 expect(resolveClosedWindowPages({}).source).toBe('default'); }); - it('is switched off by the workflow, and visibly so', () => { - expect(workflow).toMatch(/PM_SWEEP_CLOSED_WINDOW_PAGES: '0'/); - expect(resolveClosedWindowPages({ PM_SWEEP_CLOSED_WINDOW_PAGES: '0' }).pages).toBe(0); - expect(resolveClosedWindowPages({ PM_SWEEP_CLOSED_WINDOW_PAGES: '0' }).valid).toBe(true); + it('is switched ON by the workflow — the page-window hold is GONE', () => { + // The hold's absence is asserted directly. Left in place beside a floor it + // would win silently (0 pages reads nothing whatever the floor says), and + // the anchor would keep reporting UNREAD while looking re-enabled. + expect(workflow).not.toMatch(/^\s*PM_SWEEP_CLOSED_WINDOW_PAGES:/m); + expect(resolveClosedWindowPages({}).pages).toBe(4); + }); + + it('sets a dated floor in the workflow, and visibly so', () => { + expect(workflow).toMatch(/PM_SWEEP_CLOSED_FLOOR: '2026-08-28'/); + const resolved = resolveClosureFloor({ PM_SWEEP_CLOSED_FLOOR: '2026-08-28' }); + expect(resolved.valid).toBe(true); + expect(resolved.floor?.toISOString()).toBe('2026-08-28T00:00:00.000Z'); }); it('refuses a malformed page count instead of silently defaulting', () => { - // Silently falling back to 4 would re-open the reader this install shut, - // and the anchor would carry the residue as though someone chose that. + // The knob still exists and still refuses garbage — it is simply not what + // holds the historical residue out any more. for (const raw of ['O', '-1', '1.5', 'four']) { expect(resolveClosedWindowPages({ PM_SWEEP_CLOSED_WINDOW_PAGES: raw }).valid).toBe(false); } expect(resolveClosedWindowPages({ PM_SWEEP_CLOSED_WINDOW_PAGES: ' 2 ' }).pages).toBe(2); }); - it('leaves the H22 predicate itself untouched', () => { - // The adaptation is a window, not a rewritten rule: handed a closed card - // with residue the predicate must still say so. Re-enabling the reader is - // therefore a one-variable decision, not a code change. + it('refuses a malformed floor instead of silently running unfloored', () => { + // ⛔ The property the re-enable turns on. A floor that degraded to "no + // floor" would put ~347 convention rows in the anchor four times a day, + // and a flooded anchor reads exactly like a working patrol. + for (const raw of ['28-08-2026', '2026/08/28', 'yesterday', '2026-8-28', 'O']) { + expect(resolveClosureFloor({ PM_SWEEP_CLOSED_FLOOR: raw }).valid).toBe(false); + expect(resolveClosureFloor({ PM_SWEEP_CLOSED_FLOOR: raw }).floor).toBeNull(); + } + // …including a shape-valid date that does not exist. `Date.parse` rolls + // `2026-02-31` to March rather than rejecting it, so the resolver + // round-trips the parse instead of trusting it. + expect(resolveClosureFloor({ PM_SWEEP_CLOSED_FLOOR: '2026-02-31' }).valid).toBe(false); + // Unset is valid and means "no floor" — upstream's own behaviour. + expect(resolveClosureFloor({}).valid).toBe(true); + expect(resolveClosureFloor({}).floor).toBeNull(); + }); + + it('applies the floor to the H22 predicate: old closures out, new ones judged', () => { + // The whole re-enable, in two assertions. The old card is the ~815-card + // backlog in miniature — real residue, deliberately NOT a finding. const closedCard = { state: 'closed', state_reason: 'completed', labels: [{ name: 'pm:dispatched' }], }; - expect(h22ClosedCardPmResidue(closedCard)).toContain('`pm:dispatched`'); - expect(h22ClosedCardPmResidue({ ...closedCard, state: 'open' })).toBeNull(); + const floor = resolveClosureFloor({ PM_SWEEP_CLOSED_FLOOR: '2026-08-28' }).floor; + expect(h22ClosedCardPmResidue({ ...closedCard, closed_at: '2026-01-01T00:00:00Z' }, floor)).toBeNull(); + expect(h22ClosedCardPmResidue({ ...closedCard, closed_at: '2026-08-29T00:00:00Z' }, floor)).toContain('`pm:dispatched`'); + // The cutover date itself is judged: it is the first day the convention + // applies, so cards closed within it are the convention's own population. + expect(h22ClosedCardPmResidue({ ...closedCard, closed_at: '2026-08-28T12:00:00Z' }, floor)).toContain('`pm:dispatched`'); + // Unfloored, the predicate is byte-for-byte upstream's — the port stays a + // straight copy and the floor is opt-in. + expect(h22ClosedCardPmResidue({ ...closedCard, closed_at: '2026-01-01T00:00:00Z' })).toContain('`pm:dispatched`'); + expect(h22ClosedCardPmResidue({ ...closedCard, state: 'open' }, floor)).toBeNull(); + }); + + it('names the floor in the summary, so a floored pass cannot read as a full one', () => { + // ⛔ #4690, in the shape this change could newly break: "H22 read 200" with + // a floor silently applied overstates what was judged. The line must carry + // the floor date, and must say the earlier closures are UNJUDGED rather + // than clean. + const counts = { repo: 'objectstack-ai/objectui', issues: 3, unscoped: 4, prs: 1, merged: 2, closed: 200 }; + const floored = summaryLine({ ...counts, closedFloor: '2026-08-28' }, 0); + expect(floored).toContain('H22 read 200 recently-closed issue(s)'); + expect(floored).toContain('only cards closed on/after 2026-08-28 are judged'); + expect(floored).toContain('NOT a reading about them'); + // An unfloored pass must not grow the clause. + expect(summaryLine(counts, 0)).not.toContain('are judged'); }); - it('reports the closed surface as UNREAD, never as clean', () => { - // ⛔ The property the whole adaptation turns on (#4690). A disabled reader - // and an empty result are the same number and opposite facts; if this ever - // renders "H22 read 0", the anchor starts asserting a clean closed surface - // that nothing looked at. + it('still reports a DISABLED reader as UNREAD, never as clean', () => { + // The disabled branch is no longer wired here, but it is still live code + // and still the property the port turned on (#4690): a disabled reader and + // an empty result are the same number and opposite facts. Kept pinned so + // re-adding the hold cannot quietly render "H22 read 0". const counts = { repo: 'objectstack-ai/objectui', issues: 3, unscoped: 4, prs: 1, merged: 2, closed: 0 }; const disabled = summaryLine({ ...counts, closedWindowDisabled: true }, 0); expect(disabled).toContain('is DISABLED in this install'); expect(disabled).toContain('UNREAD'); expect(disabled).not.toContain('H22 read 0'); - // …and it names the way back, so the choice is reversible by a reader. expect(disabled).toContain('PM_SWEEP_CLOSED_WINDOW_PAGES'); // The other direction: an enabled reader that found nothing IS a clean diff --git a/scripts/pm/check-half-states.mjs b/scripts/pm/check-half-states.mjs index 40828ca31f..394b8ebfe6 100644 --- a/scripts/pm/check-half-states.mjs +++ b/scripts/pm/check-half-states.mjs @@ -3658,9 +3658,30 @@ export const PM_RESIDUE_LABELS = [ * predicate cannot double-report the population every other item already reads. * That gate is the predicate's own, not the caller's, because it is the one * thing separating this row from a restatement of H3. + * + * `floor` is the optional dated closure floor (see `resolveClosureFloor`): a + * `Date` before which a closed card is out of scope, or null for "judge every + * card in the window", which is the default and upstream's own behaviour. + * + * ⚠️ A card whose `closed_at` cannot be read is judged, NOT skipped. The floor + * is a scope decision that needs a date to make; without one the card's + * position relative to the cutover is UNKNOWN, and silently dropping it would + * narrow the pass on unread data — #4690 in the direction this file refuses + * everywhere else. The listing endpoint always carries `closed_at` on a closed + * issue, so fail-open costs no noise in practice; it just keeps the one + * unreadable card visible instead of disappeared. + * + * @param {any} issue + * @param {Date | null} [floor] */ -export function h22ClosedCardPmResidue(issue) { +export function h22ClosedCardPmResidue(issue, floor = null) { if (issue?.state !== 'closed') return null; + if (floor) { + const closedAt = Date.parse(issue.closed_at ?? ''); + // Strictly BEFORE the floor is out of scope; a card closed ON the cutover + // date is the first day the convention applies and is judged. + if (!Number.isNaN(closedAt) && closedAt < floor.getTime()) return null; + } const residue = labelNames(issue ?? {}).filter((l) => PM_RESIDUE_LABELS.includes(l)); if (residue.length === 0) return null; const list = residue.map((l) => `\`${l}\``).join(', '); @@ -4414,7 +4435,7 @@ export function isLoudFinding(message) { * restartCandidates?: number, blockerResolved?: number, * blockerTargets?: number, commits?: number, commitBindings?: number, * commitBindingMessages?: number, - * closedWindowDisabled?: boolean }} counts + * closedWindowDisabled?: boolean, closedFloor?: string }} counts * @param {number} findingCount */ export function summaryLine(counts, findingCount) { @@ -4471,7 +4492,9 @@ export function summaryLine(counts, findingCount) { 'not clean (see `resolveClosedWindowPages` and `PM_SWEEP_CLOSED_WINDOW_PAGES` in ' + '`.github/workflows/half-state-patrol.yml`). ' : `H22 read ${counts.closed ?? 0} recently-closed issue(s) for \`pm:*\` state residue (bounded window; ` + - `older closed carriers are outside it by design). `) + + `older closed carriers are outside it by design` + + `${counts.closedFloor ? `, and only cards closed on/after ${counts.closedFloor} are judged — ` + + 'earlier closures predate the strip-on-close convention and are NOT a reading about them' : ''}). `) + `H23 read ${commits} squash commit message(s) from the default branch's recent window, carrying ` + `${commitBindings} closing-keyword binding(s) across ${commitBindingMessages} message(s) ` + `(bounded window; a message that landed before it is invisible by design). ` + @@ -5559,6 +5582,78 @@ export function resolveClosedWindowPages(env = {}) { const CLOSED_WINDOW = resolveClosedWindowPages(process.env); +/** + * H22's DATED CLOSURE FLOOR — the cutover date at and after which a closed + * card's `pm:*` residue is judged (objectui#5985). + * + * ## The dilemma this dissolves, and why THIS install is the one that needed it + * + * The window above is bounded by UPDATE recency, which is the wrong axis for + * the question this repo kept running into: "was this card closed under the + * convention, or before it existed?" Measured here 2026-08-24, while porting + * this file: 815 closed cards carry `pm:dispatched`, and ~347 of the 400 + * issues in the window carry some `pm:*` residue (~87%, against the 26% + * upstream measured on its own board). At that density H22 reports the + * CONVENTION rather than a defect — ~347 rows that exhaust the anchor body + * budget and trim every other predicate's findings out of the report. So the + * reader shipped OFF (`PM_SWEEP_CLOSED_WINDOW_PAGES: '0'`, divergence 1), and + * objectui#5985 recorded the choice as a two-way one: either stripping is the + * rule (and ~815 cards need a BACKFILL before H22 can be honest) or it is not + * (and H22 is simply not a predicate this repo wants). + * + * The floor is the third option both readings omit. `pm:*` on a card closed + * before the convention was written is inert history: nothing queries it as a + * claim of in-flight-ness, because the loop reads state on OPEN cards only + * (`is:open` is in every one of its inventory queries). Judging only cards + * closed on/after a cutover date therefore buys the row's whole value — the + * residue produced from now on, while the paired write is still a live duty + * someone remembers — at zero backfill and zero historical noise. ⛔ The + * alternative this file must never grow is a bulk label rewrite of closed + * cards: 815 mutating writes to make a report quieter is machinery serving the + * instrument, and no code path here writes a label at all. + * + * ## Ported, not invented here + * + * This is upstream's code (objectstack `scripts/pm/check-half-states.mjs`), + * carried across with the docblock re-pointed at this board's measurement. ⚠️ + * It is NOT a fourth hand divergence: a re-sync that replaces this file with + * upstream's must keep the floor, because upstream has it. What IS divergent + * is the WIRING in `.github/workflows/half-state-patrol.yml` — see divergence + * 1 there, which now sets a floor instead of switching the reader off. + * + * ## Malformed is REFUSED, never defaulted + * + * A typo'd floor that silently became "no floor" would restore the 87% flood + * here, four times a day, and the flood reads as a working patrol — the same + * trap `resolveSweepRepo` and `resolveClosedWindowPages` refuse by name. Only + * the `YYYY-MM-DD` spelling is accepted: a bare `Date` parse would take + * "yesterday-ish" strings and timezone-bearing ones whose midnight is not the + * one the workflow author meant, and the value is written by hand exactly once. + */ +export function resolveClosureFloor(env = {}) { + const raw = String(env.PM_SWEEP_CLOSED_FLOOR ?? '').trim(); + if (!raw) return { floor: null, source: 'default', valid: true, raw: '' }; + if (!/^\d{4}-\d{2}-\d{2}$/.test(raw)) { + return { floor: null, source: 'PM_SWEEP_CLOSED_FLOOR', valid: false, raw }; + } + const at = Date.parse(`${raw}T00:00:00Z`); + if (Number.isNaN(at)) return { floor: null, source: 'PM_SWEEP_CLOSED_FLOOR', valid: false, raw }; + // A shape-valid string can still name a date that does not EXIST, and + // `Date.parse` does not reject all of them: `2026-13-01` is NaN (the month + // is outside the ISO range) but `2026-02-31` silently ROLLS to 2026-03-03. + // So the parse is round-tripped rather than trusted. Letting a rolled date + // through would move the floor days past where its author wrote it and, + // worse, do it silently — the floor is the one input here whose whole job is + // to say which cards were judged. + const floor = new Date(at); + if (floor.toISOString().slice(0, 10) !== raw) { + return { floor: null, source: 'PM_SWEEP_CLOSED_FLOOR', valid: false, raw }; + } + return { floor, source: 'PM_SWEEP_CLOSED_FLOOR', valid: true, raw }; +} + +const CLOSED_FLOOR = resolveClosureFloor(process.env); + async function listRecentlyClosedIssues() { // 0 pages = the closed reader is off in this install. Returning early (rather // than letting the loop not execute) keeps the intent legible and makes it @@ -5891,9 +5986,10 @@ async function sweepInto(findings, seen, seenPrs, seenMerged, seenUnscoped, seen // line can say what this pass covered on its own terms. for (const issue of await listRecentlyClosedIssues()) { seenClosed.set(issue.number, issue); - const residue = h22ClosedCardPmResidue(issue); + const residue = h22ClosedCardPmResidue(issue, CLOSED_FLOOR.floor); if (residue) findings.push([issue, 'H22', residue]); } + stats.closedFloor = CLOSED_FLOOR.raw; // H23 — the commit-message surface (#10942). The counting is not incidental: // this row's measured yield is ~6 in 1,546, so a silent H23 is the normal @@ -6900,9 +6996,82 @@ function selfTest() { t(`H22: \`${label}\` on a closed card is residue`, typeof h22ClosedCardPmResidue(closedCard([label])), 'string'); } + // -- H22's DATED CLOSURE FLOOR (objectui#5985) ------------------------------ + // + // The floor is what lets THIS install re-enable the row without the backfill + // its own card thought was the only alternative: judge cards closed on/after + // the cutover date, leave the ~815 historical carriers unjudged, write no + // labels at all. The cases below pin the three properties that decision rests + // on — the floor is HONOURED, its absence changes nothing, and a malformed + // value is refused rather than silently becoming "no floor". + const FLOOR = new Date(Date.parse('2026-08-28T00:00:00Z')); + const closedOn = (labels, closed_at) => ({ ...closedCard(labels), closed_at }); + + // Honoured, both directions. The old card is the ~815-card backlog in + // miniature: it carries real residue and is deliberately NOT a finding. + t('H22 floor: a card closed BEFORE the floor is out of scope', h22ClosedCardPmResidue(closedOn(['pm:dispatched'], '2026-08-01T09:00:00Z'), FLOOR), null); + t('H22 floor: …however much residue it carries', h22ClosedCardPmResidue(closedOn(['pm:dispatched', 'pm:queue', 'pm:blocked'], '2026-01-01T00:00:00Z'), FLOOR), null); + t('H22 floor: a card closed AFTER the floor is judged', typeof h22ClosedCardPmResidue(closedOn(['pm:dispatched'], '2026-08-29T09:00:00Z'), FLOOR), 'string'); + t('H22 floor: …and the row still names the residue label', h22ClosedCardPmResidue(closedOn(['pm:dispatched'], '2026-08-29T09:00:00Z'), FLOOR).includes('`pm:dispatched`'), true); + // The boundary is inclusive: the cutover date is the first day the convention + // applies, so a card closed within it is the convention's own population. + t('H22 floor: a card closed ON the floor date is judged', typeof h22ClosedCardPmResidue(closedOn(['pm:queue'], '2026-08-28T00:00:00Z'), FLOOR), 'string'); + t('H22 floor: …and later the same day too', typeof h22ClosedCardPmResidue(closedOn(['pm:queue'], '2026-08-28T23:59:59Z'), FLOOR), 'string'); + t('H22 floor: one second before the floor is out', h22ClosedCardPmResidue(closedOn(['pm:queue'], '2026-08-27T23:59:59Z'), FLOOR), null); + // The floor narrows scope; it never invents findings. + t('H22 floor: a clean card after the floor is still clean', h22ClosedCardPmResidue(closedOn(['domain:ui'], '2026-08-29T09:00:00Z'), FLOOR), null); + t('H22 floor: the closed gate still outranks the floor', h22ClosedCardPmResidue({ ...issue(['pm:dispatched']), state: 'open', closed_at: null }, FLOOR), null); + // Fail-OPEN on an unreadable closure date: the floor cannot be applied, so + // the card stays visible rather than being dropped on unread data (#4690). + t('H22 floor: a card with no closed_at is judged, not dropped', typeof h22ClosedCardPmResidue(closedOn(['pm:dispatched'], null), FLOOR), 'string'); + t('H22 floor: …and an unparseable one likewise', typeof h22ClosedCardPmResidue(closedOn(['pm:dispatched'], 'not-a-date'), FLOOR), 'string'); + + // Floor ABSENT — upstream's default, and the property that makes the ported + // code a no-op for an install that does not set the variable. + t('H22 floor: absent floor judges an old closed card exactly as before', typeof h22ClosedCardPmResidue(closedOn(['pm:dispatched'], '2026-01-01T00:00:00Z')), 'string'); + t('H22 floor: …an explicit null is the same as omitting it', typeof h22ClosedCardPmResidue(closedOn(['pm:dispatched'], '2026-01-01T00:00:00Z'), null), 'string'); + t('H22 floor: …and a clean old card is still clean', h22ClosedCardPmResidue(closedOn(['domain:ui'], '2026-01-01T00:00:00Z'), null), null); + + // resolveClosureFloor — the env reading, including the loud refusal. + t('closure floor: unset means no floor', resolveClosureFloor({}).floor, null); + t('closure floor: …and that is a VALID reading, not an error', resolveClosureFloor({}).valid, true); + t('closure floor: …reported as the default source', resolveClosureFloor({}).source, 'default'); + t('closure floor: whitespace is unset too', resolveClosureFloor({ PM_SWEEP_CLOSED_FLOOR: ' ' }).floor, null); + t('closure floor: a YYYY-MM-DD date resolves to UTC midnight', resolveClosureFloor({ PM_SWEEP_CLOSED_FLOOR: '2026-08-28' }).floor.toISOString(), '2026-08-28T00:00:00.000Z'); + t('closure floor: …and is valid', resolveClosureFloor({ PM_SWEEP_CLOSED_FLOOR: '2026-08-28' }).valid, true); + t('closure floor: …and names its source', resolveClosureFloor({ PM_SWEEP_CLOSED_FLOOR: '2026-08-28' }).source, 'PM_SWEEP_CLOSED_FLOOR'); + t('closure floor: surrounding whitespace is trimmed, not rejected', resolveClosureFloor({ PM_SWEEP_CLOSED_FLOOR: ' 2026-08-28 ' }).valid, true); + // Malformed is REFUSED. Each of these would otherwise become "no floor" and + // restore the ~347-row flood this install shut off. + for (const bad of ['28-08-2026', '2026/08/28', 'yesterday', '2026-08-28T00:00:00Z', '2026-8-28', 'O', '0']) { + t(`closure floor: \`${bad}\` is refused, not defaulted`, resolveClosureFloor({ PM_SWEEP_CLOSED_FLOOR: bad }).valid, false); + } + // …including a well-SHAPED date that does not exist — the case a bare regex + // would pass and whose floor would exclude every card, rendering an empty + // H22 as a clean closed surface. + t('closure floor: a shape-valid impossible date is refused', resolveClosureFloor({ PM_SWEEP_CLOSED_FLOOR: '2026-02-31' }).valid, false); + t('closure floor: …and an impossible month likewise', resolveClosureFloor({ PM_SWEEP_CLOSED_FLOOR: '2026-13-01' }).valid, false); + t('closure floor: a refused value carries no floor to fall back on', resolveClosureFloor({ PM_SWEEP_CLOSED_FLOOR: 'yesterday' }).floor, null); + t('closure floor: …and is reported as itself for the error message', resolveClosureFloor({ PM_SWEEP_CLOSED_FLOOR: 'yesterday' }).raw, 'yesterday'); + // The floor and the page window are INDEPENDENT knobs: a 0-page window still + // reads nothing whatever the floor says, and that is the disabled summary, + // not a floored one. Pinned because the workflow now sets the floor INSTEAD + // of the 0, and a future reader must not read one as an alias of the other. + t('closure floor: the floor does not switch the reader on', resolveClosedWindowPages({ PM_SWEEP_CLOSED_WINDOW_PAGES: '0', PM_SWEEP_CLOSED_FLOOR: '2026-08-28' }).pages, 0); + t('closure floor: …and the window does not set a floor', resolveClosureFloor({ PM_SWEEP_CLOSED_WINDOW_PAGES: '4' }).floor, null); + // The summary line's H22 clause — a pass that read nothing must not read the // same as a board with no residue (#4690), so the count is always stated. t('summary: the H22 clause states what the closed pass read', summaryLine({ repo: 'r', issues: 1, unscoped: 1, prs: 0, merged: 0, closed: 200 }, 0).includes('H22 read 200 recently-closed issue(s)'), true); + // …and when a floor is in force the line SAYS so: "read 200" with a floor + // silently applied would overstate what was judged, which is the same + // unread-reads-as-clean defect the count itself exists to prevent. + t('summary: a floored pass names the floor date', summaryLine({ repo: 'r', issues: 1, unscoped: 1, prs: 0, merged: 0, closed: 200, closedFloor: '2026-08-28' }, 0).includes('only cards closed on/after 2026-08-28 are judged'), true); + t('summary: …and says the earlier closures are not a reading about them', summaryLine({ repo: 'r', issues: 1, unscoped: 1, prs: 0, merged: 0, closed: 200, closedFloor: '2026-08-28' }, 0).includes('NOT a reading about them'), true); + t('summary: an unfloored pass adds no floor clause', summaryLine({ repo: 'r', issues: 1, unscoped: 1, prs: 0, merged: 0, closed: 200 }, 0).includes('are judged'), false); + // The DISABLED branch still wins over a floor: a 0-page window read nothing, + // so the line must keep saying UNREAD rather than describing a floored pass. + t('summary: a disabled reader with a floor set still reads UNREAD', summaryLine({ repo: 'r', issues: 1, unscoped: 1, prs: 0, merged: 0, closedWindowDisabled: true, closedFloor: '2026-08-28' }, 0).includes('UNREAD'), true); t('summary: an absent closed count degrades to 0, never to undefined', summaryLine({ repo: 'r', issues: 1, unscoped: 1, prs: 0, merged: 0 }, 0).includes('H22 read 0 recently-closed'), true); // -- H23: the COMMIT-MESSAGE surface (#10942) ------------------------------- @@ -9115,6 +9284,18 @@ if (isMain) { ); process.exit(2); } + // A malformed closure floor is the same class and gets the same answer. It + // must not degrade to "no floor": this is the install whose closed surface is + // ~87% residue, so a silent default would flood the anchor four times a day + // and the flood renders as a working patrol. + if (!process.argv.includes('--self-test') && !CLOSED_FLOOR.valid) { + console.error( + `check-half-states: ${CLOSED_FLOOR.source}=${JSON.stringify(CLOSED_FLOOR.raw)} is not a ` + + '`YYYY-MM-DD` date. Refusing to fall back to an unfloored closed pass — on this board, ' + + 'no floor is a report about the convention rather than about defects.', + ); + process.exit(2); + } if (process.argv.includes('--self-test')) { selfTest(); } else if (process.argv.includes('--probe')) {