Skip to content

Commit c1123cf

Browse files
os-litantclaude
andauthored
fix(pm): re-pin H22's closed-issue update rate from a card-only measurement (#17627)
`MEASURED_CLOSED_ISSUE_UPDATES_PER_DAY` stood at 415.1/day, measured 2026-09-06, and every sweep since has printed `RATE PREMISE DRIFTED` — so the coverage sentence H22 writes about itself was misdescribed by that factor, four times a day. Re-measured by the docblock's own recipe, and over the population the sweep is actually rated against: the scheduled runner's `/issues` pages come back card-only, while the byte-identical request from an agent container returns 49.6% pull requests. The 2026-09-11T01:55Z run reports 5 pages, 428 in-window closures and ~141.3/day; replaying this pager over a container read of the same hour reproduces 5 pages / 426 / ~139.4 on the card-only slice and 9 pages / 426 / ~256.2 on the whole one, and 428 in-window closures cannot come out of 5 PR-inclusive pages at all. The pin it replaces was taken PR-inclusive, so the 0.34 factor the alarm printed was a real slowdown times a population difference. Pinned at 139.4/day (500 card-only rows spanning 3.586 days — the five pages a 3-day horizon costs), dated 2026-09-11, with the 200 / 400 / 605-row re-reads recorded in the same shape the old record used. The alarm reads `ok` against the last run's own observation (factor 1.01). The divisor stays a HAND pin: the alarm reports, it never writes. Self-test: 3606 cases pass before and after — no case added or removed; the pins that hardcode a number derived from the constant's value are re-derived from it. Claude-Session: https://claude.ai/code/session_01YKEjmbYNvYWJvWGSWx26zK Co-authored-by: Claude <noreply@anthropic.com>
1 parent 690f083 commit c1123cf

1 file changed

Lines changed: 61 additions & 35 deletions

File tree

scripts/pm/check-half-states.mjs

Lines changed: 61 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -16471,18 +16471,40 @@ export const CLOSED_ISSUE_WINDOW_PAGE_CEILING = 40;
1647116471
* disagree with.
1647216472
*
1647316473
* ⛔ This is closed-issue UPDATE EVENTS per day, NOT closures per day. The two
16474-
* differ by ~17x on this board and confusing them is precisely the defect this
16474+
* differ by ~13x on this board and confusing them is precisely the defect this
1647516475
* window carried (see the docblock above).
1647616476
*
16477-
* read 2026-09-06T21:59Z, `GET /repos/{repo}/issues?state=closed&sort=updated`
16478-
* window 400 rows spanning 0.964 days
16479-
* rate 400 / 0.964 = ~415 closed-issue update events/day
16480-
* (re-read at depth: 800 rows / 2.051d = ~390/day, 1200 rows / 3.092d = ~388/day)
16477+
* ⚠️ AND IT IS MEASURED OVER THE STREAM THE PATROL PAGES, which is NOT the one
16478+
* an agent container gets from the same URL. The scheduled runner's token
16479+
* declares `contents: read` + `issues: write` and no pull-request scope, and its
16480+
* `/issues` pages come back CARD-ONLY; the byte-identical request from a proxied
16481+
* container returns 49.6% pull requests. Measured, not inferred from the
16482+
* workflow file: the 2026-09-11T01:55Z run reports 5 pages, 428 in-window
16483+
* closures and ~141.3/day, and replaying THIS pager over a container read of the
16484+
* same hour gives 5 pages / 426 / ~139.4 on the CARD-ONLY slice against 9 pages /
16485+
* 426 / ~256.2 on the whole one — and 428 in-window closures cannot come out of 5
16486+
* PR-inclusive pages at all, since only ~250 of those 500 rows are cards.
16487+
*
16488+
* ⛔ So a re-pin read from a container rates a different population than the
16489+
* sweep it is checked against, and the 0.34 factor the DRIFTED clause has been
16490+
* printing is TWO things multiplied: a real ~0.64 slowdown of the stream since
16491+
* 2026-09-06, and a ~0.53 population difference that is not tempo at all.
16492+
*
16493+
* read 2026-09-11T04:22Z, `GET /repos/{repo}/issues?state=closed&sort=updated`
16494+
* — card-only rows, the population the runner pages
16495+
* window 500 rows spanning 3.586 days — the 5 pages a 3-day horizon costs
16496+
* rate 500 / 3.586 = ~139 closed-issue update events/day
16497+
* (re-read at depth: 200 rows / 1.352d = ~148/day, 400 rows / 2.769d = ~144/day,
16498+
* 605 rows / 3.969d = ~152/day)
1648116499
*/
16482-
export const MEASURED_CLOSED_ISSUE_UPDATES_PER_DAY = 415.1;
16500+
export const MEASURED_CLOSED_ISSUE_UPDATES_PER_DAY = 139.4;
1648316501

