You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
turbo lint inputs have the same out-of-package hole as #3514 / #4178 — the root eslint.config.js that IS the lint program is not hashed; measured frozen at 79872f192ee4828c #4184
Found by the task sweep #4178 asked for ("any OTHER turbo task with out-of-package program files — MEASURE"). Filed unassigned; not fixed in PR for #4178, which is scoped to the test task.
The gap
#3514 (PR #4176) established the mechanism and #4178 repeated it on test: turbo hashes a task from its inputs, $TURBO_DEFAULT$ covers only files inside the package directory, and globalDependencies is unset — so any file the task reads from elsewhere in the repo is invisible to the cache key, and turbo replays the previous verdict instead of re-running.
lint is the third instance, and it is the starkest of the three: the task declares no inputs at all, so it runs on turbo's default, and the file that is the lint program — the repo-root flat config eslint.config.js — lives outside every package.
"lint": {
"outputs": [],
"cache": true
}
Every package's lint script is ESLint reading that one root config. Change a rule there and no package's lint hash moves.
Measured
On claude/issue-4178-turbo-test-inputs (which changes only the test task, so this measurement is of main's lint):
$ npx turbo run lint --filter @object-ui/core --dry=json
baseline 79872f192ee4828c
after touching eslint.config.js 79872f192ee4828c <- frozen
Not local-only: .github/workflows/lint.yml runs turbo run lint and persists .turbo/cache through actions/cache (turbo-${{ runner.os }}-${{ github.sha }}, restore key turbo-${{ runner.os }}-), so one poisoned entry rides into later runs — the same travel path #3514 documented.
This is the "looks like enforcement, isn't" class the repo has already paid for repeatedly: adding or tightening an ESLint rule can land, go green on cached verdicts computed before the rule existed, and never actually run against the code it was written for.
Give lint an explicit inputs list including $TURBO_ROOT$/eslint.config.js and whatever that config's own program pulls in — note eslint-rules/ holds this repo's local plugin, so the config's import chain reaches there too, and those rule implementations are as load-bearing as the config.
Derive rather than pin. After turbo test inputs have the same out-of-package hole #3514 closed for type-check — the root vitest.config.mts every package re-exports is not hashed #4178 the shared plumbing already exists at scripts/__tests__/helpers/turbo-inputs.ts (workspace discovery, $TURBO_ROOT$ extraction, glob matching), and scripts/__tests__/helpers/vitest-config-program.ts is a worked example of walking a JS config's import chain plus its designated files. A lint derivation would walk eslint.config.js's relative import chain the same way. Whether the flat config's files / ignores globs need any treatment beyond the import chain is the open design question.
Whether the fix is the inputs list alone or the list plus a derivation guard is a maintainer/PM call, as it was for #3514 and #4178.
Found by the task sweep #4178 asked for ("any OTHER turbo task with out-of-package program files — MEASURE"). Filed unassigned; not fixed in PR for #4178, which is scoped to the
testtask.The gap
#3514 (PR #4176) established the mechanism and #4178 repeated it on
test: turbo hashes a task from itsinputs,$TURBO_DEFAULT$covers only files inside the package directory, andglobalDependenciesis unset — so any file the task reads from elsewhere in the repo is invisible to the cache key, and turbo replays the previous verdict instead of re-running.lintis the third instance, and it is the starkest of the three: the task declares noinputsat all, so it runs on turbo's default, and the file that is the lint program — the repo-root flat configeslint.config.js— lives outside every package.Every package's
lintscript is ESLint reading that one root config. Change a rule there and no package'slinthash moves.Measured
On
claude/issue-4178-turbo-test-inputs(which changes only thetesttask, so this measurement is ofmain'slint):Not local-only:
.github/workflows/lint.ymlrunsturbo run lintand persists.turbo/cachethroughactions/cache(turbo-${{ runner.os }}-${{ github.sha }}, restore keyturbo-${{ runner.os }}-), so one poisoned entry rides into later runs — the same travel path #3514 documented.This is the "looks like enforcement, isn't" class the repo has already paid for repeatedly: adding or tightening an ESLint rule can land, go green on cached verdicts computed before the rule existed, and never actually run against the code it was written for.
Suggested shape
Symmetric with what #4176 and #4178 landed:
lintan explicitinputslist including$TURBO_ROOT$/eslint.config.jsand whatever that config's own program pulls in — noteeslint-rules/holds this repo's local plugin, so the config's import chain reaches there too, and those rule implementations are as load-bearing as the config.testinputs have the same out-of-package hole #3514 closed fortype-check— the rootvitest.config.mtsevery package re-exports is not hashed #4178 the shared plumbing already exists atscripts/__tests__/helpers/turbo-inputs.ts(workspace discovery,$TURBO_ROOT$extraction, glob matching), andscripts/__tests__/helpers/vitest-config-program.tsis a worked example of walking a JS config's import chain plus its designated files. Alintderivation would walkeslint.config.js's relative import chain the same way. Whether the flat config'sfiles/ignoresglobs need any treatment beyond the import chain is the open design question.Whether the fix is the inputs list alone or the list plus a derivation guard is a maintainer/PM call, as it was for #3514 and #4178.
Related
type-check的手维护 inputs 名单已两次因"包程序越出包目录"失真 —— 评估从 tsconfig include 派生或加钉扎测试 #3514 / PR ci(turbo): derive the type-check inputs guard from each package's tsc program (#3514) #4176 —type-check, with the full measurement and the guard architecture.testinputs have the same out-of-package hole #3514 closed fortype-check— the rootvitest.config.mtsevery package re-exports is not hashed #4178 —test, same mechanism; its sweep is what found this.buildtask is filed separately.Generated by Claude Code