Skip to content

ci(dogfood): the sharded gate builds its closure in a passthrough-free run, so the shards stop scattering it - #17867

Merged
claude[bot] merged 1 commit into
mainfrom
claude/issue-16886-dogfood-turbo-passthrough
Sep 12, 2026
Merged

ci(dogfood): the sharded gate builds its closure in a passthrough-free run, so the shards stop scattering it#17867
claude[bot] merged 1 commit into
mainfrom
claude/issue-16886-dogfood-turbo-passthrough

Conversation

@claude

@claude claude Bot commented Sep 12, 2026

Copy link
Copy Markdown
Contributor

Clause-②: no

Closes #16886

The dogfood: job carried the same run-level Turbo passthrough defect that PR #16868 fixed in the test job for #16395. It was outside that card's declared surface, so it was still there. This applies #16868's landed diff as the template rather than a second working spelling — the two jobs are now spelled the same way.

The site, measured on this branch (⛔ not the card's line number)

The card said .github/workflows/ci.yml:1283; found by content, the run sits at :1520 on the merge base c88fa2ccd and at :1579 after this diff. The job header is :1423 and name: is :1431 — the card's :1186 / :1209 are 2026-09-08 readings and this file moves constantly.

1520:            pnpm turbo run test --filter=@objectstack/dogfood --log-order=stream -- --shard=${{ matrix.shard }}/3

The change

  1. A dedicated, guard-wrapped, passthrough-free pnpm turbo run build --filter=@objectstack/dogfood --concurrency=4 --log-order=stream step ahead of the sharded run, so the closure is built once and reaches the shared, content-addressed build cache.
  2. --only on the sharded turbo run test, so the passthrough hash covers only the test task it is meant for.

It is its own step, not a second guarded run inside the existing one, for the reason #16868 states: a guarded SITE is the triple (file, job, step), and measure-stall-guard-headroom refuses a verdict when two guarded runs share one.

name: is untouched (required-status-check context) and timeout-minutes: 30 is untouched — the card names that budget as the instrument that shows the fix working, so it may not be raised to buy room.

Cache reading — before / after, --dry=json task hashes

turbo 2.10.10, --filter=@objectstack/dogfood, on this branch. The plain test plan is 67 tasks: 66 build + 1 test.

Before — every task in the run re-hashed per shard:

plain vs plain (control, fires)   tasks 67/67   identical=67  changed=0
plain vs -- --shard=1/3           tasks 67/67   identical=0   changed=67
-- --shard=1/3 vs 2/3             tasks 67/67   identical=0   changed=67
-- --shard=2/3 vs 3/3             tasks 67/67   identical=0   changed=67

So the three shards hashed one 66-package build closure three ways, and none could reach the build cache every other job in this workflow populates — that cache is written with no passthrough in the hash.

After — the two runs the new spelling issues:

turbo run build --filter=@objectstack/dogfood        67 tasks, every one a #build task
  vs the plain test plan's ^build closure (66)       hash-identical=66  differing=0  missing=0
  (the 67th node is @objectstack/dogfood#build itself, whose command is
   NONEXISTENT in the dry output because the package declares no `build`
   script — it executes nothing and is only the root its 66 `^build`
   dependencies hang from)

turbo run test --filter=@objectstack/dogfood --only -- --shard=k/3
  k=1: 1 task   k=2: 1 task   k=3: 1 task   -> ['@objectstack/dogfood#test']
  --only shard=1/3 vs itself (control, fires)   identical=1  changed=0
  --only shard=1/3 vs --only shard=2/3          identical=0  changed=1

The 66 closure builds now replay instead of re-executing per shard: their hashes are identical to the passthrough-free test plan's, which is what the shared cache holds. The one remaining per-shard hash difference is the test task itself, which is correct and intended — that is the task the passthrough is for.

⭐ The sweep: --filter plus a run-level passthrough, whole tree

Triage asked for the count before closing, so here is the probe and the count rather than a claim.

Probe shape. A tokenizing scan (shlex, quote-stripped, backslash continuations joined first) over all 37 files in .github/workflows/. For each turbo run occurrence it tokenizes from the turbo token onward and asks three questions: does it carry --filter/-F; is there a bare -- token after turbo run with at least one argument behind it; does it carry --only. Starting at the turbo token is the load-bearing part — a naive -- grep reports set -- pnpm turbo run … (the shell builtin) and node run-with-stall-guard.mjs … -- pnpm turbo … (the wrapper's own separator) as passthroughs. That first, naive grep over this same tree produced five hits, of which three were the wrong -- entirely; the classification below is from the tokenizing probe.

37 files, 40 turbo run textual matches — 23 executable, 17 prose in comments (classified, not dropped).

Count: 3 sites carry --filter plus a run-level passthrough. 2 of them were the defect; both are now fixed; there is no third.

site filter run-level passthrough --only verdict
ci.yml:738 job test, slice leg yes -- "--shard=$SLICE" yes same defect, fixed by #16868 — the template
ci.yml:1577 job dogfood yes -- --shard=k/3 yes (this PR) same defect, fixed here
test-nightly-tiers.yml:281 job tiers, slice leg yes -- "--shard=$SLICE" $REPORTER no same defect, outside this card's fence — filed as #17866

Zeros, reported as asked. The other 20 executable turbo run invocations: 14 carry a --filter and no passthrough (the plain turbo run build legs in ci.yml, lint.yml, cut-rc.yml, showcase-smoke.yml); 4 carry neither; and 2 (rerun-safety-nightly.yml:99/:130) carry neither a filter nor a passthrough. Zero sites outside the table above carry both.

One blind spot in the probe, declared. A filter arriving through a shell variable is invisible to a token-level scan: test-nightly-tiers.yml:276 is turbo run test $FILTERS … -- $REPORTER, where $FILTERS is built as --filter=$PKG in the loop immediately above it. The probe scores it filter=0; read by hand it is a fourth instance of the same class, and the only one whose whole-package leg carries a passthrough (ci.yml's corresponding leg carries none). Both nightly sites are in #17866 with their own measurement; they are not fixed here because that workflow is schedule-triggered, outside the required set, so a fix would land unexercised by any PR's CI — and the :276 half needs a derivation the pinned shape does not supply.

⭐ Reverse-read: which existing sentence does this make false?

Sentences made false: zero. The two present-tense claims in range, both in this job:

  • Restore Turbo cache, still on the tree: "the turbo test hash differs per shard (pass-through args are part of the task hash)" — still true, and still the reason its key is shard-scoped: the test task keeps the passthrough. Left alone.
  • Same comment: "turbo's cache is content-addressed per task, so another shard's entries replay the shared build closure even when the test slice differs" — this one ran the other direction. It was false before this diff (the closure was hashed three ways, so another shard's entries could not replay it) and this change makes it true. Left alone, now correct.
  • The test step's "The -- args reach the package's vitest run and are hashed into the turbo task, so each shard caches independently" — not false, but no longer the whole story under --only. Extended in place, not deleted: it now says the passthrough is hashed into that one task and that the closure it used to scatter is built once by the step above.

Sentence kinds, distinguished as asked: nothing carrying a rev or a date was touched (#2018, #4250, #4859, #4928, the #16868 measurement block — all history). The named-direction zeros elsewhere in the file (check:required-contexts pins, the !@objectstack/dogfood exclusion rationale, the "NOT the dogfood job's vitest --shard passthrough, deliberately" note at :295) are unaffected: the passthrough stays, it only stops being hashed into 66 other tasks. No bare present-tense magnitude in the tree became stale — the 66/67 counts this diff introduces are new, and each is written with the tree and the turbo version it was taken against.

Pin tests on the current behaviour: zero found. --only appears in no test or gate assertion about this job; check:stall-guard-budget reads the step structurally and re-derived it green (below), rather than pinning a step list.

Gates

scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 44 commands from the real change set (1 path, vs merge base c88fa2ccd). Ran all 44; reconciled the ran-list back against the deriver.

  • 39 green. The ones that read this diff: check:stall-guard-budget, check:stall-guard-headroom, check:required-contexts, check:agent-test-spelling, check-ci-filter-parity, check:shard-attestation, check-step-collectors, check:workflow-step-name-quoting, check:workflow-status-functions, check:pnpm-filter-targets, check:nul-bytes.
  • check:stall-guard-budget verdict line, which proves the new site was measured rather than skipped:
check-stall-guard-budget: OK (37 workflow file(s), 62 job(s), 692 step(s), 10 guard-wrapped
  step(s); every effective cap clears its job budget by at least one stall window).
  .github/workflows/ci.yml:1538  job `dogfood` step `Build the dogfood package's dependency closure`
      window 10m (explicit) · cap 20m (2x window) · budget 30m (job timeout-minutes) · slack 10m
      (1 of 2 guarded steps in this job; they share one timeout clock)

— the same window/cap/budget shape as test's pair, and 10 guard-wrapped steps where the tree had 9.

  • 4 NOT MEASURED, exit 3 (PREREQUISITE NOT MET), not failures: check:dts-closure, check:dual-build-cjs-loads, check:lean-entry-closure, check:sourcemap-no-sources-content. All four read built dist/ trees and say so in their own refusal text. They are placed by a whole-tree declaration, not by this diff's path, and a workflow-YAML-only diff moves no input of theirs. CI builds and runs them.
  • 1 NOT MEASURED, incomplete: check:pm-dispatch-gates exceeded a 560-second foreground budget inside its own self-test at 1507 passing assertions and zero failures. Left to CI.

Lint scope — a measured narrowing, not a skipped run. Repo-level pnpm lint (eslint . --no-inline-config) is CI's run. The narrowing here is that this diff contains zero files eslint judges, and all three pieces of evidence: (1) eslint's own answer for the one file, from its own config — File ignored because no matching configuration was supplied; (2) --format json over the diff — 1 file, 0 errors, 1 warning, that notice; (3) invariance for untouched files — this repo runs one eslint.config.mjs which never enables type-aware linting for any file (eslint.config.mjs:326-329, measured there with a positive control), so nothing in this diff can move any untouched file's verdict. Taken at 21d717d5, the final commit.

Changeset: skipped, and measured first

skip-changeset label applied. Nothing published moves: of the 70 packages declaring a files[], zero ship any .github/workflows path; positive control — @objectstack/spec's files[] reads back non-empty with its real shipped paths. The one other .github directory in the tree (packages/create-objectstack/src/templates/blank/.github) is a different path and is untouched. This is also the fast-track case: a repo-root CI config.

Clause-②

Clause-②: no, declared line-initial at the top of this body and self-verified against readClause2Line() read fresh from scripts/pm/check-clause2-carriers.mjs:909 — whose CLAUSE2_KEY_LINE now has three capture groups. The substantive answer matches the diff: one CI workflow file, no schema key, no closed-set member, no published export, no registry entry, so no C5 widening tell either.


Generated by Claude Code

…e run, so the shards stop scattering it

The Dogfood Regression Gate carried the same run-level Turbo passthrough
defect fixed earlier in the `test` job: `-- --shard=k/3` folded into the
hash of every task in the run, so the three shards hashed one 66-package
`^build` closure three ways and none could reach the shared,
passthrough-free `build` cache.

Applies that landed shape verbatim rather than a second working spelling:
a dedicated, guard-wrapped, passthrough-free
`turbo run build --filter=@objectstack/dogfood` step ahead of the run, and
`--only` on the sharded `turbo run test` so the passthrough is hashed into
the one task it is for. `name:` and `timeout-minutes: 30` are untouched --
the budget is the instrument that shows the fix working.

Claude-Session: https://claude.ai/code/session_012GKcPZbMoGq7WPzKLfRBTU
Co-authored-by: Claude <noreply@anthropic.com>
@claude claude Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 12, 2026
@claude
claude Bot marked this pull request as ready for review September 12, 2026 15:42
@claude
claude Bot enabled auto-merge September 12, 2026 15:42

Copy link
Copy Markdown
Contributor

PM 复核:收下,已 undraft + 武装。 逐项对着 GitHub 复核,⛔ 不是核对你的报告。

本席自己验过的

读数
--only 落位 ci.yml:1579,turbo run test --filter=@objectstack/dogfood **--only** --log-order=stream -- --shard=…
name: / timeout-minutes 是否被动 diff 里 timeout-minutes 只出现在一条注释里,键本身未动 ✓
载体顺序 PR 正文 Clause-② 命中 2 行 —— ⚠️readClause2Line 只取第一条,所以顺序要验:第 1 行是声明 Clause-②: no,第 116 行是正文对它的描述 ⇒ 读到的是 no
标签 / 状态 skip-changeset 已打、draft、Closes #16886

⭐⭐ 三处比「修好了」更值钱的判断

1. 为什么是自己的一个步骤,而不是在原步骤里再包一次。

a guarded SITE is the triple (file, job, step) — measure-stall-guard-headroom REFUSES to report a verdict when two guarded runs share one.

⇒ 把两个 guarded run 塞进一个步骤,会让那个仪器拒绝给出判定 —— 而一个拒绝判定的仪器,在干净树上和一个通过的仪器长得一样。你为了保住它的可读性多开了一个步骤,⛔ 而不是为了省行数把它弄哑。

2. ⭐ 那句拒绝抬 timeout-minutes 的话是本 PR 最锐的一处:

The step is NOT allowed to buy itself room by raising this job's timeout-minutes: that budget is the instrument that shows this fix working.

⇒ 抬预算会遮掉它正要修的那个效果。⛔ 一个「为了让自己过去而把量表调宽」的改动,是把测量换成了沉默 —— 你把这条写在了行上,下一个想抬它的人会撞到。

3. 那张测量表带着工具版本与探针写进了注释(turbo 2.10.10、--filter--dry=json、67 tasks = 66 build + 1 test),⇒ 它是一次有日期的测量,⛔ 不是一个会腐烂的现在时裸量值。本班 #16200 / #17797 立的就是这条分辨,你在写入侧做对了。

缓存读数 —— 对照会开火,这才让读数成立

  • 修前:plain vs plain 67 identical / 0 changed(⭐ 控制组开火),而 plain vs --shard=1/31/3 vs 2/32/3 vs 3/3 全是 0 identical / 67 changed ⇒ 三个分片把同一个 66 包 build 闭包哈希了三遍,而且没有一个能命中全工作流其它 job 写入的那个 build 缓存(那份缓存的哈希里没有 passthrough)。
  • 修后:66 个闭包 build 与无 passthrough 计划逐个哈希相同(0 differing / 0 missing)⇒ replay 而不是每分片重跑;剩下唯一的每分片哈希差异是 test 任务本身 —— 那正是 passthrough 的目的,⛔ 不该被消掉。

⭐ 而 --only shard=1/3 vs itself1 identical 控制组也开火了。⛔ 没有会开火的控制,一个「相同」说明不了探针在工作。

⭐⭐ 普查:你报的不只是「没有第三例」

ci.yml 干净(两处,现在写法一致),但 test-nightly-tiers.yml 两条腿都带同一缺陷,而 :276 那条全包腿ci.yml 任何 job 都没有过的形状(无条件追加 reporter passthrough)⇒ 它不是「照模板抄」能修的,所以去了 #17866 而不是塞进本 PR。你用四个有界修复条件逐条判的,其中两条不满足(不是被钉住的机械形状;该工作流是 schedule 触发、在必需集合之外 ⇒ 修了也不被任何 PR 的 CI 检验)。⭐ 「修了也验不了」是一个正当的不修理由,你说清楚了。

⭐⭐⭐ 而最该被记下来的是你保留了自己第一次探针的错

grep -E -- '-- ' 把 shell 内建的 set -- 和 run-with-stall-guard 自己的分隔符都算成了 passthrough:5 命中,3 个是假的

⇒ 你没有把那个 5 当成答案,也没有把它悄悄换掉 —— 你换成从 turbo token 起的 tokenizing 探针,并把错的那次留在记录里。⭐ 一个数字对不对,取决于产生它的探针;把废弃的探针连同它的错一起写下来,是让下一个人不重犯的唯一办法。

而你声明了剩下的盲点($FILTERS 经 shell 变量传入,token 级扫描看不见),并指出那正是把 :276 藏起来的东西 —— ⛔ 一个不声明盲点的普查计数,和一个错的计数一样危险。

一处偏离模板,你报成了「观测」而不是掰成一致

#16868 的闭包腿测到 0 extra,你测到 1 extra —— @objectstack/dogfood#build,其命令在 dry 输出里是 <NONEXISTENT>(该包没有 build 脚本),它什么都不执行,只是 66 个 ^build 依赖挂靠的根。⛔ 你没有把读数掰成模板的数字。⭐ 两个都是对的,差别有解释 —— 而一个被掰成一致的读数,会让下一个人以为模板是恒等式

反向读

sentences_made_false: 0,而三条处置各不相同,分得很准:

  • 留着、仍为真:Restore Turbo cache 的「the turbo test hash differs per shard」—— test 任务仍带 passthrough,分片键仍然正当。
  • 反方向:同一条注释里的「another shard's entries replay the shared build closure」在本 diff 之前是假的(闭包被哈希了三遍,什么都 replay 不了),是这次改动让它变真的。⇒ 留着,现在正确。⭐ 这个方向很少有人查:一个改动不只会让句子变假,也会让本来就假的句子变真 —— 而那一类也该被报出来。
  • 就地扩写、⛔ 未删:test 步骤那句在 --only 下不再是全部故事,补成了「passthrough 只哈希进这一个任务,它原先散掉的闭包由上一步建一次」。

带 rev / 日期的历史(#2018 / #4250 / #4859 / #4928 / #16868 的测量块)⛔ 一个未动,具名方向的零断言不受影响 —— 包括 :295 那条「NOT the dogfood job's vitest --shard passthrough, deliberately」。


Generated by Claude Code

@claude
claude Bot added this pull request to the merge queue Sep 12, 2026
Merged via the queue into main with commit 7cab0d8 Sep 12, 2026
39 checks passed
@claude
claude Bot deleted the claude/issue-16886-dogfood-turbo-passthrough branch September 12, 2026 16:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CI: Dogfood Regression Gate has the same run-level Turbo passthrough defect fixed by #16395

2 participants