16484-
/** WHEN `MEASURED_CLOSED_ISSUE_UPDATES_PER_DAY` was measured. */
16485-
export const MEASURED_CLOSED_ISSUE_UPDATES_PER_DAY_AT = '2026-09-06';
16502+
/**
16503+
* WHEN `MEASURED_CLOSED_ISSUE_UPDATES_PER_DAY` was measured — and therefore when
16504+
* it EXPIRES on the calendar whatever a sweep observes: `RATE_PREMISE_STALE_DAYS`
16505+
* past this date is 2026-10-11.
16506+
*/
16507+
export const MEASURED_CLOSED_ISSUE_UPDATES_PER_DAY_AT = '2026-09-11';
1648616508

1648716509
/**
1648816510
* H22's premise, in the shape `classifyRatePremise` judges (#16393).
@@ -22702,12 +22724,12 @@ async function selfTest() {
2270222724
t('windows: …and 2.73d at the rate measured 8 days later — same cap, 25% more window', Number(windowCoverageDays(300, MEASURED_COMMITS_PER_DAY).toFixed(2)), 2.73);
2270322725
t('windows: …which is why H23\'s cap became a TIME cap', COMMIT_WINDOW_DAYS, 3);
2270422726
// ⛔ H22's is the one whose DIVISOR was the surprise: its rows are consumed by
22705-
// closed-issue UPDATE activity, not by closures, and the two differ ~39x at
22706-
// the divisor re-pinned 2026-09-06, against the ~10.7 closures/day measured
22727+
// closed-issue UPDATE activity, not by closures, and the two differ ~13x at
22728+
// the divisor re-pinned 2026-09-11, against the ~10.7 closures/day measured
2270722729
// when the defect was found. The old 4-page cap read as a closure window and
2270822730
// was nothing of the kind.
22709-
t('windows: H22\'s old 4-page cap bought only 0.96d of UPDATE recency at the 2026-09-06 rate', Number(windowCoverageDays(400, MEASURED_CLOSED_ISSUE_UPDATES_PER_DAY).toFixed(2)), 0.96);
22710-
t('windows: …and the honest divisor is ~39x the closure rate it read as', Number((MEASURED_CLOSED_ISSUE_UPDATES_PER_DAY / 10.7).toFixed(1)), 38.8);
22731+
t('windows: H22\'s old 4-page cap bought 2.87d of UPDATE recency at the 2026-09-11 rate', Number(windowCoverageDays(400, MEASURED_CLOSED_ISSUE_UPDATES_PER_DAY).toFixed(2)), 2.87);
22732+
t('windows: …and the honest divisor is ~13x the closure rate it read as', Number((MEASURED_CLOSED_ISSUE_UPDATES_PER_DAY / 10.7).toFixed(1)), 13);
2271122733
t('windows: …which is why H22\'s cap became a CLOSURE-time cap', CLOSED_ISSUE_WINDOW_DAYS, 3);
2271222734
t('windows: …and its horizon is seen by 12 consecutive runs', sweepOverlap(CLOSED_ISSUE_WINDOW_DAYS), 12);
2271322735
// #13499 — H8's window is no longer a page cap at all, so the arithmetic that
@@ -22878,10 +22900,13 @@ async function selfTest() {
2287822900
t('H22 window: a 1-day-old closure is inside', issueClosedWithinWindow(closed13606(1, 1), CH), true);
2287922901
// Direction 2 — the row the OLD 4-page cap structurally could not reach. The
2288022902
// cap covered ~0.96d of UPDATE recency at the divisor pinned 2026-09-06
22881-
// (~2.12d at the one pinned when the repair landed), so a card closed 2.5
22882-
// days ago and untouched since fell out of it: not late, GONE. Measured cost
22883-
// at the time of the repair: 19 residue carriers closed within 3 days missed.
22884-
t('H22 window: the old 4-page cap reached only ~1.0 days of update-recency', windowCoverageDays(400, MEASURED_CLOSED_ISSUE_UPDATES_PER_DAY) < 2.5, true);
22903+
// (~2.12d at the one pinned when the repair landed, ~2.87d at the one pinned
22904+
// 2026-09-11), so a card closed 2.5 days ago and untouched since fell out of
22905+
// it: not late, GONE. Measured cost at the time of the repair: 19 residue
22906+
// carriers closed within 3 days missed. ⚖️ The days this cap buys move with
22907+
// every re-pin — which is the whole argument against a page cap — so what is
22908+
// pinned below is its SHORTFALL against the horizon, not a fixed count of days.
22909+
t('H22 window: the old 4-page cap reached ~2.9 days of update-recency, still short of the 3-day horizon', windowCoverageDays(400, MEASURED_CLOSED_ISSUE_UPDATES_PER_DAY) < 3, true);
2288522910
t('H22 window: …but the 3-day closure horizon admits that card', issueClosedWithinWindow(closed13606(2, 2.5), CH), true);
2288622911
// …and the window still HAS an edge; a boundary that admits everything is none.
2288722912
t('H22 window: a 4-day-old closure is outside', issueClosedWithinWindow(closed13606(3, 4), CH), false);
@@ -23103,28 +23128,29 @@ async function selfTest() {
2310323128
const rateStream16393 = (step) =>
2310423129
closedStream(Array.from({ length: 30 }, (_, i) => ratePage16393(step * (i + 1))));
2310523130

23106-
// 2x the pin: 0.11-day steps, so the horizon falls on page 28 — 2800 rows
23107-
// read across a 2.97-day span is ~942.8/day against a pin of 415.1.
23131+
// Well past the band: 0.11-day steps, so the horizon falls on page 28 — 2800
23132+
// rows read across a 2.97-day span is ~942.8/day against a pin of 139.4.
2310823133
const driftedStats16393 = {};
2310923134
const driftedRows16393 = await listRecentlyClosedIssues(driftedStats16393, NOW13606, rateStream16393(0.11));
2311023135
t('#16393 stream: the pass stops on the first page past the horizon', driftedStats16393.closedPages, 28);
2311123136
t('#16393 stream: …and the rate is over the rows READ', Math.round(driftedStats16393.closedRateObserved * 100) / 100, 942.76);
2311223137
t('#16393 stream: ⛔ …not over the rows ADMITTED, which is a different number', Math.round(observedRatePerDay(driftedRows16393, 'updated_at') * 100) / 100 === Math.round(driftedStats16393.closedRateObserved * 100) / 100, false);
23113-
t('#16393 stream: …a board running 2x the pin FIRES the alarm', h22Premise(driftedStats16393.closedRateObserved).state, 'drifted');
23114-
t('#16393 stream: …and the factor is stated so it can be acted on', h22Premise(driftedStats16393.closedRateObserved).message.includes('a factor of 2.27'), true);
23115-
t('#16393 stream: …with both rates beside it', h22Premise(driftedStats16393.closedRateObserved).message.includes('observed ~942.8 closed-issue updates/day') && h22Premise(driftedStats16393.closedRateObserved).message.includes('= 415.1/day'), true);
23138+
t('#16393 stream: …a board running past the band FIRES the alarm', h22Premise(driftedStats16393.closedRateObserved).state, 'drifted');
23139+
t('#16393 stream: …and the factor is stated so it can be acted on', h22Premise(driftedStats16393.closedRateObserved).message.includes('a factor of 6.76'), true);
23140+
t('#16393 stream: …with both rates beside it', h22Premise(driftedStats16393.closedRateObserved).message.includes('observed ~942.8 closed-issue updates/day') && h22Premise(driftedStats16393.closedRateObserved).message.includes('= 139.4/day'), true);
2311623141
// ⛔ The rule that makes this an alarm rather than a self-healing constant.
23117-
t('#16393 stream: ⛔ a drifted sweep does NOT rewrite the pin', MEASURED_CLOSED_ISSUE_UPDATES_PER_DAY, 415.1);
23118-
t('#16393 stream: …nor its date', MEASURED_CLOSED_ISSUE_UPDATES_PER_DAY_AT, '2026-09-06');
23119-
23120-
// 1x the pin: 0.25-day steps, horizon on page 13 — 1300 rows over 3.0 days is
23121-
// ~433.3/day, a factor of 1.04, and the alarm stays silent. ⚖️ It is the very
23122-
// stream that ran 2x the pin until the 2026-09-06 re-measure: the board moved
23123-
// by that much in six days, and the fixture did not move at all.
23142+
t('#16393 stream: ⛔ a drifted sweep does NOT rewrite the pin', MEASURED_CLOSED_ISSUE_UPDATES_PER_DAY, 139.4);
23143+
t('#16393 stream: …nor its date', MEASURED_CLOSED_ISSUE_UPDATES_PER_DAY_AT, '2026-09-11');
23144+
23145+
// 1x the pin: 0.75-day steps, horizon on page 5 — 500 rows over 3.0 days is
23146+
// ~166.7/day, a factor of 1.20, and the alarm stays silent. ⚖️ The step is
23147+
// stated RELATIVE to the hand-pinned rate, so it moves when the pin does — it
23148+
// was 0.25 against the 415.1 pinned 2026-09-06 — because a fixture asserting
23149+
// "in band" against a rate nobody re-measured passes for a board nobody read.
2312423150
const okStats16393 = {};
23125-
await listRecentlyClosedIssues(okStats16393, NOW13606, rateStream16393(0.25));
23126-
t('#16393 stream: an in-band board reaches its horizon in thirteen pages', okStats16393.closedPages, 13);
23127-
t('#16393 stream: …and observes a rate at the pin', Math.round(okStats16393.closedRateObserved * 100) / 100, 433.33);
23151+
await listRecentlyClosedIssues(okStats16393, NOW13606, rateStream16393(0.75));
23152+
t('#16393 stream: an in-band board reaches its horizon in five pages', okStats16393.closedPages, 5);
23153+
t('#16393 stream: …and observes a rate at the pin', Math.round(okStats16393.closedRateObserved * 100) / 100, 166.67);
2312823154
t('#16393 stream: …which raises NO alarm', h22Premise(okStats16393.closedRateObserved).state, 'ok');
2312923155
t('#16393 stream: a board with nothing dateable observes no rate at all', (await (async () => { const s = {}; await listRecentlyClosedIssues(s, NOW13606, closedStream([[{ number: 1, updated_at: 'nope' }]])); return s.closedRateObserved; })()), null);
2313023156
t('#16393 stream: …and an empty board likewise', (await (async () => { const s = {}; await listRecentlyClosedIssues(s, NOW13606, closedStream([])); return s.closedRateObserved; })()), null);
@@ -23137,13 +23163,13 @@ async function selfTest() {
2313723163
const clause16393 = (observed, extra = {}) => h22RatePremiseClause(observed, { nowMs: NOW13606, ...extra });
2313823164
t('#16393 clause: a drifted divisor is marked loud', clause16393(942.76).startsWith('⚠️ RATE PREMISE DRIFTED'), true);
2313923165
t('#16393 clause: …and names the re-measure act', clause16393(942.76).includes('re-pinned by hand, from a fresh measurement'), true);
23140-
t('#16393 clause: an expired pin is marked loud too', clause16393(415.1, { nowMs: Date.parse('2026-11-01T00:00:00Z') }).startsWith('⚠️ RATE PREMISE EXPIRED'), true);
23166+
t('#16393 clause: an expired pin is marked loud too', clause16393(139.4, { nowMs: Date.parse('2026-11-01T00:00:00Z') }).startsWith('⚠️ RATE PREMISE EXPIRED'), true);
2314123167
t('#16393 clause: an unobserved rate is marked loud, never silent', clause16393(null).startsWith('⚠️ RATE PREMISE UNOBSERVED'), true);
23142-
t('#16393 clause: an in-band divisor is NOT marked loud', clause16393(433.33).includes('⚠️'), false);
23168+
t('#16393 clause: an in-band divisor is NOT marked loud', clause16393(166.67).includes('⚠️'), false);
2314323169
// ⛔ …but it still SPEAKS. A check that is only visible when it fires cannot
2314423170
// be told apart from a check somebody deleted — `renderRatePremise`'s rule.
23145-
t('#16393 clause: …and still speaks, so a deleted check cannot pass for a healthy board', clause16393(433.33).startsWith('rate premise OK'), true);
23146-
t('#16393 clause: …naming both rates even when it agrees', clause16393(433.33).includes('observed ~433.3/day') && clause16393(433.33).includes('= 415.1/day'), true);
23171+
t('#16393 clause: …and still speaks, so a deleted check cannot pass for a healthy board', clause16393(166.67).startsWith('rate premise OK'), true);
23172+
t('#16393 clause: …naming both rates even when it agrees', clause16393(166.67).includes('observed ~166.7/day') && clause16393(166.67).includes('= 139.4/day'), true);
2314723173

2314823174
// ---- The clause on the summary line. Only time-INDEPENDENT properties are
2314923175
// asserted here, for the reason above; the verdict wording is pinned by the

0 commit comments

Comments
 (0)