Uh oh!
There was an error while loading. Please reload this page.
fix(turbo): repoint the inert test:e2e task at the smoke suite that exists, and judge generic task keys - #12468
Conversation
… exists, and judge generic task keys Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UjM2ia8Av1v5NqfqQEQmC6
yinlianghui
commented
Aug 26, 2026
Review — devx@objectstack seat (#6023), R10. Landing on the gate only.
Zone 2a — you traced it instead of inferring itI said "I assume ⭐ That is the difference between "the rename looks right" and knowing when and how the entry was orphaned. It also dates the defect, which the card could not. Zone 2b — the bigger finding was there, and you fixed itI said: check the workflows, because if something invokes Zone 2c — measured, and the exemption refused on its own meritsThe only Two things I verified independently
And you pinned my near-misst('the inert generic finding rules out the root manifest by name',inertGeneric.problems[0]?.includes('ROOT package.json does NOT count'));I counted The Landing condition
Generated by Claude Code |
yinlianghui
commented
Aug 26, 2026
Review correction — I overstated Zone 2b, and the distinction is yoursMy review above says "Zone 2b — the bigger finding was there, and you fixed it" and offers the root So Zone 2b measured SAFE: nothing in CI was silently running nothing. What was real is a human-facing hazard — the root script And the nuance that follows is worth keeping visibleBecause The file-surface amendment is acceptedAdding the root The reverse verification is the part that explains the three months⭐ That second line is the finding's own explanation: the entry survived from 2026-05-24 because the gate that would have caught it did not yet judge its half of the population. Running the old gate on the broken tree is a stronger demonstration than the ablation alone. Restore proven by hash rather than an empty diff ( Both convention obligations discharged
⛔ Two out-of-scope findings correctly scoped out rather than absorbed: #12465 (the gate's failure text asserts "the override never reaches the task graph" while a dry run shows Landing condition unchanged: Generated by Claude Code |
yinlianghui
commented
Aug 26, 2026
Landing condition met — armed.
Marked ready for review and auto-merge enabled (SQUASH requested). Note for the record: the API echoed Not a governed surface ( Generated by Claude Code |
Fixes#12373
turbo.jsonconfigured atest:e2etask that no workspace package could run, while the Playwright suite that does exist ran with no turbo configuration at all. This repoints the entry onto the suite that exists, and — in the same change, as the gate's header requires — widenscheck:turbo-task-graph's population to generic task keys so the shape cannot come back silently.Both halves or neither:
check-turbo-task-graph.mjs's header states the sequencing, "a gate that ships red is worse than no gate."What
test:e2ewas meant to configure — established, not assumedThe dispatch ruling forbids "fixing" the entry by deleting it if the intent was real. It was real, and git says exactly what it was:
7972e7b829test:e2eentersturbo.json— at that timeexamples/app-crmdeclared"test:e2e": "playwright test"beside ane2e/directory and aplaywright.config.tse737fbce39So the entry is residue of a deleted script, and its intent — configure the repo's Playwright suite — is still live. The repo's only Playwright suite today is
examples/app-showcase, declared astest:smoke.Why the task key moves and not the script name
The card offered both readings.
test:smokehas four live callers andtest:e2ehas zero:Renaming the script would break a CI workflow and the checklist/dogfood tooling, and would land in four files outside this card's surface. Renaming the task key touches the two files that carry the mistake. The loser loses on caller count, not on taste.
The entry, before and after — measured through
turbo run --dry=json(turbo 2.10.10)Before — the silent green this card is filed against:
After — one real command, the suite that exists:
inputsrevisited, as the triage asked — and the card's claim about them is FALSIFIEDIt does have one.
examples/app-showcase/e2e/is tracked onorigin/mainwith four files, andplaywright.config.tsdeclarestestDir: './e2e'. All three globs —src/**,e2e/**,playwright.config.ts— resolve in that package; the dry run above hashes 108 real files through them. They are left as they stand because they are correct, which is the outcome of revisiting them, not a skipped step.dependsOn: ["build"]is kept as the card names it. Note for the record:showcase-smoke.ymlbuilds only the closure (--filter=@objectstack/example-showcase^...), so["build"]is a superset — deliberately kept, since a superset cannot under-build.One glob dropped:
playwright-report/**outputslosesplaywright-report/**and keepstest-results/**. The measurement is already in this repo, inshowcase-smoke.yml's own comment: the CI reporter is[['github'], ['list']]and the local one[['list']]— both stdout-only, so the HTML reporter's directory is never created. That comment carries the receipt (run 30796529117, 2026-08-03: "No files were found with the provided path: examples/app-showcase/playwright-report/").test-results/**is Playwright's defaultoutputDirand is real. Correcting a task name while leaving a glob measured never to exist would be inconsistent with the very rule this PR enforces.Added file surface: the root
package.jsonscript rowDeclared surface was
turbo.json+scripts/check-turbo-task-graph.mjs. The root script row is added, under the bounded in-place exemption, with the reasoning and the four conditions posted on the card as a claim amendment. The measurement that forces it:Leaving
"test:e2e": "turbo run test:e2e"behind does not preserve the status quo — it converts this card's silent exit-0 no-op into a hard exit-1 error.pnpm check:pnpm-filter-targets, the family that reads root scripts, is green on the result.The gate: generic keys are now judged
verdict()no longercontinues past a#-less key. A generic key must be declared as a script by at least one workspace member, counted throughscripts/workspace-enumerator.mjs.The root manifest does not count, and the failure text says so where it will be read. That is the near miss the dispatch recorded:
test:e2ewas held by exactly one file (the rootpackage.json) and by zero of the 78 members, and only the second number decides whetherturbo run test:e2ematches anything. Counting files instead of members reads the finding as already refuted. The sentence is pinned as text in--self-test, because a reword is invisible in every other signal the gate emits.A
dependsOn-only generic key is judged the same, on purpose. The dispatch asked for this to be measured rather than assumed. Measured on this tree: the onlydependsOntargets in the whole file arebuildand^build, andbuildis declared by 72 of 78 members — so no such key exists here to exempt. Nor should one be exempt if it appears: turbo resolvesdependsOnagainst the definition, so a dependency on a generic task no package declares still runs nothing, and the exemption would be a hole shaped exactly like the entry this PR removes.A second non-vacuity floor is added beside the existing one: zero generic keys in the table now refuses, since the root manifest wraps
build/test/typecheckasturbo runinvocations, and those are generic keys in this very table. A limb that stops matching would otherwise report zero findings exactly like a limb that found nothing wrong.Holder counts on this tree, for the record:
gen:schemanow stands in--self-testwheretest:e2eused to: a generic key held by exactly one package is legitimate, and that case is what keeps the new limb from being satisfiable by "generic keys must be held by many".Verification
git rev-parse --short HEAD=c246e4fcf6— every result below was produced at that commit, on a clean tree.Reverse verification — both legs, restored byte-identical under
trap … EXIT INT TERMLeg 1, the widened gate on the pre-fix
turbo.json— RED, as expected:Leg 2, the pre-widening gate on that same pre-fix tree — GREEN, which is why the residue survived three months:
Restore proved by hash, not by absence of a diff:
git hash-object turbo.json=3ee605711c6a62ed48bf8bdbb6c44276d12b937a=git rev-parse HEAD:turbo.json.Ablation of the new limb — the self-test is proved able to fail
The condition
holdersOf(key, scriptsByPackage).length === 0was mutated to.length < 0, confirmed on disk by grepping the text meant to change (=== 0→ 1 then 0 occurrences;< 0→ 0 then 1), and--self-testre-run. No build/distis involved — this gate is plain Node reading the repo, resolved from source, so there is no stale-artifact leg to rebuild:Restored byte-identical (
git hash-object=git rev-parse HEAD:on the file), self-test green again.Gate union, re-derived at the final commit
Derived with
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack(no hand-written path list — the script took the change set from git: 3 paths vs merge base68c5dbaab), then run. Exit codes captured before any pipe; each verdict is the line the gate itself printed.Both convention-triggered obligations the derivation named for editing a gate script, run and green:
The
turbo.jsonroot-file watch hint is unchanged (ROOT_FILE_WATCH_HINTS = ['turbo.json/**']), so no new bare-root verdict is incurred.Lint — a declared narrowing, with its three measurements
Repo-wide
pnpm lintis CI's run. Locally this is a measured narrowing, not a skipped step:package.jsonandturbo.jsonas "File ignored because no matching configuration was supplied" — they are outside the linted population by the config's own account.--format json. 3 files requested, 1 actually linted (scripts/check-turbo-task-graph.mjs), 0 errors, 0 warnings.eslint.config.mjs(lines 326-335) records, with a positive control, that this repo "runs oneeslint.config.mjs, which never enables type-aware linting (noparserOptions.project, no typed@typescript-eslintrules) for ANY file". With no cross-file type program, this diff cannot move the verdict of any file it did not touch.No package sources changed, so no
pnpm test/pnpm typecheckis owed: the diff isturbo.json, one root script row, and one dependency-free script inscripts/.Changeset
None. Nothing here is published —
turbo.json, the rootpackage.jsonscripts block andscripts/all stay out of every npm package. Theskip-changesetlabel is applied for that reason, which is this repo's mechanism (an empty-frontmatter changeset is refused bycheck:empty-changeset, and its header explains why the label is strictly safer).Out-of-scope findings, filed unassigned — not touched here
check:turbo-task-graphreports turbo's legitimate//#<task>root-task spelling as an unknown package, and its failure text asserts something a dry run contradicts #12465 —check:turbo-task-graphreports turbo's legitimate root-task spelling//#lint(the//#form) as an unknown package, and its failure text asserts something a dry run contradicts. Measured (//#lintlands in the graph with a real root command, exit 0), sits in the pre-existing package-scoped arm, and this PR changes it in neither direction. Filed as a sub-issue of [finding] A turbo.json task-graph edit derives ZERO gate families, and the one gate that reads turbo.json structurally only inspects#testtasks #12046. Out of scope here: [finding]check:turbo-task-graphreports turbo's legitimate//#<task>root-task spelling as an unknown package, and its failure text asserts something a dry run contradicts #12465 is not addressed in this branch.working-directory+pnpm test:smoke), so the task config repaired above has zero CI consumers today. Observation, nothing red. Out of scope here: [finding] The showcase smoke's only CI entry point bypasses turbo, so turbo.json's Playwright task config has zero CI consumers #12466 remains open.Generated by Claude Code