fix(gate): check:node-esm-load grades only artifacts this tree produced - #7861
Merged
Conversation
The load leg printed "the load leg refuses to grade artifacts it did not produce" over a `pnpm build` that could not keep the promise. Measured on turbo 2.10.9, which announces it every run as `using shared worktree cache`: the cache is keyed by input hash and stored in the MAIN checkout's `.turbo/cache`, so a build in one worktree is routinely satisfied by an entry a sibling worktree wrote, and turbo restores that worktree's artifacts and its log here. Reproduced at 52cac38 in a second worktree of the same commit: 43 of 43 build tasks replayed in 272ms, every task log naming the producing worktree, and the gate then went GREEN over artifacts none of which this tree had produced. Adds a provenance leg between the build and the grading. It reads the marker turbo already writes and caches beside each task -- pnpm's `> name@version script <absolute package dir>` banner, restored verbatim by a replay, so it keeps naming the tree that produced the artifacts -- and refuses to grade any package whose artifacts came from somewhere else, naming the path they came from. It fails closed: `own` is the only verdict it grades on, so a missing or unparseable marker is a refusal, never a pass. Detection rather than isolation, on measurement: a cold `turbo run build --filter=!@object-ui/site` took 3m46.7s over 43 tasks and the same run against a warm shared cache took 213ms, about 1065x. `--force-build` buys the isolated form (TURBO_FORCE) for callers who want it. Both entry points stay honest: `--specifiers-only` now says the load leg grades only artifacts this tree produced and that it was not run. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KDq78vMMSzCGWGmhUYBabh
…sweep Measured: ablating TURBO_TASK_LOG to a different filename left all 52 tests in this file green. Every fixture WRITES the log at TURBO_TASK_LOG, so they follow the constant wherever it goes and vouch for nothing -- the anti-vacuous leg was itself vacuous in that one direction. Four ablations now, all red: accept-everything, fail-open on a missing log, a moved marker path, and a loosened banner regex. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KDq78vMMSzCGWGmhUYBabh
os-sam
marked this pull request as ready for review
September 6, 2026 00:32
This was referenced Sep 6, 2026
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 #7276
End state taken: the banner becomes TRUE
Ruling 1 offered two acceptable end states. This PR takes the first one — the load leg really does refuse to grade artifacts it did not produce — and the sentence is rewritten only so it states the mechanism instead of an unbacked promise. Nothing is caveated away.
What was actually happening (re-derived, not transcribed)
turbo 2.10.9 does not merely store its cache outside the worktree. It announces on every run:
Measured: a build in
/home/user/objectui-issue-7276wrote/home/user/objectui/.turbo/cache/c19befca204c7918.tar.zst— the MAIN checkout's cache dir, not the worktree's. So all worktrees of one clone share one cache by design, and the card's premise holds in a stronger form than it was filed with.Reproduced on
origin/main52cac38, two worktrees of the same commit:turbo run build --filter=!@object-ui/site.turbo/turbo-build.log/home/user/objectui-issue-7276/...pnpm check:node-esm-loadLoad leg: 34 of 39 ... imported and evaluatedThat last row is the silent direction ruling 3 named, demonstrated: a green verdict over artifacts of which this tree produced exactly zero, under a banner promising the opposite. (Worktree B needed one nudge to reach it —
apps/console/plugin.jshad to be present, because it is missing from a cache replay for an unrelated reason now filed as #7855.)The fix
A provenance leg between the build and the grading, reading a marker turbo already writes and already caches: each task's log, whose first line is pnpm's
> name@version script ABSOLUTE-PACKAGE-DIRbanner — the directory as it was when the script actually ran. A replay restores it verbatim, so it keeps naming the producing tree while the artifacts themselves are byte-identical either way.It fails closed.
ownis the only verdict the load leg grades on;foreign,no-build-logandunreadable-build-logare all refusals that name themselves and carry their own remedy. A provenance check that answered "sure" when it could not read its marker would be this card's subject matter one level up.Same tree as the table above, patched:
Exit 1, every foreign path named. On an honestly built tree it is
Provenance leg: 37 of 37 gradable entries were built by this tree.and the load-leg verdict is unchanged from before this PR (34 of 39, exit 0).Why detect rather than isolate — the cost reading (ruling 2)
turbo run build --filter=!@object-ui/site, 43 tasks--force-build(TURBO_FORCE), whole gate end to endMaking "I produced these" true by construction means paying the cold build on every run in any container where a sibling keeps the cache warm — which is every container this gate is run on. Both readings are shared-box wall clock under the container's verify lock, so treat the ratio as the durable number.
So: the refusal (which is what the banner actually promises) is always on and costs 37 small file reads. The isolated build is opt-in as
--force-build, and the failure message points at it.Both entry points (ruling 5)
check:esm-specifiers(--specifiers-only) reads sources and never touches an artifact, so it has no provenance question of its own — but it must not be read as having answered the load leg's. Its closing line now says what was not run:The file's header docblock — read by both doors — gains leg 3, and its
Exit:contract gains the new failure.The anti-vacuous leg (ruling 4), including one it failed
Four ablations, each mutated on disk with the hit count checked before and after, run, then restored to the HEAD blob hash:
code: PROVENANCE_OK)TURBO_TASK_LOGto another filenameThe fourth is the finding. Every fixture WRITES its log at
TURBO_TASK_LOG, so they follow the constant wherever it goes and vouch for nothing — a sweep self-consistent with the thing it checks. The second commit pins the literal; re-ablated, it is red. The real-world signal for that failure is elsewhere and loud by construction: if turbo moves its task log, every package refuses at once asno-build-log, and the remedy text names the two identifiers to update.The repository sweep is built from real manifests — every published package's real name, version and build script — and asserts both directions on each: the banner pnpm writes for it is recognised, and its own build-script line is not. A floor of
MIN_PACKAGESkeeps the sweep from becoming a loop over nothing.Scope
scripts/check-node-esm-load.mjs,scripts/__tests__/check-node-esm-load.test.ts.turbo.jsonwas NOT touched — the fix did not require it, so the shared-file allowance went unused.node scripts/check-changeset-presence.mjsreads2 file(s) changed, 0 of them published source of a package the release covers ... no changeset is owed.Theskip-changesetlabel is confirmed dead here (Askip-changesetlabel exists on this repo, but no workflow reads it — the repo's own test records that neither the workflow nor the label was ever real #4912) and is not applied.@object-ui/consolepublishesplugin.jsfrom the package root, which turbo's buildoutputsdo not cover — a cache hit restores an incomplete package #7855 —@object-ui/consolepublishesplugin.jsfrom the package root, which turbo'sbuildoutputsdo not cover, so a cache hit restores an incomplete package. That is the mechanism behind this card's loud symptom, and it outlives this fix.ownfor everything and costs nothing. It is a local-verification hazard, and that is what makes it easy to leave unfiled.Gates run locally (on
b7a63bb)pnpm check:node-esm-loadProvenance leg: 37 of 37,Load leg: 34 of 39pnpm check:esm-specifierspnpm test scripts/__tests__(the CI job's command)pnpm type-check:scriptspnpm lint:root(whole scan, not narrowed)node scripts/check-control-bytes.mjsnode scripts/check-changeset-presence.mjsnode scripts/check-governed-queue-guard.mjs --testGenerated by Claude Code