Repository navigation
ci: lint.yml's scheduled run skips the battery when a green push run already covers the sha (3 of 3) - #27085
Merged
Conversation
…already covers the sha A new first job, schedule-gate, runs on `schedule` only and asks the runs API for a completed `push` run of lint.yml on github.sha that concluded success; every lane needs it under `!cancelled()` and skips on skip=true, and the two aggregates skip with their lanes on a scheduled run only. Fails closed toward running the battery. Claude-Session: https://claude.ai/code/session_01QSeKYmhU5KEj1nukYhp1Tg Co-authored-by: Claude <noreply@anthropic.com>
… the pin lane The lane hosting the required-context pin may now carry exactly one job-level if: -- lint.yml's scheduled-run skip, admitted only as the whole shape (byte-exact lane if, needs on schedule-gate, a gate job gated to schedule alone). Any other if: is still refused; six self-test cases pin both directions. Claude-Session: https://claude.ai/code/session_01QSeKYmhU5KEj1nukYhp1Tg Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QSeKYmhU5KEj1nukYhp1Tg Co-authored-by: Claude <noreply@anthropic.com>
…nt-schedule-skip Claude-Session: https://claude.ai/code/session_01QSeKYmhU5KEj1nukYhp1Tg Co-authored-by: Claude <noreply@anthropic.com>
Contributor
Author
维护者速读 — 席位终稿 · PR #27085 · head
|
os-zhuang
approved these changes
Oct 28, 2026
os-zhuang
marked this pull request as ready for review
October 28, 2026 04:59
This was referenced Oct 28, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #26071
Clause-②: no
Metadata-protocol: none
Ruling-ref: 6416984681
维护者速读
lint.yml每 4 小时的定时运行新增第一个 jobschedule-gate:同一 commit 上已有跑完且全绿的 push 运行时,其余 14 个 job(12 条 lane 和 2 个汇总)全部跳过;PR、合并队列、push、手动触发的行为不变。check-required-contexts.mjs的一条接线断言,现在多接受一种精确拼写。回滚:revert 本 PR。.github/workflows/lint.yml里的schedule-gatejob 和两个汇总 job(lint、typecheck)的if:;scripts/check-required-contexts.mjs的pinLanesCarryNoArrangeableIf。pnpm check:required-contexts && pnpm check:workflow-status-functions && pnpm check:pm-expected-skips,应看到三组 ✓。合并后在 Actions 里看下一次Lint & Type Check定时运行:要么只有Schedule gate一个 job 在跑、其余显示 skipped,要么(push 还没跑完时)全量运行。.github/workflows/**)。请审阅并 Approve,之后由席位落地。What changes
Step (i) of ruling 6416984681 (letter A), the last of the card's three serial PRs. Steps (ii) and (iii) landed as #26512 (
3fcafcd3) and #26570 (9bfd6a62).schedule-gate(Schedule gate · skip when a green push run covers the commit). Job-levelif: github.event_name == 'schedule',permissions: actions: readonly, no checkout,timeout-minutes: 10(the file's job-wall floor). It runs one step,gh api repos/REPO/actions/workflows/lint.yml/runs?event=push&branch=main&head_sha=SHA&status=completed&per_page=10, and keeps only runs withevent == push,status == completed,conclusion == successandhead_sha == github.sha. It setsskip=trueonly on a positive match. Every other outcome setsskip=false: no run, a red run, a run on another sha, a failed read (403 included), or a body that does not parse. The step has noset -e, so a read failure cannot fail the job. If the job crashes anyway, its output reads empty and the lanes still run.needs: [schedule-gate]andif: ${{ !cancelled() && needs.schedule-gate.outputs.skip != 'true' }}. On any event other thanschedulethe gate job is skipped and its output is the empty string.!cancelled()runs a job past a skipped or failed need, so each lane runs as before. This is ci.yml's existing!cancelled() && needs.filter.outputs.*idiom. No lane body changed: a yaml-parsed comparison againstHEADfinds every job identical apart fromneeds/if.Lint & Repo Gates,TypeScript Type Check) keepalways(). Each gains one schedule-only conjunct:${{ always() && !(github.event_name == 'schedule' && contains(needs.*.result, 'skipped')) }}. On any other event that conjunct is true, soalways()decides exactly as before, and a skipped required context can never appear on a PR or a queue build. Theirneeds:and rosters are unchanged, socheck:aggregator-rosterand their run-time roster verdicts are unaffected.on:comment block now describes the skip, how it fails closed, and why ci.yml's schedule is not gated (therepushtests the affected set only). Theschedule:cron, the other triggers and theconcurrency:key are byte-identical.scripts/check-required-contexts.mjs: the wiring assertion "the lane hosting the required-context pin must carry NO job-levelif:" went red, because the pin lane (lint-ci-engine) now carries the skip. It now admits exactly one shape and still refuses every otherif:. That shape is the lane's byte-exact skipif:, plusneeds:onschedule-gate, plus aschedule-gatejob whoseif:is byte-exactlygithub.event_name == 'schedule'. There are six new self-test cases in both directions, and the battery floor rises from 13 to 23, its measured count (17 before, plus the 6).REQUIRED_CONTEXTSis untouched.Job-by-job, before → after. Before this PR every job ran on every event.
schedule-gate(new)skip=trueskip=falseLint · *,Type Check · *)Lint & Repo Gates(required)TypeScript Type Check(required)Tests
All local readings are on head
8fd56eab(the final commit, after mergingorigin/main5cba8fc2).Derived gate union: 61 of 61 exit 0
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack(no paths; 2 paths vs merge base5cba8fc2f, 186 changed lines) derived 61 commands. All 61 ran with exit codes captured before any pipe, all 0. Among them:check:required-contexts,check:workflow-status-functions(58 job-levelif:, 29 readneeds.*.outputs.*, all naming a status function),check:pm-expected-skips(206 self-test cases),check:aggregator-roster,check:ci-filter-parity,check:shard-attestation,check:select-gate-families,node scripts/ci/scheduled-full-run.mjs --self-test,check:nul-bytes, andcheck:pm-dispatch-gates(fast tier, 1935 cases).node scripts/pm/dispatch-gates.mjs --ran FILE --repo objectstack-ai/objectstack:61 derived famil(ies) accounted for — 61 run, 0 NOT-MEASURED (a DERIVED zero — all 61 recorded an exit code and none of them is 3).The first derivation, on the lint.yml-only commit, named 42 families. 41 were green and
check:required-contextswas red withwiring: the lane hosting the required-context pin must carry NO job-level if:, which led to the gate change above.Reverse verification on the live file (committed first, trap-restored)
Each mutation was made through
node scripts/ablation-replace.mjs(anchor hit 1 → 0, blob changed), run againstnode scripts/check-required-contexts.mjs --self-test, and restored withgit checkout HEAD -- ABSOLUTE_PATHunder anEXIT INT TERMtrap.schedule-gatejob'sif:widened togithub.event_name != 'merge_group'(the gate could now say skip on a PR): exit 1,wiring: the lane hosting the required-context pin must carry NO job-level if: other than the scheduled-run skip.if:respelledneeds.schedule-gate.outputs.skip == 'false': exit 1, same finding.registered 23 case(s), below its pinned floor of 999. That shows the six new cases register, and the floor is now 23.git hash-objectof both files equals theHEADblob,git diff HEADandgit status --porcelainare empty, and the self-test exits 0 again.The gate step's behaviour, offline and live
run:body was extracted from the parsed YAML and driven with a fakeghthat applies the real--jqfilter throughjq. Results: green push run on the sha →skip=true(it picks the success run past a cancelled one on the same sha); red →false; success on another sha →false; empty listing →false; HTTP 403 →false(the runs API did not answer (… HTTP 403)); non-JSON body →false. The step exits 0 in all six cases.total_count=1with one green push run each; an all-zero sha returnstotal_count 0.yamlparse with no errors;onkeys,on.scheduleandconcurrencyare identical toHEAD.actionlint: NOT MEASURED, because it is not on PATH in this container.ui/scripts/__tests__/merge-queue-reporting.test.ts, the one test file naming the edited gate script (in prose), ran under the verify lock:Test Files 1 passed (1),Tests 4 passed (4),VERDICT command-exit 0.test:repo,pnpm lintand whole-tree scans are CI's. No dogfood run: there is no!in the title and no(narrowing)arm.Acceptance notes
success, and that still holds for all 13 newest. But the ruled mechanism skips only on a completed green push run, checked when the scheduled run starts, and that holds for 4 of 13:f708cf22,6390fe52,f143cd88,05f7758f. In the other 9, the push run of the same commit finished 8 to 18 minutes after the scheduled run started (each push run's last jobcompleted_atvs the schedule'srun_started_at, 2026-10-26T06:25Z → 2026-10-28T04:14Z). A scheduled run starts late past its cron slot andmainmerges many times a day, so the head it tests is often a commit whose push run is still in flight. Those 9 run the full battery exactly as today; nothing is lost, only less is saved. Waiting for an in-progress push run would have skipped all 13, but that is a runner-spend shape the ruling did not name, so it is raised to the seat rather than built here.success. The first post-merge scheduled run is the measurement.scheduled-full-run.mjs --release-gatereads the newestschedule/pushrun on the nearest commit. On a skipped commit that is either this run or the green push run of the same sha, so both answersuccess, as the battery did.scheduled-full-run-card.ymlfiles only onfailure/timed_outand is unaffected.lint-ci-enginethat read GitHub state (issues: read,pull-requests: read) are not re-read at schedule time on a skipped run. The next merge's push run re-reads them.scripts/check-required-contexts.mjssits outside the claim's literal file surface ("a gate that reads that leg by name") but inside the dispatch's instruction to re-point what asserts on the changed shape. It touches only the wiring predicate and its self-test;REQUIRED_CONTEXTSis unchanged. No other gate read the shape: the 61-family union is green.mainrun. The scheduled skip is the one exception, stated in theschedule-gateheader. The block itself was left unedited, per the scope fence.git merge-tree --write-treeof this head withb782ee2dexits 0 and writes a tree.lint.ymlis not routed to the custom merge driver.Verification
.github/workflows/**): the PR stays draft until an authorized approval. The seat lands it; nothing here readies, arms or enqueues it.lint.yml(no paths filter). Onpull_requestthe newSchedule gatecheck-run readsskipped, whichcheck-expected-skipsadmits asnon-pr-event schedule; derived. Every lane and both aggregates run as before.Generated by Claude Code