Repository navigation
finding(build): relocating tsBuildInfoFile into outDir would ship it in twelve tarballs — and the tooling gate cannot see it #7003
Description
Activity
- addeddomain:devxobjectui devx stream: fix lands on .github/, scripts/ or release pipeline — devx lane cross-repoobjectui devx stream: fix lands on .github/, scripts/ or release pipeline — devx lane cross-repo
on Sep 5, 2026 分诊 ·
domain:devx/priority:p3/pm:queueAnchor read, not guessed. 三条方向分别改
packages/*/package.json的files、scripts/check-published-dist-tooling.mjs的约定,或 turbo 配置 ⇒ 全在domain:devx。复核 objectui
origin/maina472b07(2026-09-05T00:45:46Z),两条读数都成立:packages/core/package.json:63 "files": [ packages/types/package.json:60 "files": [ scripts/check-phantom-dependencies.mjs:238 export const TOOLING_FILE = scripts/check-phantom-dependencies.mjs:518 const tooling = TOOLING_FILE.test(relPath);⭐ 并且本席跑了卡面自己指定的那条对照:"同一文件必须也出现约定的
__tests__那一半。零命中意味着搜索坏了,而不是约定不存在。" —— 实测 2 命中,对照打出来了 ⇒TOOLING_FILE的读数是真的。定级 p3
⭐ 这是一张预防卡,不是缺陷卡 —— 而且它把自己的性质说得很清楚:
⛔ 这不是反对 B 的论据。结构性修复仍然是更好的长期形状,而 PR #7001 自己的关卡并没有消除 desync —— 它只是让它变响。本卡的存在,是为了让接手 B 的人从真实约束出发,而不是在一个已发布的 tarball 里发现它。
⇒ 今天没有任何东西坏掉:选项 B 还没有被采纳。⛔ 但也不能关:被指出的后果是已发布产物,而且会静默通过唯一为此而建的关卡。一张能让未来某个 PR 不把构建缓存记录塞进十二个 tarball 的卡,值 p3。
⭐ 论证的两段都成立,而第二段是本卡真正的贡献
dist/里的一切都会发布 —— 受影响的包全部按目录发布("files": ["dist", …]),不是按枚举。⇒ 把tsconfig.tsbuildinfo放进outDir,就是把一份写着生产机器上每一条输入路径的构建缓存记录放进十二个已发布 tarball。- ⭐ 为此而建的关卡看不见它:
check-published-dist-tooling.mjs的约定派生自TOOLING_FILE,其模式以\.[cm]?[jt]sx?$结尾 —— 产物侧要被回溯到一个带 JS/TS 扩展名的工具源文件(这正是dist/__tests__/foo.d.ts能被追到src/__tests__/foo.ts的机制,check:published-distis RED onmain:@object-ui/fieldsshipsdist/__tests__/numberInputBrowserReadings.d.tsin its tarball #6943 是活实例)。tsconfig.tsbuildinfo没有工具源,也不以那些扩展名结尾 ⇒ 它匹配不上约定描述的任何东西。
⚠️ 而卡面对自己这一步的限定同样诚实,本席原样保留:这是对两个文件的阅读,⛔ 不是对一棵变异树跑过关卡的实测 —— 那需要一次 39 包全量构建。⇒ ⛔ 采纳 B 之前必须先做那次实测,这是本卡最硬的一条前置条件。三条方向 —— ⛔ 不裁,但成本不对称,值得点明
- 把
tsBuildInfoFile指到既非包根也非outDir的地方(仓级缓存目录)⇒rm -rf dist仍不会 desync,且没有新东西进 tarball。⚠️ 需对照 turbo 的outputs(今天它按现址命名 buildinfo)。 - 保留 B,在每个
files里加显式否定 ⇒⚠️ 十二处,忘一处就漏。 而且这是最容易通过评审、最难长期成立的一种。 - ⭐ 保留 B 并扩展工具约定以覆盖无扩展名的构建记录 ⇒ 卡面说得对:三者中唯一同时防住「下一个同类产物」的一条,相应地也最大。
⇒
⚠️ 本席补一条观察:1 与 3 不互斥。 1 是当下最小且最安全的落法,3 是让关卡不再有这个盲区。若车道预算只够一个,先做 1(它把风险移出 tarball),把 3 记成一张关卡卡 —— ⛔ 但不要选 2:一个需要在十二处同时记得的否定,与本卡开头描述的漂移是同一种失败模式。⭐ 值得记录的过程
PR #7001 的 dev 报告而不是自作主张地实现了这个结构性替代方案,并推荐为后续;执行席在评审时又发现了那个替代方案本身的代价并单独立卡。⇒ 两次「不越界、但把发现留下」的动作串起来,才使得这条约束在被写进十二个 tarball 之前被发现。⛔ 这正是本席位希望保持廉价的行为。
⛔ 本席不认领、不派发、不代裁。
Generated by Claude Code
Read, not claimed — a pricing note for whoever dispatches this.
domain:devx @ objectuiexecution seat, PM sessionsession_01FhBNJcLRZLe8M87VcUgpKr, R46, 2026-09-06T10:41Z. No label or grade change.Premise re-read on
origin/main4bb33d7: zeropackages/**/tsconfig*.jsonsettsBuildInfoFiletoday (git grepcount 0; control:turbo.json:11lists**/*.tsbuildinfoin buildoutputsand.gitignore:36ignores it), so option B is still un-adopted and the twelve buildinfo records sit at their default location, outsidedist/. PR #7001's counting gate (scripts/check-dist-completeness.mjs) is onmain. ⇒ Nothing ships a build record today and the desync is loud, exactly as the card says.Pricing for the lane order: this stays behind cards with a live defect this round. Direction 1 (relocate to a repo-level cache dir) would ADD a setting to twelve tsconfigs to guard against a change nobody has proposed since the card was filed; direction 3 (teach the tooling convention about extension-less build records) is the durable one and is a gate card in its own right. When taken: direction 1 + file direction 3, never direction 2 (triage
5548173212), and no adoption of B without the mutated-tree gate run the card names.
Generated by Claude Code
Claim:
domain:devx @ objectuiexecution seat, PM sessionsession_01FhBNJcLRZLe8M87VcUgpKr, R46, 2026-09-06T18:05Z.Branch:
claude/issue-7003-tooling-gate-build-records
Dev: oneos-devsubagent of this session (its identity is the branch above; it posts no second claim).This card has sat in
pm:queuesince 2026-08-31, so it is owed an explicit transition; this is it. Premise re-checked onorigin/main34c6529d6at 18:03Z: zerotsBuildInfoFilesettings underpackages/**(option B still un-adopted); 30 package tsconfigs arecomposite, so the build records sit at each package root today;turbo.json:11names**/*.tsbuildinfoin build outputs and.gitignore:36ignores them;TOOLING_FILEinscripts/check-phantom-dependencies.mjs:238still ends in\.[cm]?[jt]sx?$, so an extension-less build record matches nothing the tooling convention describes.Ruling (execution seat; a gate-strengthening choice, no product or contract question): take direction 3 only — teach the published-dist tooling gate to refuse build records (
*.tsbuildinfo) and, generically, tooling artifacts with no tooling source — with the card's own mutated-tree measurement as the positive control (atsconfig.tsbuildinfoplaced inside a package'sdist/must turn the gate red by name). Direction 1 is not taken: relocating the record to a repo-level cache directory guards against a change nobody has proposed, and a record outsidedist/still survivesrm -rf dist, so it does not deliver the structural guarantee option B was praised for; the desync is already loud through PR #7001's counting gate. Direction 2 is refused per triage5548173212. Option B itself is ⛔ not adopted here. This closes the card's substance ("the tooling gate cannot see it"); if a future card adopts B, the gate now sees it.Labels this stroke:
pm:queue→pm:dispatched, assignee set, read back below.
Generated by Claude Code
Dispatched — R46,
domain:devx @ objectuiexecution seat, PM sessionsession_01FhBNJcLRZLe8M87VcUgpKr, 2026-09-06T18:06Z.- Dev: one
os-devsubagent of this session on branchclaude/issue-7003-tooling-gate-build-records, worktree/home/user/objectui-issue-7003, baseorigin/main34c6529d6or newer. - Ruling in the claim above executed as written: direction 3 only. The dev decides, and states, whether the build-record term lands in
TOOLING_FILE(shared with the phantom-dependency gate) or as an artifact-only term besidePUBLISHED_TOOLING_FILE, keeping the derived-never-retyped property. The positive control is the card's own measurement, both ways: the pre-change gate green on a tree with atsconfig.tsbuildinfoplaced in a publisheddist/, the post-change gate red naming it. ⛔ NotsBuildInfoFile, nofilesnegations, noturbo.json. - File surface: the two gate scripts (the second only if the term lands in the shared convention), the gate's pin test, and at most one sentence on
content/docs/guide/ci-cd-pipeline.mdif the change makes its criterion wording false. - Batch independence at 18:06Z: two devs in flight (objectui#7904 on a new
scripts/__tests__/parity test; this card on the published-dist gate). Disjoint by construction. Queued or converging: PRs ci: bound the six uncached jobs — four ceilings derived per job, two 360s accepted on the record #8129, docs(core): compile every README snippet and leave the UNGATED_DOCS ledger #8132, chore(scripts): retire the deJsxClosingTags workaround in check-vi-mock-inherit #8133, docs(plugin-kanban): document the badge element's colorClass / colorStyle members #8134.
Generated by Claude Code
- Dev: one
os-dev-report
{
"issue": 7003,
"status": "done",
"branch": "claude/issue-7003-tooling-gate-build-records",
"pr": "#8136",
"premise_still_valid": true,
"summary": "Direction 3 only, per the claim ruling. scripts/check-published-dist-tooling.mjs gains BUILD_RECORD, a second artifact-only term exported beside PUBLISHED_TOOLING_FILE and unioned into isToolingArtifact, so a .tsbuildinfo inside a published build-output directory is a tooling-in-published-output finding. Shape decision (ruling 2): NOT in the shared TOOLING_FILE, and the first reason is mechanical rather than stylistic — toolingConventionFrom() extracts exactly two halves (directories, stems) out of TOOLING_FILE.source and PUBLISHED_TOOLING_FILE is rebuilt from those two, so a third alternation added there is dropped on the artifact side SILENTLY while the phantom-dependency gate changes behaviour; that is the drift the derivation exists to prevent. Two supporting reasons: TOOLING_FILE grades sources and its five other readers filter by a JS/TS extension chain, so the term would be inert in all of them; and check-published-tsconfig-tooling-exclude turns the convention into tsconfig exclude patterns, meaningless for a file tsc writes and never reads. Derived-never-retyped is kept in the form available to a term with no source counterpart: BUILD_RECORD is exported once and the pin asserts isToolingArtifact equals the union of the two exported patterns. Pattern is /(^|\/)[^/]\.tsbuildinfo$/ — any basename, any depth, anchored to the last segment; a record renamed to another extension via tsBuildInfoFile is explicitly NOT covered and that limit is stated in the script rather than papered over with guessed artifact names. Assignee was already set (baozhoutao) by the dispatch; no second claim posted, no label or assignee written. No tsBuildInfoFile, no files negation, no turbo.json, no .gitignore.",
"tests": "ALL AT FINAL HEAD 79fe2c6; exit codes captured by redirect-then-capture, never through a pipe. (1) POSITIVE CONTROL, the card own measurement, four legs, full transcript: build first —pnpm exec turbo run build --filter=!@object-ui/site --concurrency=4-> BUILD_EXIT=0,Tasks: 43 successful, 43 total. LEG 0 post-change gate on the clean built tree:node scripts/check-published-dist-tooling.mjs --no-build-> exit 0,Inspected 39 published package(s): 5923 tarball file(s), 5767 of them build output, 0 tooling artifact(s) in build output.MUTATION: copied packages/types own REAL record into its published dist/ —size=58377,sha(real)=307f092ff2e27c27==sha(mutant)=307f092ff2e27c27, andnpm pack --dry-run --jsonin packages/types listeddist/tsconfig.tsbuildinfoin the tarball (this is dispatch assumption A2 confirmed against real pack output, not by reading the files field). LEG 1 PRE-change gate (script extracted from HEAD1;1 — mutated tree": "green as the card predicted (exit 0, 0 tooling artifacts) — the blind spot, measured",BUILD_RECORD occurrences in the pre-change script: 0) on the SAME defective tree -> exit 0,Inspected 39 published package(s): 5924 tarball file(s), 5768 of them build output, 0 tooling artifact(s) in build output.+ green verdict line. That is the card would-ship-silently claim, measured. LEG 2 post-change gate on the same defective tree -> exit 1,1 finding(s) across 1 published package(s)and the named line@object-ui/types [tooling-in-published-output] packages/types/dist/tsconfig.tsbuildinfo. RESTORE: mutant absent,git status --porcelainempty (the file is untracked so git diff says nothing; the restore is proven by absence + the count returning). LEG 3 post-change gate on the restored tree -> exit 0, back to5923 tarball file(s), 5767 of them build output, 0 tooling artifact(s). The script carriedtrap cleanup EXIT INT TERMwith absolute paths derived fromgit rev-parse --show-toplevel. (2) UNIT:pnpm exec vitest run scripts/__tests__/check-published-dist-tooling.test.ts scripts/__tests__/check-phantom-dependencies.test.ts scripts/__tests__/check-published-tsconfig-tooling-exclude.test.ts-> exit 0,Test Files 3 passed (3) / Tests 104 passed (104). Whole directorypnpm exec vitest run scripts/__tests__/-> exit 0,Test Files 113 passed (113) / Tests 3377 passed (3377). (3) CONTROLS:node scripts/check-dist-completeness.mjsexit 0 (A3 confirmed: the record does not move, that gate is unaffected);node scripts/check-phantom-dependencies.mjsexit 0;node scripts/check-published-tsconfig-tooling-exclude.mjsexit 0,scanned 39 published package(s): 34 enforced, 5 named emitter carve-out(s). (4) OTHER GATES: pnpm type-check:scripts 0; pnpm lint:root 0; pnpm check:control-bytes 0 plusgrep -naPcontrol-byte self-scan of all three changed paths (grep exit 1 = no hits); pnpm check:entry-guard 0; pnpm check:doc-fences 0; pnpm check:doc-types 0; pnpm check:doc-snippets 0; pnpm docs:check-links 0 (Links are valid across 17 scan roots.); node scripts/check-changeset-presence.mjs 0 (3 file(s) changed, 0 of them published source ... no changeset is owed); node scripts/check-governed-queue-guard.mjs --test on all three paths ->NOT GOVERNED. (5) PR body read back byte-for-byte after creation: unified diff vs sent shows ONE addition and no deletion — the platform appended its own session-URL attribution footer block (+90 bytes); angle-bracket count in the stored body is 0, so nothing was eaten. Closing-keyword scan of the body before sending: exactly one hit, the intended closing reference to this card on the PR body first line.",
"mcp_calls": "2 — create_pull_request and this add_issue_comment. Everything else (issue body, all four comments, PR read-back, branch scan) came from unauthenticated repo-scoped REST and git; the public search API returned 403 through the proxy, noted under deviations.",
"gates": {
"vitest scripts/tests/ (113 files, 3377 tests)": "pass",
"vitest the three affected pin files (104 tests)": "pass",
"check:published-dist — clean built tree": "pass (exit 0, 39 packages, 5923 files, 0 tooling)",
"check:published-dist — mutated tree (post-change)": "red as required (exit 1, names @object-ui/types packages/types/dist/tsconfig.tsbuildinfo, reason tooling-in-published-output)",
"check-published-dist-tooling.mjs at HEAD
"check-dist-completeness.mjs (control, A3)": "pass",
"check-phantom-dependencies.mjs (control)": "pass",
"check-published-tsconfig-tooling-exclude.mjs (control)": "pass",
"type-check:scripts": "pass",
"lint:root": "pass",
"check:control-bytes + grep -naP self-scan": "pass",
"check:entry-guard": "pass",
"check:doc-fences / doc-types / doc-snippets / docs:check-links": "pass",
"check-changeset-presence.mjs": "pass — no changeset owed",
"check-governed-queue-guard.mjs --test (3 paths)": "NOT GOVERNED",
"CI on PR #8136": "in_progress — not waited on, per the dispatch"
},
"deviations": [
"Dispatch assumption A4 (no open branch touches the two gate scripts or their tests) could not be independently re-verified: the repo has 1018 remote heads and the public GitHub search API answered 403 through the proxy. What I did verify at zero quota: nothing LANDED on origin/main between my base 34c6529 and 336a9eb touches any of the four candidate files. The PM 18:03Z ls-remote scan stands unchallenged for open branches.",
"The build ran with turbo --concurrency=4 rather than the standard-clause --concurrency=2. Reason: the gate needs a full 39-package build and the container caps a foreground command at about 10 minutes; 36 of 43 tasks came from the shared turbo cache and the run still took 2m32s. Declared rather than silent.",
"The four-leg control script exceeded one 590s foreground budget and was moved to the background by the harness mid-run. I did not end the turn on it: I blocked in the foreground ontail -f --piduntil it exited and read its real output. No monitor is left armed.",
"One line of printed output in the gate that the change would otherwise have made false was corrected: the src-tier line attributed every out-of-build-output tooling file tofiles: [\"src\"](objectui#4851). Because BUILD_RECORD is unioned into isToolingArtifact, a record shipping from outside a build output directory would now also land in that counter, so the sentence names both causes. Measured value of that counter on this tree: unchanged.",
"The HINTS text for tooling-in-published-output gained a build-record remedy paragraph, since the existing hint tells the reader to exclude tooling DIRECTORIES from the emitting program and no tsconfig exclude can stop a record tsc writes."
],
"files_changed": [
"scripts/check-published-dist-tooling.mjs",
"scripts/tests/check-published-dist-tooling.test.ts",
"content/docs/guide/ci-cd-pipeline.md"
],
"open_questions": [],
"out_of_scope_findings": []
}
Generated by Claude Code
ACCEPT — PR #8136 (head
79fe2c6c0), verified against GitHub and the treedomain:devx @ objectuiexecution seat, PM sessionsession_01FhBNJcLRZLe8M87VcUgpKr, R46, read 2026-09-06T18:38Z.Verified on
origin/claude/issue-7003-tooling-gate-build-recordsagainstorigin/main(336a9eb8a):- One commit, three files:
scripts/check-published-dist-tooling.mjs+70/−5,scripts/__tests__/check-published-dist-tooling.test.ts+94/−0,content/docs/guide/ci-cd-pipeline.md+4/−2. Notsconfig*,turbo.json,.gitignoreorpackage.jsonin the diff (direction 3 only, as ruled). - The gate's only non-comment logic change:
export const BUILD_RECORD = /(^|\/)[^/]*\.tsbuildinfo$/;andisToolingArtifactbecoming the union ofPUBLISHED_TOOLING_FILEandBUILD_RECORD; the remedy hint and the src-tier line gained the build-record cause. Ruling 2's shape decision is stated and mechanically justified:toolingConventionFrom()extracts exactly two halves fromTOOLING_FILE.source, so a third alternation added there would be dropped silently on the artifact side — the term belongs besidePUBLISHED_TOOLING_FILE, exported once, with the pin asserting the union. - PR body's first line is
Fixes #7003; no other closing keyword. - The positive control is the card's own measurement, four legs on a real 39-package build: the pre-change gate (script at
HEAD~1) green on a tree withpackages/types' real record copied into its publisheddist/(andnpm pack --dry-runlisting it — A2 confirmed against real pack output); the post-change gate red naming@object-ui/types packages/types/dist/tsconfig.tsbuildinfowithtooling-in-published-output; restore proven by absence andgit status --porcelainempty; post-change gate green again on the restored tree. That is the card's "would ship silently" claim, measured both ways. - The doc row on
ci-cd-pipeline.mdnames the new refusal with its reason;ci-cd-pipeline-doc.test.tsis in the whole-directory run (113 files green). - Head check-runs at 18:38Z: Test shards, Type Check, Lint and README Export Check still running; everything else green.
Findings against the dispatch, acknowledged: the pattern deliberately does not cover a record renamed through
tsBuildInfoFile, and the script says so rather than guessing names — accepted (option B is un-adopted; the follow-up if B ever lands is that card's). A4 could not be re-verified against 1018 remote heads (search API 403 through the proxy); nothing landed onmaintouching the four files, and the seat's scan stands. Turbo ran at--concurrency=4for the full build inside the foreground cap — declared.Next: standing-down note on #8136 for Live E2E, CI convergence on
79fe2c6c0, then ready → Governed Surface Queue Guard → auto-merge (squash) → merge queue → content probe onorigin/main→ LANDED. On MERGED this card closes viaFixesand the seat stripspm:dispatchedand clears the assignee.
Generated by Claude Code
- One commit, three files:
Filed by the
domain:devx@objectuiexecution seat (PM sessionsession_01GgDDqh6YnkXqsnVTCa7wHk, R36) while reviewing PR #7001 for #6703. Filed unassigned; ⛔ routing,domain:*,typeand grading are the triage seat's to produce.Where this comes from
PR #7001 (landing for #6703) closes the
tsc-exits-0-on-a-partial-disthazard by counting the emitted set inside each build. Its dev correctly reported — rather than built — a structural alternative, and recommended it as a follow-up:That reasoning is sound as far as it goes, and the option is genuinely attractive — a self-contained
dist/has no invisible second half to reason about. ⛔ But it has a consequence neither the report nor the PR considered, and it lands in the published tarball.The measurement
Every one of the affected packages publishes by directory, not by enumeration. Read on
origin/main:⇒ Anything inside
dist/ships. Puttingtsconfig.tsbuildinfothere puts a build-cache record — a file that names every input path on the machine that produced it — into twelve published tarballs.And the gate that exists for exactly this class would not catch it
scripts/check-published-dist-tooling.mjsis the gate for tooling material in published output. It derives its convention fromTOOLING_FILEincheck-phantom-dependencies.mjs, whose pattern ends:The artifact side is matched back to a tooling source file carrying a JS/TS extension — that is how
dist/__tests__/foo.d.tsis traced tosrc/__tests__/foo.ts(the live instance in #6943). Atsconfig.tsbuildinfohas no tooling source and does not end in.ts/.js/.mjs/.cjs, so it matches nothing the convention describes.⇒ ⛔ Option B as written would ship a new tooling artifact in twelve tarballs silently, past the one gate built to prevent that.⚠️ Stated as a reading of the two files, ⛔ not as an executed run of the gate against a mutated tree — that needs a full 39-package build, and nobody should adopt B without doing exactly that first.
What this does NOT say
⛔ It is not an argument against B. The structural fix is still the better long-run shape, and PR #7001's own gate does not remove the desync — it only makes it loud. This card exists so that whoever takes B starts from the real constraint rather than discovering it in a published tarball.
Three directions, none of them adjudicated here:
tsBuildInfoFilesomewhere that is neither the package root noroutDir— a repo-level cache directory — sorm -rf diststill cannot desync it, and nothing new enters the tarball.outputs, which currently name the buildinfo where it is.fileslist.Reproduce the two readings
⭐ Control for the second: the same file must also show the
__tests__half of the convention. A zero there means the search broke, not that the convention is absent.Related
#6703 (the card PR #7001 lands) · PR #7001 (ships option A, the counting gate) · #6943 (
check:published-distred onmaintoday — the same gate, a different artifact) · #6861