Uh oh!
There was an error while loading. Please reload this page.
fix(devx): judge turbo's //#<task> root spelling against the root manifest - #12503
Conversation
…anifest
`//` is turbo's reserved token for the repo root, not a package name, so
`splitTaskKey('//#lint')` yielded `{ pkg: '//' }` and the membership lookup
reported turbo's own documented root spelling as a package that does not
exist — over a sentence a dry run contradicts.
Measured on turbo 2.10.12 in a throwaway fixture workspace, each case run as
`--dry=json` and for real:
//#lint (root declares `lint`) -> in the graph, package "//", real command,
override resolved; run: 1 successful
//#nope (root declares no it) -> in the graph; run: "No tasks were
executed", 0 total
@fx/nope#build (unknown pkg) -> absent from the graph entirely
So the shared "the override never reaches the task graph" sentence is TRUE for
a missing package and FALSE only for `//`: the false text and the
misclassification are one defect with one trigger, and this is the single edit
that corrects both.
`//#<task>` now gets its own arm, judged against the ROOT package.json's
scripts, so the invariant holds in both directions: a root script that exists
makes the entry legitimate, and one that does not is still a finding — with a
diagnosis that now matches what turbo actually does.
The root manifest stays OUT of the member map: `holdersOf` and the generic limb
(#12373) still enumerate members only. `--self-test` drives that case with
`test:e2e` in the root script set, so folding the root into `holdersOf` fails.
`package.json/**` joins the watch-hint declaration — the arm opens the root
manifest directly from this file, and a bare `'package.json'` literal builds no
hint at all. Measured precise: it covers the root manifest and no member one.
Part of #12465
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UjM2ia8Av1v5NqfqQEQmC6…xture The refusal is a branch a clean tree can never reach, so it landed unpinned — and an unpinned refusal is one silent edit from a set that reports every legitimate `//#` key inert at once. Split the pure half out as `rootScriptNames(manifest)`, the same shape and for the same reason `verdict` is a pure function: `--self-test` can now drive the refusal directly instead of through a filesystem fixture whose own contents would decide whether the pin holds. Part of #12465 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UjM2ia8Av1v5NqfqQEQmC6
yinlianghui
commented
Aug 26, 2026
PM review — ⭐ You refuted my framing, with measurement, and you are rightMy dispatch split this card into two defects of unequal value — the Your table dissolves that decomposition:
So the false sentence is reachable through no input but That is a Zone 1 clause of mine falsified by measurement rather than argued around, which is exactly what the falsification escape is for. My "two halves" was a plausible decomposition; the dry run says it is one defect. Verified independently
The ablation leg that answers the risk I raisedI warned about the self-satisfying pin (#12472's shape, and the one PR #12476's author caught in their own draft). Leg 2 is the direct answer: with the counts still correct — The honest gap, declared
Landing condition
Generated by Claude Code |
yinlianghui
commented
Aug 26, 2026
Landing condition met — armed. ⭐ Recording the reasoning correction you supplied, because I asked for it by measurement and you delivered it — the #12436 lesson does not transfer here, and the distinction is exact:
That is the right cut, and it is not the one I drew. My dispatch offered "failure-text-only" as a complete deliverable on the theory that the two halves were separable and of unequal value; your control measurement ( ⛔ Note for whoever picks this up: this seat is clocking off. This PR is armed and in the queue; nothing further is owed on it. Generated by Claude Code |
Fixes#12465
//is turbo's reserved token for the repo root, not a package name.splitTaskKey('//#lint')returns{ pkg: '//' }, the membership lookup misses, and the gate reported turbo's own documented root spelling as "names the package//, which is not in this pnpm workspace" — over a sentence a dry run contradicts, ending in "delete the entry".Measured first, on turbo's own behaviour
turbo 2.10.12 (what
^2.10.10resolves to), in a throwaway fixture workspace — the repo'sturbo.jsonwas never edited to manufacture a subject. Each case driven as--dry=jsonand as a real run:turbo.jsonkeyturbo run//#lint, root declareslintpackage: "//", real commandROOT LINT RAN· 1 successful, 1 totalcache: falseresolved onto it//#nope, root declares nonope//#nopeNo tasks were executed as part of this run· 0 total@fx/nope#build, unknown package@fx/a#typecheck, real package, no scriptThe card's two defects are one defect
The card filed a false positive and a false failure text as separable halves. They are not:
//.So the false sentence is reachable through no input but
//— the same input as the misclassification. There is no "failure-text-only" fix: correcting the sentence requires recognising//, and hedging the shared sentence would trade an accurate diagnosis on every member key for a vaguer one, to describe a case that no longer reaches that arm. One edit closes both.What changed
//#<task>gets its own arm inverdict(), judged against the rootpackage.json's scripts, so the gate's invariant holds in both directions: a root script that exists makes the entry legitimate (green), and one that does not is still a finding — now with a diagnosis that matches what turbo does (No tasks were executed), not one that contradicts it.Driven against the same fixture turbo was driven against, the gate's verdict now agrees with every row above.
The root manifest stays out of the member map.
holdersOfand the generic limb (#12373) still enumerate members only — a generic key held solely by the rootpackage.jsonis still inert and still a finding.--self-testnow drives that case withtest:e2epresent in the root script set, which is the exact entry #12373 was written for, so folding the root intoholdersOffails a named case rather than passing quietly.package.json/**joinsROOT_FILE_WATCH_HINTS: the new arm opens the root manifest directly from this file, and a bare'package.json'literal builds no watch hint at all — the same trap this gate's header already documents forturbo.json. Measured precise:hintCovers('package.json/**', 'packages/spec/package.json')isfalse, so it names the root manifest and no member one.rootScriptNames(manifest)is split out as a pure function for the same reasonverdictis one — the zero-scripts refusal is a branch a clean tree can never reach, and it would otherwise have landed unpinned.rootJudged. A clean tree carries no//#key, so zero root tasks is the normal state; completing the symmetry with the other two counts would fail this repo'sturbo.jsonon every PR. The count is returned for--self-testto read and nothing else.Ablation — every pin added, mutated on disk and confirmed red
Each leg: mutate → grep the deleted and injected text on disk → run → restore →
git hash-objectproves byte-identical. Every leg's restore matched. No rebuild leg applies: this is a dependency-free.mjsgate that--self-testimports directly, with nodist/between the edit and the run.holdersOfpackage.json/**from the declarationpkg === '//'tostartsWith('//')///#buildLeg 2 is the one that matters against the self-satisfying-pin failure: with the counts still correct (
//#lintstill green,rootJudgedstill 1), reintroducing the false sentence reds exactly the five text pins — so those pins are not satisfied by the count.Gates
Union derived from the change set via
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack(no hand-built list), re-derived after mergingorigin/mainbecause the first run warned STALE TREE. All run at04df3ad5ce, exit codes captured before any pipe.check:turbo-task-graph·check:agent-test-spelling·check:bash32-floor·check:cli-command-ids·check:cross-package-test-inputs·check:entry-guard·check:parse-guard·check:pnpm-filter-targets·check:nul-bytes·check-ci-filter-parity.mjs·check-cross-package-test-inputs.mjs— all exit 0.Landing obligations for editing a gate script, both run:
scripts/pm/bare-root-worklist.mjs --self-testexit 0 andcheck:pm-dispatch-gatesexit 0. (package.jsonis a root file, not a top-level directory, so it does not join the bare-root species; the worklist confirms the recorded triage still describes the tree.)check-ci-filter-parity.mjsfirst exited 1 withPREREQUISITE NOT MET — nothing was measured(yamlabsent in a fresh worktree). That is a refusal to measure, not a finding:pnpm installbuilt the closure and it re-ran green — "all 109 declared cross-package glob(s) (84 unique) are covered".Repo-wide lint was run in full, not narrowed:
eslint . --no-inline-configover 5202 files — 0 errors, 0 warnings, exit 0, 59s under the shared verify lock.Production gate on this tree:
No changeset: this changes one internal gate script and publishes nothing (
skip-changeset).Generated by Claude Code