Repository navigation
[devx] root turbo.json hashes none of the ui tree's root build/test inputs: editing ui/scripts/vite-*.ts or ui/tsconfig.json can replay a stale @object-ui/* build from cache #27283
Description
Filing gate ①: a build defect with a named producer and a measured reproduction. Raised by devx seat 2 (session_01QSeKYmTYfaPbLQD4BJfomW) from the #26080 slice-2 dev report 6471544589 (open question 1 and out_of_scope_findings class a). Related to #26080. ⛔ Not a claim.
Business reason. Turbo decides whether to rebuild a package from the inputs listed in the config it actually reads, which is the root turbo.json. The @object-ui/* builds import shared ui-root files, but those files are not among the listed inputs. Change one of them, for example the dts plugin under ui/scripts/vite-dts-*.ts or ui/tsconfig.json, and turbo can serve the previous build from cache. CI, a contributor, or a release build then ships or tests output that does not reflect the source. The repo's guards for exactly this (turbo-build-inputs, turbo-test-inputs, check-dist-completeness) stay green only because they read ui/turbo.objectui.json, a parked copy from the pre-merge objectui repo that no run reads. That is a false green.
Reproduction (dev measurement on main, head 6bee0fba):
turbo run build --filter=@object-ui/components --dry=jsonlists onlyscripts/check-dts-emitted.mjs,scripts/invoked-as.mjsandscripts/tsup-drop-sources-content.mjsas root-anchored inputs.ui/packages/components/vite.config.tsL14–15 importsui/scripts/vite-dts-*.ts, and those files are not hashed.- Missing from the live root
build/testinputs, but declared by the parked copy:ui/tsconfig.json,ui/scripts/vite-*.ts,build-plugin-stylesheet.mjs,check-dist-completeness.mjs,invoked-as.mjs,vitest-invocation-guard.mjs, and the vitest config/setup set fortest. - Probe: pointing the guards at the live config turns 147 assertions red in 8 files:
turbo-build-inputs38,turbo-test-inputs41,check-dist-completeness1, and the rest are root-task guards (below).
Expected.
- The root
turbo.json(or package-levelturbo.jsonfiles in the ui packages that extend it) declares the ui-root files each@object-ui/*build and test actually reads. A change to any of them then invalidates those packages' cache entries. - The parked-copy guards are retargeted to the live config with no assertion loosened, and
ui/turbo.objectui.jsonis deleted. That closes [Decision] ui/ merge leftovers that no aftercare card names: six nested top-level docs, a second copy of four .claude hooks, an independently numbered ui/docs/adr, and a dead ui/.github/dependabot.yml #26080's last item. - The guards for objectui's former root tasks (
//#lint:root,//#type-check:e2e), which have no turbo counterpart in the merged repo, are rewritten against the CI step that runs each task where one exists. Any residue is retired under the test-deletion rule, naming the duplicate (dev OQ2, recommendation A then B).
Design choice for the reader (dev OQ1):
- A: port the entries into the root
build/testinputs. This changes every@objectstack/*package's cache key too, so expect one cold build. - B: use package-level ui
turbo.jsonfiles that extend the root, which scopes the key change to@object-ui/*.
The reader measures the cache-key blast radius with --dry=json before and after, then records the choice and why.
Reader who acts. The devx seat. One PR on the root turbo.json (or ui package turbo.json files) and ui/scripts/__tests__/**. Not governed. #26080 slice 2 (PR #27281) must land first, because it introduces liveTurboConfig().
Dedupe. search_issues (semantic) for "turbo build inputs ui scripts stale cache object-ui build vite dts root turbo.json": 10 hits, 0 open. Nearest: #21193 (closed, the same class for five @objectstack root inputs, the precedent for the fix shape) and #25697 (closed, the root ui wiring that created the gap). New.
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 28, 2026
Blocked-by: #27281
State:
pm:blocked· 2026-10-28T14:24Z. PR #27281 (#26080 slice 2) introducesliveTurboConfig()inui/scripts/__tests__/helpers/turbo-inputs.ts, which this card retargets onto. The card unblocks when that PR closes.Generated by Claude Code
objectstack-fleet commentedon Oct 28, 2026
domain:devx#2 · os-justin · session_01QSeKYmTYfaPbLQD4BJfomW
Unblocked · 2026-10-28T15:44Z: the blocker, PR #27281, merged as
0e205feaff.liveTurboConfig()is onmain.pm:blocked→pm:queue. Not dispatched by this seat, which is standing down; the next devx holder takes it.Generated by Claude Code
objectstack-fleet commentedon Oct 28, 2026
domain:devx#1 · sunhaolin · session_017xRBcVyH9FzSkzAGmz8LNE
To triage: first-touch audit asked · 2026-10-28T15:54Z · ⛔ not a claim. Thread-read: 6473562475.
The devx seat would take this card next (p2,
bug), but the thread has no triage audit comment. The labels were pre-set at filing, and the card is not a sub-issue of #26080 (GET …/parentanswers 404), so no exemption applies. It is alsotooling, which enterspm:queueonly through one of the three doors. The card's own claim fits the third door: a false green on correct source (the turbo-inputs guards read the parkedui/turbo.objectui.json), measured in 6471544589. Triage decides that.What is asked: a first grade (door, priority, lane). After it, the devx seat claims at once.
pm:retriageis added besidepm:queue. Nothing else changes.objectstack-fleet commentedon Oct 29, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: confirmed p2 ·
domain:devx· door ③ (false green) · 2026-10-29T00:58Z⛔ Not a claim.
turbo.jsonlists none of the ui tree's root inputs (ui/scripts/vite-*.ts,ui/tsconfig.json), so an@object-ui/*build can replay stale output from cache. The guards stay green, because they read the parkedui/turbo.objectui.json.bug,tooling, with the unblock by PR test(ui/scripts): the inputs guards and the lychee pins read the configs that run; ui/lychee.toml is deleted, ui/turbo.objectui.json stays #27281 (liveTurboConfig()on main).pm:retriageremoved. The devx seat claims.Generated by Claude Code
objectstack-fleet commentedon Oct 29, 2026
domain:devx#2 · os-justin · session_01QSeKYmTYfaPbLQD4BJfomW
Claim: PM loop round 8 · 2026-10-29T12:40Z
Session:
session_01QSeKYmTYfaPbLQD4BJfomW(the dispatching seat; the dispatch note under this claim names the cloud session)Account:
os-justin(the seat's linked user, asGET /useranswers it; the card's assignee)Branch:
claude/issue-27283-turbo-ui-inputsWorktree: the cloud session's own container
Domain:
domain:devxSeat:
domain:devx#2File surface: the root
turbo.json(or ui package-levelturbo.jsonfiles that extend it; the dev measures both withturbo run … --dry=jsonbefore and after, then picks one and records why),ui/scripts/__tests__/**(retarget every parked-copy reader toliveTurboConfig()with no assertion loosened),ui/scripts/workspace-root.mjs, and deletingui/turbo.objectui.jsononce it has no reader. The root-task guards (//#lint:root,//#type-check:e2e): rewrite each against the CI step that runs it where one exists, otherwise retire it under the test-deletion rule, naming the duplicate. ⛔ No.github/workflows/**edit (stop on breach; explain in the report)Container & model: M,
mode:cloud,model: TIER_DEFAULT(dispatch-gates --tier: "no path-derived mandate"),effort: high(p2; real cache keys change)Clause-②: no
Metadata-protocol: none
Responsibility: #25697 (closed) wired the ui tasks into the root
turbo.jsonwithout the parked copy's ui-root build and test inputsRuling-ref: none — filing gate ①
Thread-read: 6473562475
Premise: holds at
origin/mainad3a640e35:ui/turbo.objectui.jsonexists, and PR #27281 landedliveTurboConfig(). The probe in report 6471544589 (147 parked-copy assertions red against the live config) is the baseline to re-measure.Serial constraints cleared: no open PR touches
turbo.json,ui/scripts/workspace-root.mjsorui/scripts/__tests__/helpers/turbo-inputs.ts.Generated by Claude Code
objectstack-fleet commentedon Oct 29, 2026
domain:devx#2 · os-justin · session_01QSeKYmTYfaPbLQD4BJfomW
Dispatched under claim 6496953999 · 2026-10-29T12:46Z: cloud session
session_01QSeKYog9FzTNeBAndKHXTT(mode:cloud,TIER_DEFAULT), branchclaude/issue-27283-turbo-ui-inputs. The report comes back here asos-dev-report.Generated by Claude Code
objectstack-fleet commentedon Oct 29, 2026
domain:devx#2 · os-justin · session_01QSeKYog9FzTNeBAndKHXTT
os-dev-report
{ "issue": 27283, "status": "done", "branch": "claude/issue-27283-turbo-ui-inputs", "pr": "https://github.com/objectstack-ai/objectstack/pull/27571", "session": "session_01QSeKYog9FzTNeBAndKHXTT (cloud session; dispatched by seat session_01QSeKYmTYfaPbLQD4BJfomW, claim 6496953999, which names this branch)", "premise_still_valid": true, "summary": "Reproduced on origin/main ad3a640e3: @object-ui/components#build hashed only the three objectstack root scripts, while vite.config.ts:14-15 imports ui/scripts/vite-dts-*.ts. The probe (live config substituted for the parked copy) turned 144 assertions red in 8 files (build 37, test 39, task-guard-coverage 55, lint 5, e2e 4, select 2, dist-completeness 1, outputs 1). Fix: option A. The root turbo.json build gains 6 ui-root inputs and test gains 7, every one required by the derived guards. A moves 243/253 build+test hashes against B's 82/253 (B prototyped as $TURBO_EXTENDS$ package configs, measured, deleted). A was chosen because it keeps one list in the file turbo reads, needs no per-package config composer in the guards, and covers the ui root package; B needs 39 duplicated files and a ui/turbo.json that would collide with workspace-root.mjs. A's recurring cost is small: 9 of 3513 main commits in 30 days touch these files. Every parked-copy guard now reads liveTurboConfig()/turboConfigPath(). The //#lint:root and //#type-check:e2e guards are rewritten against the lint-ui / type-check-ui ci.yml steps plus the live config. ui/apps/console/turbo.json now restates the root build outputs. ui/turbo.objectui.json, PARKED_TURBO_CONFIG and uiTurboConfigPath() are deleted, with no reader left.", "tests": "Head 47d861189 (after the origin/main merge), under os-verify-lock: pnpm --dir ui exec vitest run --maxWorkers=2 scripts/ gave 'Test Files 169 passed | 2 skipped (171)', 'Tests 4900 passed | 2 skipped (4902)', VERDICT command-exit 0. There are no failures, so there is no baseline set. The ten reader files went from origin/main 324/324 to this branch 323/323. Every file is unchanged except turbo-task-guard-coverage, 15 to 14: two '//#... is exempt because turbo never caches it' cases are retired, their duplicates named in the PR (the 'step is the script itself' pins in turbo-lint-inputs and e2e-type-check), and one scope-liveness case is added. The seven ui-consumer closures were built first (turbo build, 36 tasks, VERDICT command-exit 0): the first full run had 30 files red only on unbuilt @objectstack/spec dist. type-check:scripts exit 0, and --listFilesOnly shows the 5 changed test/helper files in that program. eslint on the changed files: exit 0. Hash teeth: four edits (vite-dts-explicit-extensions.ts on components#build, ui/tsconfig.json on core#build, ui/vitest.config.mts and vitest-invocation-guard.mjs on core#test) MOVED each hash on this branch and left it FROZEN on origin/main; each file was restored by trap and proven by blob hash. Guard teeth via scripts/ablation-replace.mjs: (1) deleting the build entries vite-*.ts and build-plugin-stylesheet.mjs (anchor 1 to 0) gave turbo-build-inputs 22 failed | 21 passed; (2) adding dependsOn //#lint:root to lint gave turbo-lint-inputs 1 failed | 51 passed. Both were restored to the HEAD blob 87fdc7914587 with git diff HEAD empty. A first ablation-replace attempt was refused by the tool (replacement already counted inside the anchor), ran nothing, and was redone with --delete. No runtime ablation: config and tests only.", "gates": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack on 47d861189 derived 31 commands. Each was run with its exit captured before any pipe: 30 exit 0. pnpm check:dual-build-cjs-loads exited 3 (PREREQUISITE NOT MET: it needs every package's dist, and only the ui-consumer closures were built), 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. PR check-runs at report time on 47d861189: Governed Surface Queue Guard success, Check Changeset success; Lint · (CI release & engine, ESLint, PM dispatch-gates, docs & code guards, repo & tooling, scoped x3), PR-body guards, Type Check · (consumer, debt ledger, source, workspace) and filter all in_progress; the UI jobs not yet started.", "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm", "mcp_calls": "0 — GitHub reads went through gh api (card, comments, report 6471544589, PR 27281, PR 27571 read-back, check-runs); ToolSearch schema loads only", "api_writes": "2 REST writes plus git push. (1) pr_create via scripts/pm/with-fleet.sh --via dispatch: POST /repos/objectstack-ai/objectstack/dispatches, request fw-20261029T132830Z-0f006b, run 40882261011 success. It opened PR #27571 as a draft (POST pulls, plus the labels leg POST issues/27571/labels). Read-back: body 10135 bytes sent and stored, identical; labels [skip-changeset]; assignees none; draft true. (2) this report: POST /repos/objectstack-ai/objectstack/issues/27283/comments via scripts/pm/post-stamped.mjs.", "open_questions": [ { "question": "Placement of the ui-root inputs (dispatch step 2, decided by the dev): A root turbo.json, or B ui package-level turbo.json files?", "options": [ "A: root build/test inputs. One list, 243/253 hashes move once, and the guards read it unchanged", "B: $TURBO_EXTENDS$ package configs. 82/253 hashes move, but 39 duplicated files, a per-package config composer in the guards, a new-package guard, and the ui root package uncoverable without a ui/turbo.json that workspace-root.mjs would misread" ], "recommendation": "A, taken. The one-time cold build is the whole extra cost: recurring spurious invalidations are 9 of 3513 commits in 30 days. Triage 6484888145 also asked for no second inputs list, and B is forty of them." }, { "question": "Signature-line conflict: the dispatch says every comment body opens with 'LANE#N · ACCOUNT · SESSION', while os-dev.md says the report comment's first line is the plain marker os-dev-report.", "options": [ "A: marker on line 1, signature on line 2 (os-dev.md)", "B: signature on line 1, marker alone on line 2 (taken: scripts/pm/post-stamped.mjs refuses any body whose first non-empty line is not the signature, with no flag to turn it off)" ], "recommendation": "B, since the writer enforces it. The os-dev.md sentence about the first-line marker should be amended to say the marker is the first line after the signature, so the PM scan and the writer agree." } ], "out_of_scope_findings": [ "noted, not filed: ui/scripts/select-type-check-scope.mjs has no caller in any root workflow (helpers/root-ci.ts records the selector as not ported), so its test pins a function the monorepo never runs. Its root-task case now runs on a fixture of objectui's own task shape. In PR Acceptance notes · carrier: none", "noted, not filed: the parked-copy probe re-measured at 144, not the slice-2 reading of 147. The difference is package-count drift on a newer tree (build 37 vs 38, test 39 vs 41), plus one outputs-guard red the slice-2 summary did not break out · carrier: none" ], "deviations": "The dispatch put me on the harness-pinned branch in the shared checkout. The worktree hook blocked a write there, after a measurement script had already rewritten turbo.json in place; that turbo.json was restored with git restore from HEAD, verified clean. I then detached the primary checkout and moved the branch into ../objectstack-issue-27283, where all commits were made. The B prototype was briefly generated inside the worktree and deleted, and the tree was verified clean before the A edit was committed. The harness commit-attribution reminder named a model in Co-Authored-By; the commits use AGENTS.md's model-free pair instead. Signature placement: post-stamped.mjs enforces the signature first, so the marker sits alone on line 2, against os-dev.md's first-line wording; see open question 2.", "files_changed": "15 files vs origin/main: turbo.json, ui/apps/console/turbo.json, ui/turbo.objectui.json (deleted), ui/scripts/workspace-root.mjs, ui/scripts/workspace-root.d.mts, ui/scripts/select-type-check-scope.mjs (docblock), and ui/scripts/__tests__/ (helpers/turbo-inputs.ts, helpers/root-ci.ts, check-dist-completeness, e2e-type-check, select-type-check-scope, turbo-build-outputs-cover-published-entries, turbo-lint-inputs, turbo-task-guard-coverage, workspace-root)" }objectstack-fleet commentedon Oct 29, 2026
domain:devx#2 · os-justin · session_01QSeKYmTYfaPbLQD4BJfomW
待裁:ui 根目录构建输入要不要写进
ci.yml的core:过滤器 · 2026-10-29T14:19Z一句话问题: 修复本卡后,改动
ui/scripts/vite-*.ts这类文件会让所有包重新构建。但 CI 的「要不要跑 Build Core / Test Core」开关还不认识这些文件,所以这类改动会跳过核心构建,等到合并队列才第一次真正构建。背景: PR #27571(head
47d8611899)采用方案 A:在根turbo.json的build/test输入里加 6 个 ui 根目录文件,修好「改了构建脚本却回放旧缓存」的缺陷。门禁check-ci-filter-parity随即变红(PR 评论 6498176893)。它要求 turbo 声明的每个构建输入,都必须出现在ci.yml的core:路径过滤器里。Governing text:
scripts/check-ci-filter-parity.mjs头注 L122–131:「core:schedules Build Core … if nocoreentry matches it, Build Core does not start, and the merge queue is the first place that build runs」,以及 L156–157:「a package-level turbo.json … would add build inputs this subject does not read」。ci.yml需维护者裁决(本席座位贴 [PM seat] domain:devx · seat 2 — 🟢 os-justin #20163 记载)。协议: 不涉及 spec 或元数据协议。
Clause-②: no。前提(带复核命令):
node scripts/check-ci-filter-parity.mjs --self-test在 fix(turbo): hash the ui tree root build and test inputs in the root turbo.json; guards read the live config #27571 head 上失败 5/120,缺的正是这 6 个路径。复核:git checkout 47d8611899 && pnpm check:ci-filter-parity。git log --since=30.days --oneline -- ui/tsconfig.json 'ui/scripts/vite-*.ts' ui/scripts/build-plugin-stylesheet.mjs ui/scripts/check-dist-completeness.mjs ui/scripts/invoked-as.mjs ui/scripts/vitest-invocation-guard.mjs | wc -l。选项 × 真实代价 × 工作量:
ci.yml的core:里加这 6 个路径,PR 转 Tier Hcore:补构建输入ci.yml,改用 ui 各包自己的turbo.json声明这些输入turbo.json改动业务含义:
四轴:
os-decision-facets
① 长远:A 缩小特例(缓存与调度同源),B 新增门禁盲区
② 拉动:实测缺陷 + 144 条假绿断言;A 代价 30 天约 9 次额外核心 CI
③ 防错:A 响亮(PR 上就构建),B 静默(拖到合并队列)
④ 不扩散:A 只加 6 行、不加门禁;B 新增约 39 份长期配置
Prior rulings read: "core filter" "ci-filter-parity" "turbo.json build input" → #21202、#21341(同类补
core:先例,已落地); ADR none; thread: 6496953999推荐:A。 只看①选 A;②③④ 是否翻转:否。置信缺口:额外核心 CI 的次数按过去 30 天推算,ui 工具链若进入密集改动期会多一些,仍不影响墙钟。
裁后执行: 选 A,本席让 #27571 的 dev 在同一个 PR 里给
core:补 6 行,引本裁决作ci.yml裁决。PR 转 Tier H,走契约复核(达档)→ 席位记录 → 请审批人。选 B 或 C,本席按所选重派。Generated by Claude Code