Skip to content

ci(gates): every Lint and Type Check gate runs after a red and each lane ends in one verdict step; Test Core continues past a red package - #23212

Merged
os-zhuang merged 7 commits into
mainfrom
claude/issue-23191-gates-run-all-verdict
Oct 13, 2026
Merged

os-zhuang merged 7 commits into
mainfrom
claude/issue-23191-gates-run-all-verdict

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #23191
Clause-②: no

Governed surface, Tier H (.github/workflows/**, and AGENTS.md): this PR stays a draft until the maintainer approves it. No seat readies, queues or arms it.

What the ruling binds (verbatim, #23177 ruling B, comment 6139629628, item 1)

A1, as the card wrote it, in this form: every gate step of a Lint · * lane and of Type Check · * keeps running after a red (continue-on-error: true on the gate steps only; setup, install and build steps that later steps depend on stay fail-fast), and each lane ends with one verdict step that reads every gate's conclusion, prints the names of the red ones and exits 1 — the job's conclusion is still red, never silent. Test Core runs turbo run test … --continue=dependencies-successful so one package's red does not stop the others; its verdict stays red. The #13690 ordering text (lint.yml 151–159) and the "⛔ not continue-on-error" note (502–503) are rewritten in the same PR to say what now holds: the order no longer shortens a red run, the verdict step is what keeps the red. Governed surface: draft until the maintainer approves.

What changes

.github/workflows/lint.yml

  • 240 gate steps across the 8 Lint · * lanes and the 4 Type Check · * lanes now carry id: and continue-on-error: true (eslint 2, repo & tooling 60, docs & code 55, CI/release/engine 58, scoped erasure 2, scoped slot/corpus 3, scoped tooling 3, PM dispatch-gates 1, source gates 33, workspace 2, debt ledger 2, consumer gates 19). Nothing else about any of them moved: base and head were parsed and every step compared with those two keys removed, and all 240 are identical (same run:, if:, env:, order and comment).
  • Fail-fast, untouched: checkout, Node and pnpm setup, the store path, the pnpm cache, the Turbo cache restore, the install, the gate-family selector (id: gate-families), the three workspace builds, and Save Turbo cache (main only).
  • Every lane ends with Lane verdict: if: ${{ !cancelled() }}, env OS_LANE_STEPS: ${{ toJSON(steps) }} and OS_LANE_JOB_STATUS: ${{ job.status }}, run: node scripts/ci/lane-verdict.mjs. The two aggregates (Lint & Repo Gates, TypeScript Type Check) are unchanged: they read each lane's result, and a lane with a red gate still concludes failure because its verdict step exits 1.
  • The unmeasured-tail reporter pair is replaced by the verdict in the 8 Lint lanes, and with it each lane's actions: read scope (it bought only the reporter's jobs-API read). Behind a red gate the tail is now empty by construction; behind a failed setup step the verdict lists the gates that never ran, offline, from the steps context.
  • One new gate step, Lane verdict self-test, in lint-ci-engine beside Aggregator roster gate: the instrument's own self-test, the one place the outcome-versus-conclusion read can go red (on a green tree both readings agree).
  • The two notes the ruling names are rewritten. The [finding] Gate ORDER is a free variable no option in #12413's box touches — the two gates causing 78% of reds sit mid-farm (~45 and ~59 of 130), and the four slowest sit at 7/8/9/19 #13690 GATE ORDER IS DELIBERATE note (the ruling's 151–159) is now EVERY GATE RUNS, AND ONE VERDICT STEP KEEPS THE RED: what is fail-fast and why, why continue-on-error alone would trade the red for silence, what the verdict reads, what a new gate step needs, the one gate that feeds another (check:authorable-surface writes the tree check:docs reads; the second refuses loudly on a missing tree), and that the order no longer shortens a red run. The ⛔ Nor is it continue-on-error note (502–503) stood in the reporter pair's block in lint-eslint; that block is gone, and the verdict step at the same site carries the rewritten note. The lint aggregate's header and the lint-repo-tooling / lint-eslint headers that repeated "stops at its first red" are corrected too.

scripts/ci/lane-verdict.mjs (new; its header is the authority)

  • Reads the lane's roster from the workflow file (GITHUB_WORKFLOW_REF names it, GITHUB_JOB the job): every step whose continue-on-error is set. The steps context holds only steps with an id:, so a roster taken from the context could never see a gate added without one; read from the file, such a gate is UNREAD and red, naming it.
  • Reads each gate's outcome, never its conclusion: under the flag a failed step's conclusion is success.
  • skipped is two facts, told apart by what is certain: a gate with its own if: in a job still success was turned off by that condition (the scoped families); any other skipped is "did not run", red.
  • A failed fail-fast step (job.status not success) is red and names the gates it hid. Zero gates, an unparseable context or a missing input is a refusal (exit 3, PREREQUISITE NOT MET), never a pass.
  • Prints every gate with its verdict, one ::error annotation per red gate, and a job-summary table only when not green. Self-test: 31 cases over 9 named batteries with a floor and a handshake (the AGENTS.md shape).

.github/workflows/ci.yml (Test Core)

  • Both turbo invocations of Run this shard's tests pass --continue=dependencies-successful.
  • A red leg no longer breaks the leg loop: the CLI slice leg runs after a red whole-package leg too, STATUS keeps the first non-zero exit and the step exits with it, so the shard is red exactly when it was before. The ⛔ A failing leg STOPS the remaining ones note is rewritten with the wall argument: the legs together are the shard's own planned work, so a red shard takes what a full one takes, inside the budget the 50-minute wall was sized to.
  • The attestation pair, the completeness guard, the timing steps and check-shard-attestation's static pins are untouched (pnpm check:shard-attestation green below).

AGENTS.md (one sentence)

That job runs its gates sequentially, so the first stale artifact masks every one behind it became false with this change; it now says the lanes run every gate after a red, so one run reports every stale artifact. This file is outside the claim's declared surface; it is here because the change makes the sentence wrong.

scripts/report-unmeasured-gate-tail.mjs (deleted)

Its only caller was the step pair this PR replaces; nothing else in the tree imports or names it (git grep over the whole tree: zero hits outside the two files this PR edits). Its self-test wiring left with it, and check:self-test-wired is green below.

The PR's mechanism assumptions, measured

  1. outcome versus conclusion. GitHub records outcome before continue-on-error is applied and conclusion after it, so a failed gate under the flag reads outcome: failure, conclusion: success. The verdict reads outcome; the self-test battery outcome, never conclusion feeds it exactly that shape and goes red if conclusion were read. The live half (that the runner really records it so, and that a skipped scoped gate appears in the context as skipped) is what the proof run below reads.
  2. Existing infrastructure. No gate forbids a step-level continue-on-error on these lanes: the guards that read the key pin job-level flags (check-required-contexts) or named steps elsewhere (ci.yml's attestation pair, pr-automation.yml, release.yml's gate job, the queue guard's toolchain install), none of which this touches. The lint.yml note about ci: every Test Core run publishes the slowest test files and packages beside their pinned weights (maintainer-directed, part B measurement) #16454 is about ci.yml's timing steps and is unchanged. The aggregators and check-aggregator-roster.mjs are unchanged and green. The unmeasured-tail printing is replaced, not duplicated.
  3. Test Core. A three-package turbo 2.11.5 probe (a#test exits 1, c depends on a): with the flag b and c ran and turbo exited 1 with Failed: a#test; without it the run stopped at a (Tasks: 1 successful, 4 total), exit 1. The flag moves no task hash: --dry=json on this tree, plain against flagged, 5 of 5 identical, so main-seeded caches still replay.
  4. The proof on CI. The branch carries one THROWAWAY commit, 5301220aa, and its revert: a dead docs anchor that reddens Docs anchors resolve to real headings (the first gate of Lint · repo & tooling gates, 59 gates behind it) and a deliberately red test in @objectstack/types (one Test Core shard). The final head's tree is byte-identical to 5a7b95e15, the commit before the throwaway. The readings from that run's jobs API (every later gate's step conclusion, the lane and aggregate conclusions, the verdict's named red, the Test Core shard's turbo tally) are in the dev report comment on ci(gates): every Lint and Type Check gate step runs after a red and each lane ends in one verdict step; Test Core continues past a failed package (#23177 ruling B, item A1) #23191; this body was written once, at creation, before that run finished.

Local verification

Run on 1e695d6b1 (this branch merged with origin/main at 44e2d505c). The next commit, 5a7b95e15, changed comments only (the rewritten note in lint-eslint, one sentence of the verdict script's header); the same union re-run on 5a7b95e15 was still running when this body was written, and its reconciliation is in the dev report.

  • node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack: 68 derived commands; all 68 run and reconciled with --ran: 64 exit 0, 4 NOT MEASURED (exit 3, PREREQUISITE NOT MET: check:dts-closure, check:dual-build-cjs-loads, check:lean-entry-closure, check:sourcemap-no-sources-content read built dist/ trees; this diff changes no package source, and CI's Type Check and Build Core lanes run them). Among the 64: check:pm-dispatch-gates, check:required-contexts, check:shard-attestation, check:select-gate-families, check:self-test-wired, check:self-test-workflow-commands, check:step-collectors, node scripts/check-aggregator-roster.mjs (gate and self-test), check:workflow-status-functions, check:workflow-step-name-quoting, check:nul-bytes, check:pm-governed-prose, check:type-check-debt, node scripts/ci/lane-verdict.mjs --self-test.
  • A simulation of the in-job verdict over the real lint.yml, every lane, with a context built from the file: all 12 lanes OK when every gate succeeds (the four scoped lanes as "turned off by their own if:"), and every lane with an unconditional gate FAILED naming it when its first gate's outcome is failure under conclusion success.
  • ESLint, narrowed and proved: the diff's only linted file is scripts/ci/lane-verdict.mjs (eslint --no-inline-config --format json: 1 file, 0 errors, 0 warnings); eslint.config.mjs never enables type-aware linting, so no untouched file's verdict can move, and the deleted script has no importer.

Acceptance notes

  • check-test-completeness.mjs's NEITHER branch (a package that printed no summary on a run that did not complete is a note, not red) was written for a turbo that stopped at the first red. Under --continue nothing is cancelled except a failed build's dependents, so that note could now be upgraded to red for a silent package beside an unrelated red. Not done: it is the gate's own judging logic, which this card does not touch. Carrier: none.
  • A gate step added later without continue-on-error is fail-fast again (still red, one red per cycle); without an id: the verdict reds the lane naming it. The new note above jobs: says both. No structural gate for the first case was added (new gates default to no).

维护者速读(草稿)

  • 改了什么:Lint · * 八条与 Type Check · * 四条车道里的 240 个门禁步骤,前一个红了后面照跑;每条车道最后一步「Lane verdict」读每个门禁的结果,点名红的并退出 1,车道和两个必需聚合(Lint & Repo Gates、TypeScript Type Check)照样红。Test Core 的 turbo 加 --continue=dependencies-successful,一个包红不再停掉同分片其它包。原「未测尾巴」报告步骤由裁决步取代,脚本删除。
  • 为什么改:按 [Decision] 门禁过严拖慢发版:一轮 CI 暴露全部红、成本型棘轮加容差带、门禁瘦身审计 #23177 裁决 B 第 1 项(A1)。过去一个红门禁会跳过同车道其后所有门禁、turbo 也在第一个红包处停下,一轮 CI 只能看到一个红,每个藏住的红多花一轮 CI 与一次 Version PR 刷新。
  • 风险与代价(含回滚):门禁命令与判定一字未改,只改编排与汇总;红的运行现在会跑满(时长等于绿的运行,在现有 job 时限内)。风险点是裁决步本身:若它读错字段会让红变绿,所以它读 outcome 不读 conclusion、名单从 workflow 文件读、读不到即红,并有自测与 CI 实证。回滚:revert 本 PR 即恢复原 fail-fast 与报告步骤。
  • 席位意见:
  • 你要做的:审阅后批准(Tier H,受管面须维护者批准);批准前本 PR 保持草稿。

Generated by Claude Code

…ct step per lane keeps the red; Test Core continues past a red package

Every gate step of the eight Lint lanes and the four Type Check lanes now
carries continue-on-error and an id; setup, install, the gate-family selector
and the workspace builds stay fail-fast. Each lane ends with a Lane verdict
step that reads every gate's outcome from the steps context, names the red
ones and exits 1, so the lane and its required aggregate still conclude
failure. It replaces the unmeasured-tail reporter pair, whose tail is now
empty behind a gate red and exact (offline) behind a setup red.

Test Core runs turbo with --continue=dependencies-successful and no longer
skips the slice leg after a red whole-package leg; STATUS keeps the first
non-zero exit.

Claude-Session: https://claude.ai/code/session_012zwsLMxQBwHUYJgPyrpEvx
Co-authored-by: Claude <noreply@anthropic.com>
…erted in the next commit

A dead docs anchor reddens 'Docs anchors resolve to real headings', the
first gate of Lint · repo & tooling gates; a red test in @objectstack/types
reddens one Test Core shard. Pushed only to read the jobs API: every later
gate of that lane must execute, the lane verdict must name the red gate, and
turbo must carry on past the red package. Reverted before review.

Claude-Session: https://claude.ai/code/session_012zwsLMxQBwHUYJgPyrpEvx
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/xl ci/cd documentation Improvements or additions to documentation tests labels Oct 13, 2026
@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Oct 13, 2026
@github-actions

github-actions Bot commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs.

What this run could not see

Coarse fallback — 0 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 75cada0ea6859b735635688e07e5fcba324e90e8 → packageMentionDocs.

… fail-fast step that fails, reverted before review

lint-eslint gains an id'd continue-on-error gate whose if: is false, so the
verdict must read it as skipped in the steps context and turned off by its own
condition; lint-scoped-erasure gains a failing step without continue-on-error
before its gates, so the verdict must report the fail-fast failure and the two
gates it hid. Reverted before review.

Claude-Session: https://claude.ai/code/session_012zwsLMxQBwHUYJgPyrpEvx
Co-authored-by: Claude <noreply@anthropic.com>
The head's tree is byte-identical to 5a7b95e again; the proof readings
live in the dev report on the card.

Claude-Session: https://claude.ai/code/session_012zwsLMxQBwHUYJgPyrpEvx
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

维护者速读 · PR #23212(#23191,裁决 B 第 1 条 A1) · domain:devx seat 1 · 2026-10-13T09:41Z

改了什么

为什么

  • chore: version packages (next) #21988 的第三个红被前面的红挡住,是额外跑探针才发现的。
  • 改后一轮 CI 能看到所有红,不必修一个、等一轮、再看下一个。

席位意见

  • ACCEPT(6142742536)。已在真实 CI 上用两个临时提交证明,两个都已 revert:
    • 早期检查红了,后面 59 个检查照跑,裁决步点名红项,聚合检查红;
    • Test Core 某个包红了,后续包照跑。
  • 代价:红的分片会跑满全程(仍在现有超时墙内,最慢的满分片实测 27m05s)。

一个动作

  • 用 os-zhuang 或 hotlong 提交 Approve review。
  • 之后由席位翻 ready 并入队落地;或者你直接合并。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cd documentation Improvements or additions to documentation size/xl skip-changeset PR has no user-facing published change; bypasses the changeset gate tests

Projects

None yet

3 participants