Skip to content

fix(ci): a cancelled Test Core leg with partial attestations counts its roster — a missing shard is red, not green (#16157) - #16316

Merged
baozhoutao merged 1 commit into
mainfrom
claude/issue-16157-attestation-partial-cancel
Sep 6, 2026
Merged

fix(ci): a cancelled Test Core leg with partial attestations counts its roster — a missing shard is red, not green (#16157)#16316
baozhoutao merged 1 commit into
mainfrom
claude/issue-16157-attestation-partial-cancel

Conversation

@claude

@claude claude Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Fixes #16157

Clause-②: yes — required-check semantics change (Test Core accepts fewer leg states than before; see derived judgments).

Ruling executed: option A (issue comment 5559243179, director seat, decision batch #54, maintainer 「同意」). Head sha of every reading below: b79626679.

What changed

judge() in scripts/check-shard-attestation.mjs short-circuited every leg whose aggregate result read cancelled to expected attestations: 0 — before the count — so the required Test Core context went green over a shard that was killed mid-run while its five siblings had attested (run 34007386254, the card's measurement; 43 further instances today, table below). That is #3668's shape only when the whole matrix was superseded and nothing ran; a shard cancelled by its job timeout produces no verdict, so there is no failure to dominate the aggregate.

The cancelled branch is now split by the count, on the same present map --verify reads from the downloaded shard-attest-test-* artifacts:

  • zero attestations of the leg present ⇒ the CI: Dogfood Regression Gate 把 cancelled 当失败 —— 每次连续推送都产生一条假红 #3668 path, byte-for-byte: satisfied (cancelled — run-lifecycle state, #3668; expected attestations: 0), no counting.
  • at least one attestation present ⇒ the leg logs cancelled AFTER k of N declared shard(s) attested — not a superseded matrix; the roster is REQUIRED and every missing shard is named (#16157) and falls through to the ordinary count: every missing shard is a MISSING line, the error names the missing shard(s) and the sibling(s) that did attest, and the gate exits 1.

One counting path, not two: the partial-cancellation case reuses the roster count that success/abandoned/leaked values already go through, so the #11998 attempt-scoping rules apply to it unchanged. The header's "What is deliberately NOT changed" bullet now states both shapes; the pinned self-test assertion "a leg that attested before the cancellation is allowed, not required" is overturned and rewritten; the #3668 lifecycle battery is renamed to the case it actually measured; a #16157 battery pins both directions. No ci.yml edit: the gate already receives --leg "test/6:$OS_TEST_RESULT" and the artifact download step is unchanged.

Derived judgments — the required check's accept/reject set, before and after

leg aggregate result attestations of the leg present before (origin/main at 1ca95df16) after (b79626679)
cancelled 0 of N pass, not counted (#3668) unchanged — pass, not counted, same log line
cancelled 1..N-1 of N pass, expected attestations: 0 (the defect) RED — every missing shard named, siblings that attested named
cancelled N of N pass, not counted pass, counted N/N (all N declared shard(s) published a positive attestation)
failure any red (declared negative vetoes a full roster) unchanged
success / abandoned / any leaked value N of N pass unchanged
success / abandoned / any leaked value fewer than N red, missing named (#6082) unchanged
skipped, filter success 0 pass, expected-N adjusts to 0 (#4928) unchanged
skipped, filter success any credential red (contradicts the skip) unchanged
skipped, filter cancelled any pass, allowed-not-required (#3668) unchanged — see note
skipped, filter other red (#4928) unchanged
empty (did not interpolate) red (#4690) unchanged
no legs declared red (#4690) unchanged

Note on the skipped-while-filter-cancelled branch (judge(), the block right under the cancelled one): left as is. The ruling's logic keys on a shard of the leg having run — "the matrix ran, so a cancelled sibling was killed mid-suite". A skipped leg scheduled nothing, so no shard of it can have been killed mid-run; the only credential that can sit there is a carried-over earlier-attempt one, which is #11998's domain. The distinction is therefore not forced, and the branch is out of scope per the dispatch; flagging it here so the reviewer can disagree.

Blast radius — every ci.yml run since 2026-09-06T00:00Z with a cancelled Test Core (k/6) shard

Read from the Actions API only (/actions/workflows/ci.yml/runs?event=merge_group|pull_request&created=…, then /runs/{id}/jobs?filter=latest, /pulls/{n}); nothing was re-run. 252 runs scanned (110 merge_group, 142 pull_request); 48 had at least one cancelled shard.

Summary:

Real-data reading on run 34033611402 (queue group of #16286, the 6th confirmation): the artifact listing for that run names exactly shard-attest-test-1-of-6, -2-, -3-, -4-, -6-of-6 (shard 5 absent). Replaying --verify at b79626679 over a fixture directory emitted with that exact roster and --leg test/6:cancelled exits 1 with 1 of 6 declared shard(s) of test published no positive attestation (test-5-of-6) while 5 sibling(s) did. The artifact BYTES themselves are NOT MEASURED — the blob host answers CONNECT 403 from this container — so the fixture battery is the evidence, with the artifact names as the roster reading.

run event PR shards cancelled shards success aggregate shape merged which run gated the merge?
34034041605 merge_group #16288 5 1,2,3,4,6 success partial (5 attested) 13:16Z 1ca95df queue run of the merge
34033611402 merge_group #16286 5 1,2,3,4,6 success partial (5 attested) 13:08Z 7a01847 queue run of the merge
34031266939 merge_group #16283 5 1,2,3,4,6 success partial (5 attested) 12:19Z 11f848e queue run of the merge
34030159525 merge_group #15331 5 1,2,3,4,6 success partial (5 attested) 11:55Z d53335e queue run of the merge
34029787619 merge_group #16199 5 1,2,3,4,6 success partial (5 attested) 11:48Z 00e6413 queue run of the merge
34029786607 merge_group #16246 5 1,2,3,4,6 success partial (5 attested) 11:47Z 99261a7 queue run of the merge
34023108382 merge_group #16235 5 1,2,3,4,6 success partial (5 attested) 09:24Z 6573af9 queue run of the merge
34022950479 merge_group #16238 5 1,2,3,4,6 success partial (5 attested) 09:21Z 6136293 queue run of the merge
34022742517 merge_group #16257 5 1,2,3,4,6 success partial (5 attested) 09:16Z 1b5bc6b queue run of the merge
34022508216 merge_group #16254 5 1,2,3,4,6 success partial (5 attested) 09:11Z 8472dae queue run of the merge
34021657963 merge_group #16220 5 1,2,3,4,6 success partial (5 attested) 08:53Z 6a1e382 queue run of the merge
34021499707 merge_group #16250 1 2,3,4,5,6 success partial (5 attested) 08:49Z 8adb9e8 queue run of the merge
34021483232 merge_group #16248 1 2,3,4,5,6 success partial (5 attested) 08:49Z 720bf47 queue run of the merge
34021297370 merge_group #16240 1 2,3,4,5,6 success partial (5 attested) 08:45Z 6971170 queue run of the merge
34015590176 merge_group #16188 1 2,3,4,5,6 success partial (5 attested) 06:36Z cf8cc8c queue run of the merge
34014743295 merge_group #15958 1 2,3,4,5,6 success partial (5 attested) 06:15Z 4a85d97 queue run of the merge
34014351770 merge_group #16162 1 2,3,4,5,6 success partial (5 attested) 06:06Z 4c0b22b queue run of the merge
34012774720 merge_group #16156 1 2,3,4,5,6 success partial (5 attested) 05:27Z 48b0fcf queue run of the merge
34011900148 merge_group #16153 1 2,3,4,5,6 success partial (5 attested) 05:06Z a7cce55 queue run of the merge
34011535618 merge_group #16139 1 2,3,4,5,6 success partial (5 attested) 04:58Z bdc0218 queue run of the merge
34011478569 merge_group #16131 1 2,3,4,5,6 success partial (5 attested) 04:57Z 2c8e7ea queue run of the merge
34010594191 merge_group #16113 1 2,3,4,5,6 success partial (5 attested) 04:35Z d5d8d50 queue run of the merge
34010209580 merge_group #16143 1 2,3,4,5,6 success partial (5 attested) 04:27Z 7d7ca6c queue run of the merge
34010176715 merge_group #16144 1 2,3,4,5,6 success partial (5 attested) 04:25Z 92dc937 queue run of the merge
34008620296 merge_group #16117 1 2,3,4,5,6 success partial (5 attested) 03:48Z b45c3f6 queue run of the merge
34001065141 merge_group #15966 1 2,3,4,5,6 success partial (5 attested) 00:52Z 6530e04 queue run of the merge
34034071230 pull_request #16298 3,4 1,2,5,6 success partial (4 attested) no PR open, not merged
34030798999 pull_request #16252 5 1,2,3,4,6 success partial (5 attested) no PR open, not merged
34023957613 pull_request #15675 1,3,4 2,5,6 success partial (3 attested) 12:37Z 7beaaa3 no — head moved (c895b1238d66fe)
34020321603 pull_request #16252 1 2,3,4,5,6 success partial (5 attested) no PR open, not merged
34020085795 pull_request #16220 1 2,3,4,5,6 success partial (5 attested) 08:53Z 6a1e382 no — head moved (d911e6b860e251)
34019472845 pull_request #16246 1 2,3,4,5,6 success partial (5 attested) 11:47Z 99261a7 YES — run on the merged head sha
34019399541 pull_request #16220 1,2,5 3,4,6 success partial (3 attested) 08:53Z 6a1e382 no — head moved (a039f1e860e251)
34019254508 pull_request #16220 1 2,3,4,5,6 success partial (5 attested) 08:53Z 6a1e382 no — head moved (4e2869e860e251)
34018547387 pull_request #16243 1 3,4,5,6 (2:failure) failure partial (4 attested) no PR open, not merged
34018298477 (attempt 2) pull_request #16240 1,2,3,4,5,6 success re-run: latest attempt fully attested (attempt-1 shard cancellation cured) 08:45Z 6971170 YES — run on the merged head sha
34015462817 pull_request #16212 1 2,3,4,5,6 success partial (5 attested) 07:55Z 29bef09 no — head moved (3909a114731e99)
34014464537 pull_request #16199 1 2,3,4,5,6 success partial (5 attested) 11:48Z 00e6413 YES — run on the merged head sha
34014233014 pull_request #16196 1 2,3,4,5,6 success partial (5 attested) 11:47Z 5ca314a YES — run on the merged head sha
34013827131 pull_request #16191 1 2,3,4,5,6 success partial (5 attested) 11:48Z ab50c8f YES — run on the merged head sha
34009395649 (attempt 2) pull_request #16154 1,2,3,4,5,6 success re-run: latest attempt fully attested (attempt-1 shard cancellation cured) 05:35Z 33388f9 YES — run on the merged head sha
34007386254 (attempt 2) pull_request #16131 1,2,3,4,5,6 success re-run: latest attempt fully attested (attempt-1 shard cancellation cured) 04:57Z 2c8e7ea YES — run on the merged head sha
34007103200 pull_request #16113 1 2,3,4,5,6 success partial (5 attested) 04:35Z d5d8d50 no — head moved (ecea92c990b3d4)
34005984440 pull_request #16123 1 2,3,4,5,6 success partial (5 attested) 03:54Z 4b0508e no — head moved (33c6aabf8e396c)
34005462440 pull_request #16115 1,2,3,4,5,6 success whole matrix (0 attested) 04:18Z be75493 no — head moved (9ebb0cb1c032a2)
34002509125 pull_request #16101 1 2,3,4,5,6 success partial (5 attested) 04:25Z 07f40e5 YES — run on the merged head sha
34002158061 pull_request #16088 1 2,3,4,5,6 success partial (5 attested) 03:24Z cf9bda4 no — head moved (1749a0922112c9)
34000944838 pull_request #16088 1 2,3,4,5,6 success partial (5 attested) 03:24Z cf9bda4 no — head moved (842b69922112c9)

Cause of the cancellations is #16173 (shard duration straddling the 30-minute job timeout; parallel card, not a prerequisite). After this PR lands, each such cancellation is a red Test Core instead of a silent pass — a queue entry ejected rather than merged untested — until #16173's timings refresh removes the timeouts. That is the accepted cost of the ruling.

Self-test battery

Ablation (at b79626679, committed first; restore by blob hash)

Mutation: the new guard if (attested.length === 0) rewritten to an always-true comparison (attested.length at-or-above 0), which restores the pre-PR short-circuit exactly. On-disk proof before/after: marker count 0 → 1, original count 1 → 0, working blob 9aba2c8e…191edd2c….

--- self-test under ablation
SELFTEST_EXIT=1
✗ check-shard-attestation --self-test -- 10 failure(s)
  • #16157: a cancelled leg with 1 of 3 attested ⇒ red (the overturned #3668 pin, inverted)
  • #16157: the error names EVERY missing shard and its issue
  • #16157: the error also names the sibling(s) that did attest — the reason the roster is knowable
  • #16157: the log states that the leg fell through to the count and why
  • #16157: a partially attested leg never prints the zero-expectation line that made the transcript contradict itself
  • #16157 measured sequence: shard 2/6 killed mid-run, five siblings attested ⇒ red
  • #16157 measured sequence: exactly the killed shard is named as missing, none of the five that attested
  • #16157: that green is a COUNTED verdict (6/6), never "none claimed to"
  • #16157 × #11998: attempt 2 cancelled with five carried-over credentials ⇒ red naming the sixth
  • #16157: a red partial cancellation prints no satisfied line at all
--- --verify over the measured partial shape (5 of 6 attested, leg cancelled) under ablation
VERIFY_EXIT=0
  leg test (declared roster 1..6/6) — aggregate result: cancelled
    satisfied (cancelled — run-lifecycle state, #3668; expected attestations: 0)
Test Core: satisfied — no shard was expected to run, and none claimed to.
--- restoration proof
git diff HEAD: empty
working blob: 9aba2c8e37893856f8c569f6612e7bf072eb2035
HEAD blob:    9aba2c8e37893856f8c569f6612e7bf072eb2035
RESTORED: blob hashes match and are non-empty
marker count after restore: 0

The 4 cases of the new battery that stay green under ablation are the unchanged directions (zero-attestation pass, 6/6 pass, failure dominance, six carried-over credentials) — as intended. --verify under ablation exits 0 over five downloaded credentials and prints none claimed to: the self-contradicting transcript the card quoted. With the change (same fixture): exit 1, ::error::Test Core: 1 of 6 declared shard(s) of test published no positive attestation (test-2-of-6) while 5 sibling(s) did (test-1-of-6, test-3-of-6, test-4-of-6, test-5-of-6, test-6-of-6). The leg was cancelled mid-run, so the missing shard(s) produced no verdict at all — an untested shard is not a passing shard, see #16157.

No dist/ is involved (the gate runs scripts/ source directly under node), so no preflight beyond the on-disk grep counts applies.

Gates (all at b79626679, each exit code captured by redirect, not through a pipe)

  • node scripts/pm/dispatch-gates.mjs --changed --repo objectstack-ai/objectstack --ran …✓ dispatch-gates --ran: 31 derived famil(ies) accounted for — 31 run, 0 NOT-MEASURED. All 31 exit 0, including pnpm check:shard-attestation (✓ check-shard-attestation --self-test: 144 assertions … + ✓ check-shard-attestation: 2 aggregate gate(s) count 3 declared leg(s) across 3 attesting job(s).), node scripts/check-self-test-wired.mjs (+ --self-test), pnpm check:nul-bytes, pnpm check:pm-dispatch-gates, pnpm check:cross-package-test-inputs.
  • pnpm check:required-contexts (named by the dispatch; imports this script) → exit 0: ✓ check-required-contexts --self-test: 153 assertions … / ✓ check-required-contexts: 6 required context name(s) pinned across 2 workflow(s) ….
  • eslint --no-inline-config scripts/check-shard-attestation.mjs → exit 0. The repo-wide pnpm lint is CI's run; not narrowed here beyond the one changed file (type-aware linting is not enabled in eslint.config.mjs, so this diff moves no other file's verdict).
  • Changeset: none — scripts/ tooling, nothing published from any package; skip-changeset applied.

Out of scope, noted for the reviewer


Generated by Claude Code

…ts roster

`judge()` in scripts/check-shard-attestation.mjs short-circuited every leg
whose aggregate result read `cancelled` to "expected attestations: 0",
before the count. That is #3668's shape (whole matrix superseded, zero
shards ran) and stays; but a shard killed mid-run by its job timeout
produces no verdict, no `failure` exists to dominate, and the required
`Test Core` context went green over its untested packages (run
34007386254, five of six attested).

Split `cancelled` by the count: zero attestations of the leg keep the
#3668 pass verbatim; at least one attestation makes the declared roster
REQUIRED, every missing shard is named, and the gate is red. The pinned
self-test assertion "a leg that attested before the cancellation is
allowed, not required" is overturned and rewritten; a #16157 battery pins
both directions, the measured 6-shard shape, the dominance experiment and
the #11998 attempt-scoping interplay. Battery floor 15 -> 16.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vbw3RPgdtqesx4azk9SbW8
@claude claude Bot added needs:contract-review skip-changeset PR has no user-facing published change; bypasses the changeset gate labels Sep 6, 2026
@github-actions github-actions Bot added the size/m label Sep 6, 2026
@baozhoutao
baozhoutao marked this pull request as ready for review September 6, 2026 14:23
@baozhoutao
baozhoutao enabled auto-merge September 6, 2026 14:24
@baozhoutao
baozhoutao added this pull request to the merge queue Sep 6, 2026
Merged via the queue into main with commit b9a14dd Sep 6, 2026
43 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-16157-attestation-partial-cancel branch September 6, 2026 14:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants