ci(turbo): derive the lint and build inputs guards from each package's real program (#4184, #4185) - #4200
Merged
yinlianghui merged 2 commits intoAug 10, 2026
Conversation
…s real program (#4184, #4185) `lint` and `build` both declared no `inputs` at all, so both ran on turbo's default — which covers only files inside the package directory. The repo-root `eslint.config.js` that IS the lint program, and the root tsconfigs plus `scripts/vite-*.ts` that the build programs read, were unhashed: turbo replayed the previous verdict instead of re-running. Adds the third and fourth walkers on the #4176 / #4188 architecture — an ESLint flat-config program and a build program (a union over tsc / vite / tsup / next / node / pnpm segments) — plus the family's completeness gate, which asserts every CACHEABLE turbo task has a guard so a fifth instance cannot arrive quietly. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Qqyix2QcnpUC9XeYVDzx3
…on (#4185) The repo-root postcss.config.mjs is outside every build program today only because two facts coincide, not by construction. Written down where the derivation's other narrowings live, so the next reader does not have to re-measure it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Qqyix2QcnpUC9XeYVDzx3
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
This was referenced Aug 10, 2026
yinlianghui
marked this pull request as ready for review
August 10, 2026 20:39
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 #4184
Fixes #4185
PR #4188 (the #4178 sibling) merged as
2b9428338before this started, so this branches offmainrather than stacking onclaude/issue-4178-turbo-test-inputs.Premise check first
Both cards hold, and both headline numbers reproduce exactly on
mainat2b9428338.One number in #4185 needs a correction, and it is a correction to the value, not to the claim. The card measured
@object-ui/console#buildate6c5943a3e062002; here the baseline isa17b12650e52ce28. The card measured onclaude/issue-4178-turbo-test-inputs, andapps/consolehas changed onmainsince that branch point (#4180 and #4186, 11 files), so$TURBO_DEFAULT$legitimately moved the baseline. The freeze — the thing the card actually asserts — reproduces exactly:Two things the cards left as open questions, both answered by measuring rather than by guessing — and both are why this ships derivations rather than the suggested lists.
#4184's open question was
files/ignores. They are not program files. A flat config'sfiles/ignoresare globs that SELECT what gets linted; the selected files are ESLint's subject, not its program, and they live inside the package where$TURBO_DEFAULT$already hashes them. The root config'sfiles: ['packages/types/src/objectql.ts']block looks like a counter-example and is not one: when@object-ui/typesis linted that path is inside the package, and for every other package the selector matches nothing. Treating a selector as a program file would make every package's lint program claim to read every other package's source.#4185's open question was which build scripts drive tsc vs Vite. Neither, exclusively — the answer is five tools, which is why the build derivation is a union and not one walk:
What ships
1.
scripts/__tests__/helpers/eslint-config-program.ts— thelintderivation. Each package's ESLint flat-config program: the config ESLint resolves for itslintscript, plus that config's transitive relative imports. That second step is load-bearing rather than decorative —eslint.config.jsimports./eslint-rules/index.js, which imports the four local rule implementations. A rule body decides what theobject-ui/*ratchets actually reject, and none of the five files was hashed. Config resolution mirrorsfindUp(FLAT_CONFIG_FILENAMES)fromConfigLoader.locateConfigFileToUsein eslint 10.8.0.2.
scripts/__tests__/helpers/build-program.ts— thebuildderivation, classifying each&&segment by the tool it runs, per the table above. A segment running a tool it cannot classify throws rather than being skipped.3.
scripts/__tests__/helpers/config-program.ts— the walk itself (relative imports, key-directed designation, resolve-or-throw), lifted out of #4188's Vitest derivation now that three guards need it. Three hand-written copies of one walk is three chances for one to drift toward resolving FEWER files, and a derivation that quietly resolves fewer files is a guard that quietly stops requiring inputs. #4188's test count is unchanged at 28, which is the evidence the extraction was behaviour-preserving.4.
scripts/__tests__/helpers/tsc-program.ts— #4176's tsconfig walker, lifted out for the same reason:builddrives tsc too. #4176's test count is unchanged at 43.5.
scripts/__tests__/turbo-lint-inputs.test.ts/turbo-build-inputs.test.ts— the policy over each result, mirroring the family's assertion set, plus one new assertion each that both cards specifically asked for: the explicit list must still contain$TURBO_DEFAULT$. #4185 raised exactly this worry ("an explicit list must not accidentally narrow what is already covered"); this makes it mechanical instead of a review note, for the two tasks that had no list at all until now.6.
scripts/__tests__/turbo-task-guard-coverage.test.ts— the family's completeness statement, made mechanical. See the sweep section.7.
turbo.json— the five entries the guards' first run named:Measured after the fix
Every file behind those entries now moves the hash independently.
@object-ui/core#lint, baseline59e4d3f652579c58:eslint.config.jstob215fd79877449fa,eslint-rules/index.jsto84f338fc4abaa1ad,no-synthetic-event-trigger.jsto7d6d40d5c8b7e709,no-try-catch-around-hook.jsto0ce406c9bb711b43,no-inline-spec-config.jstoca6e6ea3b8079a3c,no-dynamic-import-in-test-hook.jsto8f28175307530e1f.@object-ui/core#build, baselined3ed3d15ec3cfaa8:tsconfig.jsonto338f9a9ba5c56531,tsconfig.base.jsonto3d658ca0e83287d5.@object-ui/console#build, baseline3c0abe51fa3ccecd:vite-crypto-stub.tsto5b639849fe16fdd8,vite-maplibre-worker.tsto82b7d6ffaeedcf24.End to end, both halves
lint — subject
@object-ui/permissions, defect a newly ADDED rule in the root flat configThis is the exact failure #4184 describes: a rule lands, and cached verdicts computed before it existed report green.
Not merely a stale exit code: turbo replayed the previous run's captured output,
0 errorsand all, over a config that now errors on every file.build — subject
@object-ui/types, defect a real TS2688 in the roottsconfig.jsonIn both halves the verdict depended on cache state rather than on the code, and after the fix it depends on the code.
Reverse verification
Each new entry removed in turn,
$TURBO_DEFAULT$removed, a phantom entry added, and — the two that matter most — a new root file introduced into a chain, one per half.The two that separate a derivation from a restatement:
[10] proves the guard follows the import chain rather than trusting its own glob:
eslint-rules/*.jsdoes not match a path one directory deeper, so a restatement-style check would have stayed green. [11] is #4188's [8] in the build half — the case count goes 76 to 77 becauseit.eachenumerates only packages with a non-empty set, so a package that reached nowhere before now has a case at all, and it names a file no entry, comment or fixture in this PR mentions.The completeness gate reverse-verifies too: removing
"cache": falsefrom thecleantask turns it red with "turbo.json caches thecleantask ... There is no scripts/tests/turbo-clean-inputs.test.ts deriving whatcleanreads."Sweep: every remaining turbo task
Both cards' sweeps were done by hand, by a reader who thought to ask. Three of the four instances of this class were found that way. That is a discovery process with a hole in it, so the answer is now mechanical —
scripts/__tests__/turbo-task-guard-coverage.test.tspartitionsturbo.json's tasks on the one property that matters (turbo caches by default, so the exemption must be spelled out):buildturbo-build-inputs.test.ts(this PR, #4185)testturbo-test-inputs.test.ts(#4178)lintturbo-lint-inputs.test.ts(this PR, #4184)type-checkturbo-type-check-inputs.test.ts(#3514)test:watchcache: false, persistentcleancache: falsedevcache: false, persistentAll four cacheable tasks now have derived coverage, and a fifth cacheable task cannot arrive without one: it goes red here naming itself. If a task ever loses its
cache: false, the same assertion starts requiring a guard for it, which is the intended coupling.Out of scope, filed
#4198 (
finding, unqueued) — the repo-rootpostcss.config.mjsis a dormant fifth instance. Vite hands CSS topostcss-load-config, which searches upward, so avite buildpackage that processes CSS and has no postcss config of its own would read it. Measured: no package does both today, because every CSS-processing vite-build package carries its own postcss config, and the one package without one (packages/fields) has a CSS file that never enters the Vite graph — nothing imports it,scripts/build-css.mjscompiles it separately with an explicit plugin list. That is a coincidence of two facts rather than a structural guarantee, and this PR's derivation does not model postcss discovery, so the guard would not catch it. Recorded inbuild-program.ts's narrowings as a known blind spot rather than a safe one, and filed for triage rather than fixed inside this card's scope.Gates
Changeset: none owed —
check-changeset-presence.mjsarbitrates: "No source of a released package changed in this range, so no changeset is owed." Noskip-changesetlabel per #3724.Generated by Claude Code