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
{{ message }}
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
Repository navigation
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
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.
Claim: PM loop round 6 — packed with #4185 (same file, same mechanism family, third and fourth instances of the #3514 class; full claim + dispatch context here, stub on #4185).
Session: session_017Qqyix2QcnpUC9XeYVDzx3 · Branch: claude/issue-4184-4185-turbo-lint-build-inputs · Worktree: objectui-issue-4184
File surface: turbo.json (lint + build task inputs) + the derivation guard extensions (per-task walkers on the #4176/#4188 helper architecture). Serial constraints: PR #4188 (armed, unmerged) owns turbo.json + the shared helpers — stack on claude/issue-4178-turbo-test-inputs if it has not merged at start; disjoint from #4167/#3398/#4162 in flight.
Container & model: M, mode:subagent, model: opus
Severity framing for the dispatch: #4184 (lint) is LIVE in CI — lint.yml runs turbo run lint with .turbo/cache persisted, so a stale green can ride into merged PRs today (the exact #3514 CI vector); the filing measured @object-ui/core#lint frozen after touching root eslint.config.js, which IS the lint program. #4185 (build) is the highest-stakes of the family: no inputs on the task whose dist/ every other task consumes via dependsOn: ^build; frozen hashes measured against root tsconfig.json/tsconfig.base.json and a vite stub. Derivations differ per task (eslint flat-config import graph for lint; tsconfig chain — reuse #4176's walker — plus build-script file reads for build); the guard must name files, not restate lists, per the family's established bar.
ACCEPT (objectui seat PM, session session_017Qqyix2QcnpUC9XeYVDzx3, review of record) — PR #4200, closing #4184 + #4185 as the dispatched pack.
The family's method held for its third and fourth instances: inputs derived from the tool's OWN resolution semantics, never enumerated from prose. #4184's walker mirrors ESLint flat-config resolution (which is how the four local rule implementations enter the program); #4185's is a union over the five build tools the && chains actually drive, measured per package (tsc 14 / vite 22 / tsup 4 / next 1 / node+pnpm scripts) — and both cards' open design questions were answered by measurement, not judgment. $TURBO_DEFAULT$ preservation is now an assertion, which was #4185's own stated worry about declaring inputs on a task that had none. The shared walks were extracted (config-program.ts, tsc-program.ts) with unchanged sibling test counts (28, 43) as the preservation evidence — the same anti-drift move as #4188, now covering all four guards.
The evidence ladder is complete on both halves, including the money shot each time (FULL TURBO replaying a PREVIOUS run's captured output over a defective tree, exit=0 — lint replaying "26 warnings" over a config that errors on every file; build replaying green over a real TS2688). Eleven reverse cases, of which two deserve naming: the glob-escape probe (a helper in a subdir the shipped glob deliberately cannot match → 45 cases red naming it, proving the guard follows the import chain, not its own glob) and the zero-derivation-package probe (case count 76→77 naming a file nothing in the PR mentions — derivation over enumeration, again).
Beyond the two cards, accepted deliberately: turbo-task-guard-coverage.test.ts partitions turbo.json's tasks on cacheability and requires a derived guard for every cacheable one — the family's completeness statement made mechanical, and it reverse-verifies (un-exempting clean demands its guard). With it, this four-card family is structurally CLOSED, not just currently covered. The dormant postcss instance is correctly recorded as a stated narrowing in the module AND filed as #4198 with options costed, unassigned — the honest shape for a blind spot the derivation deliberately does not model.
No changeset owed (arbitrated by check-changeset-presence: no released-package source changed). CI converged 18/18 completed, zero failures. Flipping ready + auto-merge.
Activity
yinlianghui commentedon Aug 10, 2026
Claim: PM loop round 6 — packed with #4185 (same file, same mechanism family, third and fourth instances of the #3514 class; full claim + dispatch context here, stub on #4185).
Session:
session_017Qqyix2QcnpUC9XeYVDzx3· Branch:claude/issue-4184-4185-turbo-lint-build-inputs· Worktree:objectui-issue-4184File surface:
turbo.json(lint + build task inputs) + the derivation guard extensions (per-task walkers on the #4176/#4188 helper architecture). Serial constraints: PR #4188 (armed, unmerged) owns turbo.json + the shared helpers — stack onclaude/issue-4178-turbo-test-inputsif it has not merged at start; disjoint from #4167/#3398/#4162 in flight.Container & model: M,
mode:subagent,model: opusSeverity framing for the dispatch: #4184 (lint) is LIVE in CI — lint.yml runs
turbo run lintwith.turbo/cachepersisted, so a stale green can ride into merged PRs today (the exact #3514 CI vector); the filing measured@object-ui/core#lintfrozen after touching rooteslint.config.js, which IS the lint program. #4185 (build) is the highest-stakes of the family: no inputs on the task whosedist/every other task consumes viadependsOn: ^build; frozen hashes measured against root tsconfig.json/tsconfig.base.json and a vite stub. Derivations differ per task (eslint flat-config import graph for lint; tsconfig chain — reuse #4176's walker — plus build-script file reads for build); the guard must name files, not restate lists, per the family's established bar.Generated by Claude Code
yinlianghui commentedon Aug 10, 2026
ACCEPT (objectui seat PM, session
session_017Qqyix2QcnpUC9XeYVDzx3, review of record) — PR #4200, closing #4184 + #4185 as the dispatched pack.The family's method held for its third and fourth instances: inputs derived from the tool's OWN resolution semantics, never enumerated from prose. #4184's walker mirrors ESLint flat-config resolution (which is how the four local rule implementations enter the program); #4185's is a union over the five build tools the
&&chains actually drive, measured per package (tsc 14 / vite 22 / tsup 4 / next 1 / node+pnpm scripts) — and both cards' open design questions were answered by measurement, not judgment.$TURBO_DEFAULT$preservation is now an assertion, which was #4185's own stated worry about declaring inputs on a task that had none. The shared walks were extracted (config-program.ts, tsc-program.ts) with unchanged sibling test counts (28, 43) as the preservation evidence — the same anti-drift move as #4188, now covering all four guards.The evidence ladder is complete on both halves, including the money shot each time (FULL TURBO replaying a PREVIOUS run's captured output over a defective tree, exit=0 — lint replaying "26 warnings" over a config that errors on every file; build replaying green over a real TS2688). Eleven reverse cases, of which two deserve naming: the glob-escape probe (a helper in a subdir the shipped glob deliberately cannot match → 45 cases red naming it, proving the guard follows the import chain, not its own glob) and the zero-derivation-package probe (case count 76→77 naming a file nothing in the PR mentions — derivation over enumeration, again).
Beyond the two cards, accepted deliberately:
turbo-task-guard-coverage.test.tspartitions turbo.json's tasks on cacheability and requires a derived guard for every cacheable one — the family's completeness statement made mechanical, and it reverse-verifies (un-exemptingcleandemands its guard). With it, this four-card family is structurally CLOSED, not just currently covered. The dormant postcss instance is correctly recorded as a stated narrowing in the module AND filed as #4198 with options costed, unassigned — the honest shape for a blind spot the derivation deliberately does not model.No changeset owed (arbitrated by check-changeset-presence: no released-package source changed). CI converged 18/18 completed, zero failures. Flipping ready + auto-merge.
Generated by Claude Code
ci(turbo): derive the `lint` and `build` inputs guards from each pack…