Skip to content

Commit a718ee3

Browse files
os-zhuangclaude
andauthored
docs(check-cross-repo-closer-outcome): repair two evidence citations, and stop hand-counting the battery (#9917) (#10441)
Both items are evidence citations inside comments. No behaviour, no assertion, no scenario and no mutation changes. 1. L9's fixture and `makeDoubles`' `issues.get` default cited objectui#4478 as a closed ISSUE answering `state_reason: null`. That number is a pull request, i.e. exactly the object L13 exists to refuse, so the file offered one observation as evidence for two opposite scenarios. Both sites now model the null from the endpoint's contract and cite nothing; L9 records why a sampled specimen is the wrong kind of evidence for this fixture in particular, and leaves L13's measured pull request (#9143) as the only cited object. 2. lint.yml's step comment hand-counted "Ten scenarios" and "seven ways" against a measured eighteen and fifteen. Both counts are replaced by the mutation CLASSES, the shape the objectui port chose; the totals now live only where they are computed -- the check's own OK line and `--self-test`'s. The same stale enumeration in this script's own header is repaired the same way. Claude-Session: https://claude.ai/code/session_01DdCnBGcHeufjrq7drTD3wt Co-authored-by: Claude <noreply@anthropic.com>
1 parent 0681a76 commit a718ee3

2 files changed

Lines changed: 78 additions & 28 deletions

File tree

.github/workflows/lint.yml

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1081,24 +1081,39 @@ jobs:
10811081
# — one card per silent exit — and each fix lands as more unexercised
10821082
# code. This step is the exercise: the shipped script is extracted from
10831083
# the YAML with a real parser (never retyped) and run under doubles the
1084-
# way actions/github-script runs it, as one AsyncFunction body. Ten
1084+
# way actions/github-script runs it, as one AsyncFunction body. Its
10851085
# scenarios pin the target parse and the outcome of EVERY exit — which of
10861086
# setFailed / warning / job summary fires, and which API calls were made.
1087+
# How many of them there are is deliberately NOT written here (#9917):
1088+
# the check names the total on its own OK line, and `--list` prints the
1089+
# whole table. This comment used to say "ten" against a real eighteen.
10871090
#
10881091
# Assertion 0 is the compile, and it is not theoretical: this job failed
10891092
# twice on 2026-08-02 with `SyntaxError: Identifier 'octokit' has already
10901093
# been declared`, i.e. a script that never ran at all, on a post-merge
10911094
# workflow whose red nothing else in CI can see.
10921095
#
10931096
# Its --self-test runs first and is the half that stops the battery
1094-
# rotting into decoration: it mutates the shipped script seven ways —
1095-
# downgrade the verdict to a warning, stop collecting failed keys, break
1096-
# out of the loop instead of isolating, drop the same-repo skip, narrow
1097-
# the keyword set, drop the already-closed skip, downgrade the notice
1098-
# path's verdict — and requires the battery to go RED for each, naming the
1099-
# scenario that catches it. A mutation whose anchor no longer exists is a
1100-
# failure too, so a rewrite of the workflow cannot leave the mutations
1101-
# silently matching nothing.
1097+
# rotting into decoration: it mutates the shipped script along every
1098+
# CLASS the contract is made of — a verdict downgraded (red becomes a
1099+
# warning, or an info line nothing annotates), the loop's bookkeeping
1100+
# deleted (which keys failed, which half was lost, which target was
1101+
# refused), isolation abandoned mid-loop, the target parse narrowed (the
1102+
# keyword set, the optional colon, the same-repo skip), a guard removed
1103+
# (already-closed, triage, pull-request), idempotency eroded (the
1104+
# backlink marker dropped, or a blind post where a skip belongs) — and
1105+
# requires the battery to go RED for each, naming the scenario that
1106+
# catches it. A mutation whose anchor no longer exists is a failure too,
1107+
# so a rewrite of the workflow cannot leave the mutations silently
1108+
# matching nothing.
1109+
#
1110+
# Classes rather than a count, and rather than the enumeration that used
1111+
# to sit here, because BOTH hand-maintained numbers in this comment had
1112+
# gone stale unnoticed — measured 2026-08-20 from the check's own output,
1113+
# eighteen scenarios and fifteen mutations against a comment claiming ten
1114+
# and seven (#9917). A stale enumeration reads exactly as authoritative
1115+
# as a fresh one, and this comment is what the next reader checks the
1116+
# harness against. Prose classes survive an M16; a count does not.
11021117
#
11031118
# Invoked as `node` rather than through a `pnpm check:*` alias: that alias
11041119
# belongs in root package.json, declared territory of the @changesets/cli

scripts/check-cross-repo-closer-outcome.mjs

Lines changed: 54 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -89,15 +89,26 @@
8989
//
9090
// A battery of assertions over a script that is already correct is green on day
9191
// one and green forever, including the day someone deletes the thing it
92-
// guards. `--self-test` mutates the extracted source -- drop the `setFailed`,
93-
// stop collecting failed keys, `break` out of the loop instead of isolating,
94-
// disable the same-repo skip, narrow the keyword set, collapse the
95-
// already-closed branch, strip the backlink marker, drop the triage guard,
96-
// post blind when the comment listing is refused, stop recording which half
97-
// was lost -- and requires the battery to go RED for each, naming the scenario
98-
// it expects. Each mutation also asserts its own anchor was PRESENT before
99-
// substituting: a mutation that silently matched nothing would leave the
100-
// battery green and read exactly like a passing self-test.
92+
// guards. `--self-test` mutates the extracted source and requires the battery
93+
// to go RED for each mutation, naming the scenario it expects.
94+
//
95+
// What the mutations cover is written here as CLASSES, and their number is not
96+
// written here at all (#9917). The list this paragraph used to carry named ten
97+
// of them and was never updated again; the same habit in lint.yml's step
98+
// comment was counting seven when there were fifteen. A prose class survives
99+
// the next mutation being added, a hand-count does not, and `--self-test`
100+
// prints its own total on every run. The classes: a verdict DOWNGRADED (red
101+
// becomes a warning, or an info line nothing annotates), the loop's
102+
// BOOKKEEPING deleted (which keys failed, which half was lost, which target
103+
// was refused -- each one lets a run report green about work it did not do),
104+
// ISOLATION abandoned mid-loop, the TARGET PARSE narrowed (the keyword set,
105+
// the optional colon, the same-repo skip), a GUARD removed (already-closed,
106+
// triage, pull-request), and IDEMPOTENCY eroded (the backlink marker dropped,
107+
// or a blind post where a skip belongs).
108+
//
109+
// Each mutation also asserts its own anchor was PRESENT before substituting: a
110+
// mutation that silently matched nothing would leave the battery green and
111+
// read exactly like a passing self-test.
101112
//
102113
// One scenario (L11) is driven TWICE, the second run's world built out of the
103114
// first run's calls. Re-run idempotency is a property of the PAIR, and a
@@ -200,9 +211,14 @@ function makeDoubles({ body, token, issues = {}, prCommentError = null, summaryE
200211
calls.get.push(`${owner}/${repo}#${n}`);
201212
const t = target(owner, repo, n);
202213
if (t.getError) throw t.getError;
203-
// `state_reason` is nullable on a real closed issue -- objectui#4478
204-
// answers `null` from this very endpoint -- so the default models that
205-
// rather than inventing a value the API does not promise.
214+
// `state_reason` is nullable BY CONTRACT on this endpoint -- the API
215+
// states a reason when it has one and promises nothing otherwise -- so
216+
// the default models `null` rather than inventing a value the API does
217+
// not promise. Modelled, not sampled, and that distinction is load
218+
// bearing here: the citation that stood on this line named a foreign
219+
// number as a closed issue answering `null` and it was a pull request
220+
// (#9917). L9 carries the reason a sampled specimen is the wrong kind of
221+
// evidence for THIS default in particular.
206222
//
207223
// `pull_request` is how the same endpoint says the number is a PULL
208224
// REQUEST, and it is modelled as ABSENCE rather than as `undefined`
@@ -667,9 +683,25 @@ export const SCENARIOS = [
667683
scenario: () => ({
668684
body: MIXED_BODY,
669685
token: 'pat',
670-
// Measured, not invented: objectstack-ai/objectui#4478 is closed and
671-
// answers `state_reason: null` from `issues.get`. A fix that keys on the
672-
// reason has to say what null means, and this pins the answer.
686+
// MODELLED FROM THE CONTRACT, deliberately not sampled. `issues.get`
687+
// declares `state_reason` nullable and promises no reason on a closed
688+
// issue, so a fix that keys on the reason has to say what null means, and
689+
// this pins the answer -- the shipped script's own reading of it, "no
690+
// objection recorded", is stated on the already-closed branch of
691+
// .github/workflows/cross-repo-issue-closer.yml, which is the subject
692+
// this scenario drives.
693+
//
694+
// A SAMPLED citation stood here and was withdrawn (#9917): it named
695+
// objectui#4478 as a closed issue answering `null`, and that number is a
696+
// PULL REQUEST. Read that as the trap it is rather than as one bad
697+
// lookup -- the objects that most dependably answer `state: 'closed'`
698+
// with `state_reason: null` ARE pull requests, so hunting a specimen for
699+
// this fixture walks straight into L13's subject, and the file then
700+
// offers ONE observation as evidence for two opposite scenarios. L9 and
701+
// L13 are kept on separate footings on purpose: L9 models the contract
702+
// and cites nothing, L13 cites a measured pull request
703+
// (objectstack-ai/objectstack#9143). Do not "improve" this by finding a
704+
// real issue to name.
673705
issues: { [CLOSED_TARGET]: { state: 'closed', stateReason: null, comments: [] } },
674706
}),
675707
check: (r, t) => [
@@ -845,10 +877,13 @@ export const SCENARIOS = [
845877
token: 'pat',
846878
// A merged pull request answers `state: 'closed'` with
847879
// `state_reason: null` from the issues endpoint -- measured on
848-
// objectstack-ai/objectstack#9143. That is the very fixture L9 uses to
849-
// prove a closed ISSUE gets its backlink, which makes this scenario the
850-
// ORDERING test: a `pull_request` guard placed after the state branch
851-
// would sail past it and comment on somebody else's pull request.
880+
// objectstack-ai/objectstack#9143. That is the very fixture SHAPE L9 uses
881+
// to prove a closed ISSUE gets its backlink -- the shape, not the
882+
// observation: L9 models its null from the contract and cites no object,
883+
// precisely so that this measured pull request stays evidence for THIS
884+
// scenario only (#9917). The shared shape is what makes this the ORDERING
885+
// test: a `pull_request` guard placed after the state branch would sail
886+
// past it and comment on somebody else's pull request.
852887
issues: {
853888
[PR_TARGET]: {
854889
state: 'closed',

0 commit comments

Comments
 (0)