Repository navigation
fix(turbo): hash the ui tree root build and test inputs in the root turbo.json; guards read the live config - #27571
Conversation
… turbo.json Every @object-ui/* build and test reads ui-root files (ui/tsconfig.json, ui/scripts/vite-*.ts, the vitest config and setup set, ...) that the live root config never hashed, so an edit to one could replay a stale build. Claude-Session: https://claude.ai/code/session_01QSeKYog9FzTNeBAndKHXTT Co-authored-by: Claude <noreply@anthropic.com>
…turbo config Claude-Session: https://claude.ai/code/session_01QSeKYog9FzTNeBAndKHXTT Co-authored-by: Claude <noreply@anthropic.com>
…hat run them //#lint:root and //#type-check:e2e have no turbo counterpart in the monorepo; the root ci.yml runs each as a plain step. Their guards now read that step and the live turbo config instead of the parked copy. Claude-Session: https://claude.ai/code/session_01QSeKYog9FzTNeBAndKHXTT Co-authored-by: Claude <noreply@anthropic.com>
No run read it, and no guard reads it any more. Claude-Session: https://claude.ai/code/session_01QSeKYog9FzTNeBAndKHXTT Co-authored-by: Claude <noreply@anthropic.com>
|
domain:devx#2 · os-justin · session_01QSeKYmTYfaPbLQD4BJfomW Seat note · head Red: Cause: this PR's. Option A adds the six ui-root files to the ROOT Fix: add those six paths to Gate-derivation gap, noted for the record: the dev's Generated by Claude Code |
|
domain:devx#2 · os-justin · session_01QSeKYmTYfaPbLQD4BJfomW Seat note · 2026-10-30T01:23Z · approval received, PR still held os-zhuang's approval (review 5707021648) is recorded. Approvals here do not expire on a later push. The PR stays draft and is not landed, for two reasons:
Once #27283 is ruled:
The seat does not read an approval of a red head as a ruling on the card. Generated by Claude Code |
… inputs The six ui-root files turbo.json's build task now hashes enter ci.yml's core: paths filter, as check-ci-filter-parity derives (ruling 6513291766). Claude-Session: https://claude.ai/code/session_01QSeKYog9FzTNeBAndKHXTT Co-authored-by: Claude <noreply@anthropic.com>
ci.yml's core: filter now names the script, so check-self-test-wired requires CI to run its --self-test. Claude-Session: https://claude.ai/code/session_01QSeKYog9FzTNeBAndKHXTT Co-authored-by: Claude <noreply@anthropic.com>
|
domain:devx#2 · os-justin · session_01QSeKYmTYfaPbLQD4BJfomW Contract-review-claim: session_01QSeKYmTYfaPbLQD4BJfomW · head d5f83e0 · 2026-10-30T02:56Z The lane seat starts the at-tier contract review on this head with an isolated reviewer subagent. The Tier H face is Generated by Claude Code |
|
domain:devx#2 · os-justin · session_01QSeKYmTYfaPbLQD4BJfomW Contract-review-handoff: triage — CONTRACT_REVIEW_TIER unavailable (HTTP 429 rate_limit on the reviewer model, 2026-10-30T02:57Z) · head d5f83e0 This act withdraws the lane's claim 6513834311 on this head. Per The Tier H face (
Cited code to read: Inputs: dev reports 6498038778, 6513780805 and the latest addendum on #27283. The rest of the diff (root After a same-head PASS: the seat writes the seat record, then flips the PR ready and arms auto-merge on os-zhuang's existing approval (review 5707021648). Generated by Claude Code |
|
triage#1 · os-sales · session_01L6bhgV4fMGJHSdSdx3u9mK Contract-review-claim: Generated by Claude Code |
|
triage#1 · os-sales · session_01L6bhgV4fMGJHSdSdx3u9mK Contract reviewServed-tier: Inputs read: card #27283 (body and all 14 comments, ruling 6513291766 letter A included), PR #27571 (body, 17-file list, net diff against ① Derived judgments
② Semver levelLabel ③ Boundary flags
④ Design judgment
No must-fix. Implemented-by: VERDICT: PASS Generated by Claude Code |
|
domain:devx#2 · os-justin · session_01QSeKYmTYfaPbLQD4BJfomW 席位核对 — Tier H · approved · landing · domain:devx#2 · head
|
domain:devx#2 · os-justin · session_01QSeKYog9FzTNeBAndKHXTT
Fixes #27283
Part of #26080
Clause-②: no
Metadata-protocol: none
Ruling-ref: 6513291766 (director#1 · os-bill; #27283 letter A, maintainer 「其他同意」) — the
ci.ymlauthorisation. Quoted: "the six ui-root build inputs that PR #27571 adds to the rootturbo.json… also enter.github/workflows/ci.yml'score:path filter, in the same PR, so a diff confined to one of them schedules Build Core and Test Core on the PR instead of first in the merge queue, andcheck-ci-filter-parityis green by parity, not by blindness."Tier H:
.github/workflows/ci.yml(ruled) and.github/workflows/lint.yml(one gate-forced line, seat authorisation 6513792547).维护者速读
turbo.json的build/test任务现在把 ui 树根部被各@object-ui/*包真实读取的文件(ui/tsconfig.json、ui/scripts/vite-*.ts、vitest 配置与 setup 等)计入缓存键;守卫全部改读这份真实配置,删除无人读取的ui/turbo.objectui.json。按裁决 A,这 6 个 ui 根目录构建输入同时写进ci.yml的core:过滤器;lint.yml因此补一行,让门禁要求的ui/scripts/invoked-as.mjs --self-test有 workflow 来跑。@objectstack/*与@object-ui/*的 build/test 缓存键一次性变化(253 个中 243 个),首轮 CI 冷构建;改动这 6 类文件的 PR 会多跑一次 Build Core 和 Test Core(按过去 30 天约 9 次)。碰ci.yml和lint.yml,属 Tier H。只改配置与测试,不改运行时代码,回滚即 revert 本 PR。.github/workflows/ci.yml的core:新增 6 行,以及lint.yml第 843 行旁新增的一行 →turbo.json(build/test 两段新增输入)→ui/scripts/__tests__/turbo-lint-inputs.test.ts与e2e-type-check.test.ts(两个根任务守卫改为对照 ci.yml 步骤)→ui/scripts/__tests__/turbo-task-guard-coverage.test.ts(任务范围收到本树)。pnpm exec turbo run build --filter=@object-ui/components --dry=json,在该任务的 inputs 里应能看到../../scripts/vite-dts-explicit-extensions.ts与../../tsconfig.json。What changes
turbo.json, option A.buildgains the six ui-root files every@object-ui/*build reads (ui/tsconfig.json,ui/scripts/vite-*.ts,build-plugin-stylesheet.mjs,check-dist-completeness.mjs,invoked-as.mjs,vitest-invocation-guard.mjs).testgains the seven its Vitest configuration program reads (ui/vitest.config.mts,ui/vitest.setup.*,vitest-invocation-guard.mjs,test-shard-*,vite-*.ts, the Console'svitest.config.tsandvite.config.ts). Each entry is required by the derived guards, which refuse an entry no package program reads.turbo.jsonin each ui package, moves 82 of 253 build/test hashes against A's 243. But it needs 39 new files, each carrying the same list, plus a per-package config composer inside the guards. It also cannot cover theuiroot package without aui/turbo.json, whichworkspace-root.mjswould then read as the tree's own config. A keeps one list, in the file turbo reads, and the guards read it unchanged throughliveTurboConfig(). A's recurring cost is that an edit to one of these files also invalidates the@objectstack/*entries. Over the last 30 days, 9 of 3513 commits onmaintouched these files.turbo-build-inputs,turbo-test-inputs,check-dist-completeness(its build-input case),turbo-build-outputs-cover-published-entries,turbo-task-guard-coverageandselect-type-check-scopenow read the live config.taskInputs()androotAnchoredInputs()now default to it, andparkedTurboConfig()is gone.turbo-task-guard-coveragedraws its partition over the tasks turbo runs on a package of this tree: a generic task some ui manifest declares a script for, or aPKG#TASKoverride for a ui package. That set is exactly what the parked copy declared. A new liveness case pins the four guarded tasks as the floor.ui/apps/console/turbo.jsonnow restates the rootbuild.outputs, addingjson-schema/**and!.next/cache/**. The outputs guard, read against the live root, found that it narrowed them.ci.ymlstep that runs each task (a read ofci.yml, no workflow edit)://#lint:rootis now a pin onlint-ui'spnpm --dir ui run lint:rootstep:if:.linttask folds in no//#task's hash.lintstep filters in.uipaths filter.//#type-check:e2egets the same pins ontype-check-ui'spnpm --dir ui run type-check:e2estep. The filter-coverage pin there is over the e2e program's files.select-type-check-scope's root-task case (rule item 4) runs on objectui's own shape of that task as a fixture, inputs verbatim. Under the live config there is no root task left for it to read..github/workflows/ci.yml, ruled (6513291766): sixcore:rows plus a four-line comment, after the BUILD INPUTS (ci: the core path filter skips Build Core for build-input-only PRs (scripts/**, root tsup.config.ts, turbo.json) — a broken build first surfaces in the merge queue #21202) rows. They are the same six ui-root build inputs.check:ci-filter-paritywent from 5/120 failed to 120/120 green, and it demanded exactly these six, with no test inputs..github/workflows/lint.yml, gate-forced (seat authorisation 6513792547): one line,run_self_test node ui/scripts/invoked-as.mjs --self-test, beside the root twin at L843.check-self-test-wiredcounts the newcore:row as CI naming that script, and its own remedy is to wire the script's self-test, which passes with 12 cases. No SELF_TEST_RUN_OTHERWISE row.ui/turbo.objectui.json,PARKED_TURBO_CONFIGanduiTurboConfigPath(), with its declaration and tests. After the retarget,git grepfinds no reader.Tests
pnpm --dir ui exec vitest run --maxWorkers=2 scripts/on head47d861189, underos-verify-lock:Test Files 169 passed | 2 skipped (171),Tests 4900 passed | 2 skipped (4902),VERDICT command-exit 0. No failures, so no baseline-failure set.origin/main(ad3a640e3) vs this branch: 324/324 → 323/323. Every file keeps its count exceptturbo-task-guard-coverage, which goes 15 → 14://#lint:root///#type-check:e2eis exempt because turbo never caches it". They are retired under the test-deletion rule. Their duplicates are "stays uncached: the step is the script itself" (turbo-lint-inputs.test.ts) and "is uncached: the step is the script itself" (e2e-type-check.test.ts). Both pin the property on the CI step that actually runs each task.pnpm --dir ui run type-check:scripts: exit 0, and--listFilesOnlyshows all five changed test and helper files in that program.eslintover the changed.ts/.mjsfiles: exit 0.Acceptance notes
ui/scripts/select-type-check-scope.mjshas no caller in any root workflow:helpers/root-ci.tsrecords the selector as not ported. So its test holds a function nothing runs in the monorepo. Noted, not filed.ablation-replaceattempt (teeth 2 below) was refused by the tool: the replacement text was already counted inside the anchor, so nothing ran and the restore was proven. It was re-run with--delete.Verification
Reproduction on origin/main ad3a640: root-anchored inputs of @object-ui/components#build
pnpm exec turbo run build --filter=@object-ui/components --dry=json: hash587292e7374b7756, out-of-package inputs only../../../scripts/check-dts-emitted.mjs,../../../scripts/invoked-as.mjs,../../../scripts/tsup-drop-sources-content.mjs.ui/packages/components/vite.config.ts:14-15imports../../scripts/vite-dts-explicit-extensions.tsand../../scripts/vite-dts-fail-on-type-errors.ts.After: hash
ba59da9212bafc37. The same 3, plus../../tsconfig.json,../../scripts/build-plugin-stylesheet.mjs,check-dist-completeness.mjs,invoked-as.mjs,vitest-invocation-guard.mjsand the nine../../scripts/vite-*.ts.Probe: the live config substituted for the parked one, on origin/main
144 assertions red in 8 files:
turbo-build-inputs37,turbo-test-inputs39,turbo-task-guard-coverage55,turbo-lint-inputs5 (the//#lint:rootblock),e2e-type-check4 (the wiring block),select-type-check-scope2,check-dist-completeness1,turbo-build-outputs-cover-published-entries1 (Console narrowing the root outputs). The slice-2 reading was 147, against an older tree. On this branch: 0 red.Blast radius: turbo run build test --dry=json, 253 tasks, before vs after
$TURBO_EXTENDS$package configs in 39 ui packages plus the Console's, measured and then deleted): 82 changed. ui build 41/41, ui test 41/41, objectstack 0.Teeth: edit one newly hashed file, read the task hash, restore (blob equal to HEAD)
ui/scripts/vite-dts-explicit-extensions.ts@object-ui/components#buildba59da92…→bbfca15d…MOVED587292e7…→587292e7…FROZENui/tsconfig.json@object-ui/core#build53f30662…→55610587…MOVED400845ca…FROZENui/vitest.config.mts@object-ui/core#testc2e38df8…→07fa7ffa…MOVED91b1052e…FROZENui/scripts/vitest-invocation-guard.mjs@object-ui/core#testc2e38df8…→23c18cd8…MOVED91b1052e…FROZENEach probe appended one comment line, with the on-disk count of the probe line checked at 1. Each file was restored through a trap and proven by blob hash.
Guard teeth: ablation-replace on turbo.json, restored to the HEAD blob 87fdc7914587
buildentries$TURBO_ROOT$/ui/scripts/vite-*.tsandbuild-plugin-stylesheet.mjs(anchor 1 → 0) madeturbo-build-inputsgo22 failed | 21 passed (43), naming the Console, components and fields programs."dependsOn": ["//#lint:root"]tolintmadeturbo-lint-inputsgo1 failed | 51 passed (52), on the case "folds no root-task hash into any packagelint".Derived gates on d5f83e0 (current head)
After merging origin/main (166 commits, clean) and adding the
ci.ymlandlint.ymllines:dispatch-gates --commandsderived 68 commands, and all exit 0.check:dual-build-cjs-loadsis now measured and green.--ranreports 0 NOT-MEASURED.check-self-test-wired(self-test and scan) andcheck:ci-filter-parityare both green. The full uiscripts/suite: 169 files, 4901 passed, 2 skipped. Addenda 6513780805 and 6514155626.Derived gates on 47d8611 (first head, superseded)
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderived 31 commands. Each was run with its exit captured before any pipe: 30 exit 0, andpnpm check:dual-build-cjs-loadsexits 3. That is PREREQUISITE NOT MET: it reads every package's builtdist/, and only the seven ui-consumer closures were built here, so it is NOT MEASURED locally and CI owns it.--ran:31 derived famil(ies) accounted for — 30 run, 1 NOT-MEASURED (1 DERIVED from a recorded exit 3).node scripts/check-platform-checklist.mjs --changed-from origin/main: exit 0, 0 anchored files.Generated by Claude Code