Skip to content

Commit 5a5e710

Browse files
os-try-charlesclaudeos-elon-musk
authored
ci: wire the issue-citation verdict (blocking) and its census (report-only), plus the merged-result probe (#19259)
Fixes #18224 Two gates were registered in the root manifest and invoked by **zero** workflows: `scripts/check-issue-citations.mjs` (delivered by #18223 / card #17512) and `scripts/check-merged-result.mjs` (delivered by #18338 / card #16287). Both cards' declared file surfaces excluded `.github/workflows/**`, so both devs correctly stopped and filed the wiring rather than widening. This PR is that wiring. ## The three entry points, and their postures | entry | lane | posture | | :--- | :--- | :--- | | the DIFF-scoped citation verdict | `lint.yml` · `Lint & Repo Gates` | **blocking** | | `check:merged-result` self-test | `lint.yml` · `Lint & Repo Gates` | **blocking** | | `check-issue-citations --census` | `half-state-patrol.yml` (scheduled) | **report-only, never blocking** | The census posture is a ruling, not a preference, and the card carries the measurement that forces it: **#16783, #16786 and #16787 were measured RESOLVING on 2026-09-10 and 404 on 2026-09-14, with no change to this tree.** A tree-wide blocking verdict would therefore red a motionless repository because a third party deleted an issue. The diff-scoped half is the part an author owns, it is small, and it is the only thing that stops 2,785 unresolvable sites becoming 3,000. ## The manifest alias is NOT the verdict `package.json` maps `check:issue-citations` to `node scripts/check-issue-citations.mjs --self-test` and nothing else -- the shape every credential-needing gate in this manifest uses (`check:pm-half-states`, `check:pm-closed-card-sweep`), because a live mode needs a board and a credential. Wiring that alias alone would have run the self-test twice and scanned nothing. So the lint step holds **two** commands, self-test first: ``` pnpm check:issue-citations && node scripts/check-issue-citations.mjs ``` The second spelling is lint.yml's documented gate-invocation idiom (`dispatch-gates.mjs`'s own header names it), and the first is what `check-self-test-wired` requires of every script CI runs. `check:merged-result` needed no such split: the manifest key already IS `node scripts/check-merged-result.mjs --self-test`, which is the whole gate. ## `package.json` is deliberately untouched No new manifest key was added. Both keys already exist and both are now named by `lint.yml`, so the wiring needs no manifest edit, and leaving the file alone keeps this PR textually disjoint from PR #18414, which adds a key two lines from where a new one would have gone. ## The non-step changes: `issues: read` AND `pull-requests: read` > ⚠️ Heading corrected by the dispatching PM at 2026-09-20T07:25Z: this section was written when the block > gained ONE scope. It now adds TWO — `pull-requests: read` followed from a measurement taken after > the body was written (patrol run 35495222460 censused **3628** unresolvable sites on a token without it, > run 35496169133 on the fixed head censused **2167**, the exact full-scope reading; the 1460 difference is > exactly the `resolves-as-pull-request` tally, because `GET /issues` omits pull requests without that scope). > ⛔ Nothing else in this body was altered. The `Lint & Repo Gates` job's `permissions:` block gains `issues: read`. An explicit `permissions:` block sets every unnamed scope to `none`; this board is public today, but a **blocking** gate whose transport depends on repository visibility is a gate that goes red on a settings change no file in this repo can assert. Read-only, one scope wide: the gate never writes an issue, a comment, a label or an assignee. ## Acceptance 2 -- both directions measured, on the CI side PR #18223 measured the red/green pair on the **gate** side. This card asks for the same pair on the **CI** side. Two instruments, both here. **A. The commits on this branch ARE the CI-side ablation.** `test(ci): ABLATION LEG 1 of 2` adds one citation naming a number beyond this board's allocation frontier, in a declared surface (`packages/**/src/**/*.ts`, comment-prose projection). `ABLATION LEG 2 of 2` removes it and restores the file to the byte. The run on the first head is the red reading; the run on the final head is the green one. Both run identifiers are recorded in the dev report on the card. **B. The exact command the new step holds, ablated locally with disk evidence.** Three legs, run from a committed state, each restored with `git checkout HEAD -- path` and proven by hash: ``` target packages/types/src/index.ts HEAD blob 0235d4e leg 1 unresolvable marker 0 -> 1 blob 0235d4e -> 2ac9ba3 exit 2 RED finding: [never-issued] packages/types/src/index.ts:8 #999999 beyond the allocation frontier (19258) -- this number was never minted leg 2 restored marker 1 -> 0 blob back to 0235d4e exit 0 GREEN `git diff HEAD` empty after the restore leg 3 resolvable cites #17512 instead exit 0 GREEN board: probed (1 citations), frontier #19258, 2 numbers resolve ``` Leg 3 is the control on leg 2: a green produced by a live board read, not by a run that never asked the board anything. ## Acceptance 3 -- where the census reports, how often, who pays Written into the step itself, and repeated here: - **Where.** The patrol run's **step summary** and job log, plus one `::warning::` carrying the site count. Deliberately **not** the anchor issue: that body is owned end to end by `check-half-states.mjs`'s generator, and a second writer is how half a generated body goes stale. - **How often.** This workflow's schedule -- four times a day, six hours apart (`37 1,7,13,19`) -- plus any `workflow_dispatch`, plus the `pull_request` runs the paths filter admits. A row for `scripts/check-issue-citations.mjs` was added to that filter for the reason the file already gives for its two siblings: a step whose script can change without the trigger firing is a step whose PR-time proof is a coincidence. - **Who pays the 159 requests/run.** This repository's own `secrets.GITHUB_TOKEN` core quota -- the same 5,000/hour the job already draws the live sweep from. Four runs a day is roughly 636 requests/day, under half a percent of a single hour's allowance. No PAT and no cross-repo credential, per this file's own standing rule. The step is gated on `github.repository == 'objectstack-ai/objectstack'`, exactly as the closed-card sweep above it is, because `half-state-patrol.yml` is copied verbatim into sibling repos that do not carry this script -- a copy must skip the step, not fail on a missing file. It is placed **after** the anchor write, unlike the closed-card sweep: the anchor is this patrol's product, the job has a 15-minute timeout, and a report-only reading must never be able to starve it. It always exits 0. ## Acceptance 4 -- the 422 wall This diff touches `.github/workflows/**`, which is outside the PM seat's arming channel: the seat's `auto_merge` answers **HTTP 422** for this PR. **It merges by a human.** That is the same wall as PR #18096 and #18341, it is not a tool fault, and it must not be retried. No seat should undraft this PR or arm auto-merge on it. Note that `.github/workflows/**` is *not* on the `GOVERNED_SURFACES` register in `scripts/pm/check-governed-merges.mjs`, so the governed-merge machinery is not what holds this one -- the 422 is. ## Placement, and the three in-flight PRs on these files Read before editing: #18414 (`lint.yml` + `package.json`, green, awaiting a human merge), #19024 (`lint.yml`), #19225 (`half-state-patrol.yml`, draft and frozen). Only additions here; no existing step was moved, renumbered or reformatted. - In `lint.yml` the two steps go **above** the `#15149` step-name-quoting step, which keeps that step's own documented placement ("immediately above" the duration-unit-keys step) true and keeps the duration-unit-keys step last among the gates. #18414 inserts at the control-byte guard (line ~411) and #19024 edits the typecheck lanes (lines ~5173 and ~6104), so all three hunks are disjoint. - In `half-state-patrol.yml` the census step goes between the summary publish and the final fail step. #19225 rewrites that file wholesale into a composite action and is frozen behind a `pm:blocked` card; a textual conflict there is expected and was accepted at dispatch. ## Measurement this PR does not relay The card's prose carries three disagreeing counts for the manifest census. Re-measured on this branch's base `0f42d36ff`, with a firing control and a dark control: ``` root manifest check:* keys 165 not named by any workflow (name or path) 2 -> check:merged-result, check:issue-citations ...and not named by any other script 2 firing control 'check:nul-bytes' in a wf true dark control 'check:zznotreal' in a wf false ``` Both unreached keys are the two this PR wires, so the reading after this lands is 0. ## Acceptance notes - `check-self-test-wired` admits a script when a workflow names it directly or through a root manifest alias, repo-wide rather than per workflow, so the patrol's census step needs no second self-test invocation: the lint step above already runs it. - No changeset: nothing any package publishes moves. Workflow files ship in no package's `files[]`. --- _Generated by [Claude Code](https://claude.ai/code/session_017ef78bLdybu3AffehKkhfk)_ --- _Generated by [Claude Code](https://claude.ai/code)_ --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: os-elon-musk <elon-musk@objectstack.ai>
1 parent 8ddefbc commit 5a5e710

3 files changed

Lines changed: 374 additions & 13 deletions

File tree

‎.github/workflows/half-state-patrol.yml‎

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,11 @@ on:
161161
# as the row above, one file along: a step whose script can change without
162162
# this trigger firing is a step whose PR-time proof is a coincidence.
163163
- 'scripts/pm/sweep-closed-cards.mjs'
164+
# The citation census this workflow also calls (#18224). Same reasoning as
165+
# the two rows above, one file along: a step whose script can change
166+
# without this trigger firing is a step whose PR-time proof is a
167+
# coincidence.
168+
- 'scripts/check-issue-citations.mjs'
164169
# The patrol's body. A glob, not the one file: anything added to that
165170
# directory is part of what runs here.
166171
- '.github/actions/half-state-patrol/**'
@@ -170,9 +175,21 @@ on:
170175
# plus the labels and comment the closed-card sweep is ruled to write. `issues:
171176
# write` is the narrowest scope GitHub offers for either edit; the sweeper the
172177
# patrol calls is read-only against the API by construction.
178+
#
179+
# `pull-requests: read` is READ-ONLY and buys one thing, for the citation
180+
# census step only — MEASURED on run 35495222460, this workflow's own
181+
# pull_request run WITHOUT this row: the census reported 3,628 unresolvable
182+
# citation sites where a full-scope read of the same tree reported 2,168, and
183+
# the difference is 1,460 — EXACTLY the `resolves-as-pull-request` tally.
184+
# `GET /repos/{owner}/{repo}/issues` answers with the pull requests omitted
185+
# unless this scope is held, so every citation naming a PR number was reported
186+
# as a number the board never had. A report-only reading that is wrong by 67%
187+
# is still a machine-readable surface telling a lie. ⛔ Do not drop this row
188+
# as tidying, and ⛔ do not widen it to `write`: nothing here writes a PR.
173189
permissions:
174190
contents: read
175191
issues: write
192+
pull-requests: read
176193

177194
# One patrol at a time. A scheduled run overlapping a manual dispatch would have
178195
# two runs racing to rewrite the same body, and the loser's findings would vanish
@@ -351,3 +368,121 @@ jobs:
351368
elif [ "$code" != "0" ]; then
352369
echo "::warning::closed-card sweep exited $code — at least one card was left UNJUDGED. An unjudged card is not a clean card; see this run's summary."
353370
fi
371+
372+
- name: Census the repo's issue citations
373+
# #17512's gate, wired here by #18224 — the REPORT-ONLY half, and the
374+
# posture is a ruling, not a preference. `--census` judges every
375+
# citation in the gate's declared surfaces (the published release pages
376+
# and package source docblocks), which is ~2,785 unresolvable sites on a
377+
# tree nobody touched, and its verdict is NOT a function of this tree:
378+
# #16783, #16786 and #16787 were measured RESOLVING on 2026-09-10 and
379+
# 404 on 2026-09-14 with no change to this repository. That is exactly
380+
# the shape this workflow exists for — a fact about a live shared board,
381+
# not about whichever change happens to run CI next — so it belongs on
382+
# the patrol lane and ⛔ NEVER on a blocking one. The DIFF-scoped half of
383+
# the same gate is the blocking one and lives in `lint.yml`; the two
384+
# postures are opposite on purpose and ⛔ neither moves to the other's
385+
# lane.
386+
#
387+
# ⛔ NOT in the composite action this job `uses:` above — it is a step of
388+
# the CALLER, for the same reason the closed-card sweep one step up
389+
# states, plus two that are specific to a census of a TREE. All three
390+
# are measurable on this repo rather than argued:
391+
#
392+
# - It is not repo-agnostic. `scripts/check-issue-citations.mjs` is
393+
# objectstack-only, so the action's own "Locate the patrol sources"
394+
# step would have to either name it — and then REFUSE to run in
395+
# every sibling that adopted the action, which is the one thing that
396+
# action exists to make possible — or not name it, and leave a step
397+
# inside the action failing on a missing file in that sibling. The
398+
# repo-name gate below is what answers both, and it belongs where
399+
# the repository is the caller's own.
400+
# - The action runs its scripts from `steps.sources.outputs.root`, the
401+
# tree the ACTION ships from, while the board's checkout is
402+
# `github.workspace`; that split is deliberate and the action's own
403+
# comments carry it. This census's subject is the WORKSPACE tree's
404+
# declared surfaces, so from inside the action a sibling's run would
405+
# census objectstack's own release pages and report the count under
406+
# the sibling's name — a machine-readable surface telling a lie, the
407+
# failure this step's `permissions:` note above is about. In the
408+
# caller the two directories are the same one, on every install.
409+
# - `pull-requests: read` is granted by the `permissions:` block of
410+
# this file. A composite action declares no permissions and the
411+
# action's input documentation names only `issues: write` and
412+
# `contents: read`, so a census inside it would silently depend on a
413+
# scope no adopting caller was told to grant — measured above as a
414+
# reading wrong by 67%. A scope and its one consumer stay in one
415+
# file.
416+
#
417+
# WHERE THE REPORT GOES: this run's step summary and job log, plus one
418+
# `::warning::` carrying the site count. ⛔ NOT the anchor issue — that
419+
# body is owned end-to-end by `check-half-states.mjs`'s generator, and a
420+
# second writer is how half of a generated body goes stale.
421+
# HOW OFTEN: on this workflow's schedule — four times a day, six hours
422+
# apart — plus any `workflow_dispatch`, plus the `pull_request` runs the
423+
# paths filter above admits.
424+
# WHAT IT COSTS AND WHO PAYS: the census enumerates the whole board once
425+
# (159 requests on this repo at the time of writing, cursor-paginated —
426+
# the alternative is one request per distinct number). It is paid by
427+
# THIS repository's own `secrets.GITHUB_TOKEN` core quota, the same
428+
# 5,000/hour this job already draws the live sweep from: ~636
429+
# requests/day at four runs, under half a percent of a single hour's
430+
# allowance. ⛔ No PAT, no cross-repo credential — the file's own rule.
431+
#
432+
# ⛔ Gated on the repository NAME, for the reason the closed-card sweep
433+
# above states: this script is objectstack-only until a sibling has a
434+
# copy, and a verbatim copy of this workflow elsewhere must SKIP rather
435+
# than fail on a missing file.
436+
#
437+
# ⛔ `!cancelled()` rather than an implicit `success()`, and this
438+
# placement has to SPELL that rather than inherit it. While the patrol's
439+
# steps were written inline in this file, this step sat ABOVE the one
440+
# step that fails the job, so it ran whatever the sweep returned. The
441+
# patrol is now a single `uses:` step that goes red itself when the
442+
# sweep could not read the board, so a default `success()` here would
443+
# skip the census on exactly the runs where the patrol is down. This
444+
# step reads neither the sweep's exit code nor its files: a patrol that
445+
# could not reach the board says nothing about whether the citations in
446+
# this tree resolve.
447+
#
448+
# ⛔ This step never fails the job, whatever the census returns —
449+
# findings are not a failure condition here, and neither is a census
450+
# that could not read the board: that is an alarm (`::error::`), not a
451+
# gate. LAST on purpose, after the patrol's own anchor write and after
452+
# the closed-card sweep: the anchor is this patrol's product, this job
453+
# has a 15-minute timeout, and a report-only reading must never be able
454+
# to starve the thing the workflow exists to deliver.
455+
if: ${{ !cancelled() && github.repository == 'objectstack-ai/objectstack' }}
456+
env:
457+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
458+
run: |
459+
set +e
460+
node scripts/check-issue-citations.mjs --census \
461+
> "$RUNNER_TEMP/issue-citations.md" 2> "$RUNNER_TEMP/issue-citations.err"
462+
code=$?
463+
set -e
464+
# Captured with NO pipe in between, for the reason the two steps above
465+
# state at length: piped, `$?` is the pipe's status and a red run and a
466+
# green one read the same.
467+
{
468+
echo "### Issue-citation census — exit $code (report-only)"
469+
echo
470+
echo '```'
471+
cat "$RUNNER_TEMP/issue-citations.md" 2>/dev/null || echo '(no report produced)'
472+
echo '```'
473+
echo
474+
echo '<details><summary>stderr</summary>'
475+
echo
476+
echo '```'
477+
cat "$RUNNER_TEMP/issue-citations.err" 2>/dev/null || true
478+
echo '```'
479+
echo
480+
echo '</details>'
481+
} >> "$GITHUB_STEP_SUMMARY"
482+
cat "$RUNNER_TEMP/issue-citations.err" >&2 || true
483+
if [ "$code" != "0" ]; then
484+
echo "::error::issue-citation census exited $code — the board was NOT read, so this run says nothing about whether unresolvable citations are accumulating. A census that could not run is not a clean census. See this run's summary."
485+
else
486+
sites=$(sed -n 's/^.*census: \([0-9][0-9]*\) unresolvable citation site(s).*$/\1/p' "$RUNNER_TEMP/issue-citations.md" | tail -1)
487+
echo "::warning::issue-citation census: ${sites:-unknown} unresolvable citation site(s) in the declared surfaces. Report-only — the blocking half judges only what a change ADDS."
488+
fi

‎.github/workflows/lint.yml‎

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,27 @@ jobs:
184184
# `scripts/report-unmeasured-gate-tail.mjs`. Nothing else in this job
185185
# reads it, and no step writes anything anywhere.
186186
actions: read
187+
# ⭐ Added by #18224, for the issue-citation step far below: it resolves
188+
# the numbers THIS change cites against the board. Read-only and one
189+
# scope wide — no issue, comment, label or assignee is ever written. It
190+
# is spelled even though this board is PUBLIC, because an explicit
191+
# `permissions:` block sets every unnamed scope to `none`, and a BLOCKING
192+
# gate whose transport depends on repository visibility is a gate that
193+
# goes red on a settings change no file here can assert.
194+
issues: read
195+
# ⭐ AND `pull-requests`, which is NOT redundant with the row above —
196+
# MEASURED, on run 35495222460 of `half-state-patrol.yml`, whose token
197+
# carries `issues` and NOT this scope: its census reported 3,628
198+
# unresolvable citation sites where a full-scope read of the same tree
199+
# reported 2,168, and the difference is 1,460 — EXACTLY the
200+
# `resolves-as-pull-request` tally. `GET /repos/{owner}/{repo}/issues`
201+
# answers with the pull requests omitted, so every citation naming a PR
202+
# number reads as a number the board never had. On THIS blocking step
203+
# that is a false RED on a correct citation, and the frontier probe
204+
# (`?per_page=1&sort=created&direction=desc`) is read from the same
205+
# response — so an understated frontier turns later numbers into
206+
# `never-issued` too. ⛔ Do not drop this row as tidying.
207+
pull-requests: read
187208

188209
steps:
189210
- name: Checkout repository
@@ -4638,6 +4659,105 @@ jobs:
46384659
- name: workspace manifest dependency graph has no cycle
46394660
run: pnpm check:workspace-manifest-cycles
46404661

4662+
# Merged-result budget probe, wired by the card that wired the citation
4663+
# gate below (#18224). `node scripts/check-merged-result.mjs --self-test`
4664+
# IS the whole gate and is exactly what the root manifest registers as
4665+
# `check:merged-result`; the probe landed in #18338 with the file surface
4666+
# of its own card excluding `.github/workflows/**`, so until this step no
4667+
# workflow named it and its self-test ran nowhere. Offline: no build, no
4668+
# network, no credential, sub-second. Its defect class is a MATCHING RULE,
4669+
# which a clean tree cannot tell from a rule that stopped matching — the
4670+
# self-test is the only instrument that can, and an instrument nobody runs
4671+
# is silence.
4672+
- name: Merged-result probe self-test
4673+
run: pnpm check:merged-result
4674+
4675+
# Issue citations (#17512, wired by #18224). TWO commands, and the split
4676+
# is the point:
4677+
#
4678+
# `pnpm check:issue-citations` the checker's own
4679+
# `--self-test` — offline,
4680+
# no board, no credential.
4681+
# `node scripts/check-issue-citations.mjs` the DIFF-SCOPED verdict —
4682+
# the live run, judging only
4683+
# the citations THIS change
4684+
# adds.
4685+
#
4686+
# ⛔ The manifest alias is NOT the verdict. It runs `--self-test` and
4687+
# nothing else — the shape every credential-needing gate in this manifest
4688+
# uses (`check:pm-half-states`, `check:pm-closed-card-sweep`), because a
4689+
# live mode needs a board and a credential. Wiring the alias ALONE would
4690+
# run the self-test twice and scan nothing. Both spellings are here on
4691+
# purpose, self-test first: `check-self-test-wired` requires CI to run the
4692+
# self-test of every script CI runs, and a verdict from a checker whose
4693+
# own cases were never exercised is a verdict about nothing.
4694+
#
4695+
# ⭐ WHY DIFF-SCOPED AND NOT TREE-WIDE, which is the whole ruling. The
4696+
# standing population is ~2,785 unresolvable citation sites, and the
4697+
# predicate is NOT a function of this tree: #16783, #16786 and #16787 were
4698+
# measured RESOLVING on 2026-09-10 and 404 on 2026-09-14, with no change
4699+
# to this repository. A tree-wide blocking verdict therefore reds a
4700+
# MOTIONLESS tree because somebody else deleted an issue, and fails a PR
4701+
# for a change its author did not make and cannot repair. The diff-scoped
4702+
# half is the part an author owns, it is small, and it is the only thing
4703+
# that stops 2,785 becoming 3,000. The tree-wide reading is a `--census`
4704+
# and it runs REPORT-ONLY in `half-state-patrol.yml`, four times a day —
4705+
# never here, and never blocking. ⛔ Do not "upgrade" this step to
4706+
# `--census`.
4707+
#
4708+
# THE DIFF BASE, per event, and the merge_group row is the whole of
4709+
# #19259's ejection:
4710+
#
4711+
# pull_request `git merge-base origin/main HEAD`. CORRECT and left
4712+
# alone: the checked-out merge ref already CONTAINS the
4713+
# main it was computed against, so the merge base IS that
4714+
# main and nothing newer can leak into the added set.
4715+
# push (main) the merge base IS the head, so the added set is empty,
4716+
# the run makes ZERO requests and exits 0 before it asks
4717+
# the board anything.
4718+
# merge_group ⭐ the group's own `base_sha`, declared below. A queue
4719+
# entry is built on the GROUP's base, which carries the
4720+
# entries AHEAD of it in the queue and has NOT landed on
4721+
# `main` yet, so the published `origin/main` this runner
4722+
# fetched is BEHIND that base and the ref guess lands at
4723+
# the published tip — attributing every entry ahead in the
4724+
# queue to THIS change.
4725+
#
4726+
# MEASURED, on this step's own first queue build (entry `a7109d1f08`,
4727+
# 2026-09-20): the ref guess resolved to the published main of 14:30Z and
4728+
# judged 15 file(s) / 16 citations, 3 unresolvable — `#6361` twice from
4729+
# `ada701220` (#19364) and `#18003` from `8271c81425` (#19363), the two
4730+
# entries ahead in the queue, both of which landed on `main` only at
4731+
# 14:36:53Z and 14:47:59Z. The step exited 2 (its findings code) and the
4732+
# queue ejected the PR for citations its author never wrote. Against
4733+
# `base_sha` the same tree judges 0 file(s).
4734+
#
4735+
# ⛔ Do NOT `if:`-skip this step on `merge_group` instead: this file's
4736+
# header asserts that every gate step here runs on merge_group, and a
4737+
# blocking gate turned off on the one event that guards what is about to
4738+
# reach `main` is a gate that is not one. Both halves of the repair are
4739+
# this: the base above, and the gate's own refusal below.
4740+
#
4741+
# `fetch-depth: 0` is what makes either spelling resolvable (the checkout
4742+
# step above says so for its own reason).
4743+
#
4744+
# ⛔ A failed read is never a pass: an unreadable board — and now an
4745+
# unresolvable base, which used to throw an unclassified exit code —
4746+
# exits PREREQUISITE NOT MET rather than reporting a clean tree.
4747+
- name: Issue citations this change adds resolve on the board
4748+
env:
4749+
# The board read is one GET per distinct number the change cites
4750+
# (none at all when it cites none), against a PUBLIC board. Spelled
4751+
# anyway: the gate uses the token when it finds one, and an
4752+
# unauthenticated runner shares one IP's 60 requests/hour.
4753+
GITHUB_TOKEN: ${{ github.token }}
4754+
# Renders the group's base on `merge_group` and EMPTY on every other
4755+
# event, where the gate keeps the `origin/main` / `main` guesses. Same
4756+
# name, same expression, same fact as the gate-family selector above
4757+
# — one fact, one spelling.
4758+
OS_GATE_MERGE_GROUP_BASE_SHA: ${{ github.event.merge_group.base_sha }}
4759+
run: pnpm check:issue-citations && node scripts/check-issue-citations.mjs
4760+
46414761
# #15149. Grep-level guard for the defect this very repo just shipped: an
46424762
# unquoted `- name:` step name containing ` #` is silently truncated by
46434763
# YAML at that point (a space + hash starts a comment inside a plain

0 commit comments

Comments
 (0)