Repository navigation
Commit b42e034
Fixes #21193
Clause-②: no. Turbo hash declarations change no contract's acceptance or
rejection behaviour.
This is finding F1 of the stage-1 report on #21186, the precondition for
that card's stage 2. #21186 stays open; its draft PR #21191 (the
`remoteCache` block) is untouched here.
## What changes
`turbo.json` only, `build` task family only. Each build now declares the
root files its build command loads, as task-level `$TURBO_ROOT$/…`
inputs beside `$TURBO_DEFAULT$`:
| task | added inputs | why it reads them |
|:--|:--|:--|
| `build` (generic) | `scripts/tsup-drop-sources-content.mjs`,
`scripts/check-dts-emitted.mjs`, `scripts/invoked-as.mjs` | the root
`tsup.config.ts` (loaded with `--config` by 47 builds) and 19 package
tsup configs import the first, 66 builds in all (it shapes every
`.map`); 67 build commands end with `node …/check-dts-emitted.mjs`,
which imports the third |
| `@objectstack/spec#build` | the generic three, plus
`check-dts-references.mjs`, `ts-parse.mjs`, `check-regen-pending.mjs`,
`regen-artifacts.mjs`, `git-env.mjs`, `import-prerequisite.mjs`,
`cli-build-prerequisite.mjs`, `check-dev-prereqs.mjs`,
`build-input-hash.mjs`, `workspace-enumerator.mjs`,
`js-comment-mask.mjs` | `gen:schema` (`build-schemas.ts`) imports
`check-regen-pending.mjs` to write the `json-schema/` stamp; the build
runs `check-dts-references.mjs` and `check-dev-prereqs.mjs --stamp`; the
rest is their import closure |
| `@objectstack/core#build`, `@objectstack/organizations#build`,
`@objectstack/plugin-auth#build` | the generic three, plus
`check-dev-prereqs.mjs`, `build-input-hash.mjs`,
`workspace-enumerator.mjs`, `js-comment-mask.mjs` | each build ends with
`check-dev-prereqs.mjs --stamp`; `build-input-hash.mjs` computes the
stamp bytes written into `dist/` |
| `create-objectstack#build` | `check-dts-emitted.mjs`,
`invoked-as.mjs`, `sync-scaffold-emission-policy.mjs`,
`sync-template-versions.mjs`, `packages/cli/src/commands/init.ts` | its
first step reads the `SCAFFOLD_*` policy out of `init.ts` and stamps it
into the template that tsup copies to `dist/templates`; it does not run
the root tsup config, so `tsup-drop-sources-content.mjs` is not declared
there |
All paths above are under `scripts/` unless spelled in full. The rule
for "is it an input": a file Node loads while the build command runs.
That is the static import closure of every script the command executes
and every tsup config it loads, measured by walking the relative
imports, not by reading names. Each of these files can change a build's
output bytes (`tsup-drop-sources-content.mjs`, `build-input-hash.mjs`,
`check-regen-pending.mjs`, `sync-scaffold-emission-policy.mjs`,
`init.ts`) or its pass/fail verdict (all of them, at minimum by failing
to load).
Not `globalDependencies`: `globalCacheInputs` is byte-identical before
and after. A root `pkg#task` key replaces the generic definition instead
of merging with it (measured on turbo 2.11.5: an override carrying only
`inputs` resolved to `dependsOn: []` and `outputs: []`), so each
override restates the generic `dependsOn` and `outputs` verbatim.
Measured: across all 320 tasks of `build test test:repo typecheck` (231
with a command), every resolved task definition is identical before and
after except `inputs`.
## Probe (the #21186 stage-1 method, re-run)
Method: append one comment line to the file, run `turbo run build test
test:repo typecheck --dry=json`, compare every task hash with the
unedited plan, restore with `git checkout HEAD -- FILE`, and prove the
restore by blob hash. Before = base `ebdb6f2aca`; after = `120faa2030`.
Turbo 2.11.5. Build Core has 72 build tasks; `+docs` means
`@objectstack/docs#build` moved too. Expected = the direct readers'
build tasks plus every build downstream of them in the task graph.
"Exact" means the moved set equals the expected set task for task, not
only in count.
| file | direct readers | build moved, before | build moved, after |
expected | test / test:repo / typecheck moved, before → after |
|:--|--:|--:|--:|:--|:--|
| `scripts/tsup-drop-sources-content.mjs` | 66 | 0/72 | 72/72 +docs |
exact | 1/1/0 → 70/6/75 |
| `scripts/sync-scaffold-emission-policy.mjs` | 1 | 0/72 | 6/72 | exact
| 2/1/0 → 7/1/7 |
| `packages/cli/src/commands/init.ts` | 2 | 5/72 (no
`create-objectstack#build`) | 6/72 | exact | 8/5/7 → 8/5/7 |
| `scripts/check-dev-prereqs.mjs` | 4 | 0/72 | 71/72 +docs | exact |
1/1/0 → 70/6/75 |
| `scripts/check-regen-pending.mjs` | 1 | 0/72 | 71/72 +docs | exact |
1/1/0 → 70/6/75 |
| `scripts/check-dts-emitted.mjs` | 67 | 0/72 | 72/72 +docs | exact |
1/1/0 → 70/6/75 |
| `scripts/check-dts-references.mjs` | 1 | 0/72 | 71/72 +docs | exact |
1/1/0 → 70/6/75 |
| `scripts/invoked-as.mjs` | 67 | 0/72 | 72/72 +docs | exact | 2/1/0 →
70/6/75 |
| `scripts/build-input-hash.mjs` | 4 | 0/72 | 71/72 +docs | exact |
1/1/0 → 70/6/75 |
| `scripts/workspace-enumerator.mjs` | 4 | 0/72 | 71/72 +docs | exact |
1/1/0 → 70/6/75 |
| `scripts/js-comment-mask.mjs` | 4 | 0/72 | 71/72 +docs | exact |
13/2/0 → 71/6/75 |
| `scripts/regen-artifacts.mjs` | 1 | 0/72 | 71/72 +docs | exact | 1/1/0
→ 70/6/75 |
| `scripts/git-env.mjs` | 1 | 0/72 | 71/72 +docs | exact | 2/1/0 →
70/6/75 |
| `scripts/import-prerequisite.mjs` | 1 | 0/72 | 71/72 +docs | exact |
1/1/0 → 70/6/75 |
| `scripts/cli-build-prerequisite.mjs` | 1 | 0/72 | 71/72 +docs | exact
| 2/1/0 → 70/6/75 |
| `scripts/ts-parse.mjs` | 1 | 0/72 | 71/72 +docs | exact | 1/1/0 →
70/6/75 |
| `scripts/sync-template-versions.mjs` | 1 | 0/72 | 6/72 | exact | 2/1/0
→ 8/1/7 |
| control: root `tsup.config.ts` | global | 72/72 +docs | 72/72 +docs |
unchanged | 73/6/79 → 73/6/79 |
| control: `packages/create-objectstack/tsup.config.ts` | 1 | 6/72 |
6/72 | exact | 7/5/8 → 7/5/8 |
| negative control: `scripts/check-turbo-task-graph.mjs` | 0 | 0/72 |
0/72 | nothing new | 1/1/0 → 1/1/0 |
The 71/72 rows: `@objectstack/sdui-parser#build` is the only build that
is not downstream of `@objectstack/spec#build`, and it does not load
those files.
**End to end, on the real cache.** `turbo run build
--filter=create-objectstack --only`, then `SCAFFOLD_TYPESCRIPT_RANGE` in
`init.ts` edited from `^5.3.0` to `^5.9.0` and run again. Before
(`ebdb6f2aca`): `cache hit, suppressing logs d14ac0ba80366056`, and
`dist/templates/blank/package.json` still says `^5.3.0`. After
(`120faa2030`): `cache miss, executing 78b5115918e44c53`, and the dist
template says `^5.9.0`. Restored, the next run is a hit on the original
hash `b336dcf35aaf1381` with `^5.3.0` again.
## Before/after plan diff (this commit itself)
`turbo run build --dry=json` (same hashes as the four-task run, 80/80
identical) from `ebdb6f2aca` to `120faa2030`:
- Input keys: the 67 generic build tasks with a command gain exactly the
three generic files; spec gains its 14; core, organizations and
plugin-auth gain their 7; create-objectstack gains its 5. Zero input
keys are removed or re-hashed on any task, so `$TURBO_DEFAULT$` keeps
the default file set. Zero test, test:repo or typecheck tasks gain or
lose an input key. `@objectstack/docs#build` inputs are unchanged.
- Hashes: 73/73 build tasks with a command move once (the definition
changed; docs through its `^build` edge). This is the one-time "every
build hash moves once" the stage-1 option A priced.
- Test and typecheck: 70/73 test, 5/6 test:repo and 75/79 typecheck
hashes move once. That is turbo folding each dependency task's hash into
the dependent's hash along `dependsOn: ["^build"]`, not a declaration on
those tasks. The 8 that do not move are exactly the tasks with zero task
dependencies (`spec`, `sdui-parser` and `vitest-filter-preflight` test;
`spec#test:repo`; `refd-timer-testkit`, `sdui-parser`, `spec` and
`vitest-filter-preflight` typecheck).
The same mechanism explains the probe's test column: after this change,
an edit to a spec-closure script moves 70/6/75, against 73/6/79 for the
root `tsup.config.ts` global control. The difference is the
zero-dependency tasks, which a `globalDependencies` entry would also
have moved.
## Gates
Derived with `node scripts/pm/dispatch-gates.mjs --commands` at
`120faa2030`. All 9 ran and exited 0: `check-closing-keyword-parity`
(plus its `--self-test`), `check-comment-mask-corpus`,
`check:driver-memory-census`, `check:gitlink-declared`,
`check:nul-bytes`, `check:refd-timer-probe`, `check:turbo-task-graph`
(43 package-scoped and 9 generic tasks judged),
`check:watch-hint-literal`. `dispatch-gates --ran`: 9 derived, 9 run, 0
NOT-MEASURED, 0 unrun. No guard asserts build inputs. I also ran the
other gates that parse `turbo.json` or read the declared files, all exit
0: `check:cross-package-test-inputs`, `check-ci-filter-parity`
(`--self-test` and the run), `check:examples-live-imports`,
`check-dev-prereqs.mjs --self-test` (it reads `globalDependencies`,
which is unchanged) and `check:scaffold-emission-policy`.
Labelled `skip-changeset`: `turbo.json` is repo-root configuration and
ships in no package.
## Acceptance notes
- **The declared set is wider than the card's table, on the card's own
rule.** Beyond the five named rows, it adds `check-dts-references.mjs`
(the spec build runs it; it was in the stage-1 option A list but not in
the card's table) and the import closure of every script: `invoked-as`,
`build-input-hash`, `workspace-enumerator`, `js-comment-mask`,
`regen-artifacts`, `git-env`, `import-prerequisite`,
`cli-build-prerequisite`, `ts-parse` and `sync-template-versions`.
Before this change each one moved 0/72 builds, the same as the named
rows. `build-input-hash.mjs` decides the stamp bytes written into
`dist/` and `json-schema/`.
- **The generic key over-reaches on five tasks at the declaration level
only.** `@objectstack/cli#build` (tsc) and the four `example-*#build`
tasks (`objectstack build`) load none of the three generic files. Each
is downstream of `@objectstack/spec#build`, which does load them, so
their moved set is the same either way (the probe rows are exact).
Splitting them out would take five more overrides for no hash
difference.
- **`@objectstack/docs#build` is left alone.** Its build runs the spec
`gen:schema` and `gen:docs`, whose out-of-package imports were measured
to be a subset of the spec closure above. It reaches them through its
`^build` edge to `@objectstack/spec#build`, the same edge that already
carries `packages/spec/scripts/**` to it. Every spec-closure row shows
`+docs`.
- **Not declarable as a turbo input:** the spec build's
authorable-surface check anchors on `merge-base(HEAD, origin/main)`,
which is git state, and it is verdict-only (from the stage-1 audit).
- **Nothing keeps these lists honest.** The test side has
`check:cross-package-test-inputs`, but no gate compares a build's
declared inputs with its import closure. A new relative import in any of
these scripts reopens this gap silently. Carrier: none.
- **Scheduling, not hashing, at PR time.** `ci.yml`'s `core` filter
matches none of `scripts/**`, the root `tsup.config.ts` or `turbo.json`
(picomatch 4.0.5 against the checked-in list). So a PR confined to these
files, this one included, skips Build Core and its build-time gates (for
example sourcemap-no-sources-content) until the merge queue, where the
filter defaults to true. This PR leaves `ci.yml` alone by constraint.
The Type Check lane still executes the new build definitions through
`typecheck`'s `^build` edge.
---
_Generated by [Claude
Code](https://claude.ai/code/session_57c17198-e47f-48f8-9242-3fc94c651c85)_
Co-authored-by: Claude <noreply@anthropic.com>
1 parent 5e5ce48 commit b42e034
1 file changed
Lines changed: 82 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
7 | 7 | | |
8 | 8 | | |
9 | 9 | | |
10 | | - | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
11 | 17 | | |
12 | 18 | | |
13 | 19 | | |
| |||
30 | 36 | | |
31 | 37 | | |
32 | 38 | | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
33 | 114 | | |
34 | 115 | | |
35 | 116 | | |
| |||
0 commit comments