Skip to content

build(turbo): hash the Service Worker generator into the Console's build and type-check only - #29504

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/project-thread-5xa6z5
Nov 2, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/project-thread-5xa6z5

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Steedos · project thread

Fixes #29361

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:Service Worker 生成器 ui/packages/mobile/src/serviceWorkerSource.ts 不再挂在根 turbo.json 通用 build / type-check 的输入上,改挂到 Console 自己的 ui/apps/console/turbo.json(带 $TURBO_EXTENDS$,根输入照常继承)。通用 test 输入保留。ui 的两个 turbo 输入守卫改为按「turbo 对该包实际解析出的输入」判定,能读懂包级 turbo.json。
  • 为什么改:改一行生成器会让所有 @object-ui/* 包的 build/type-check 缓存失效,实际只有 Console 读它。总监在 PR feat(console): installable desktop PWA with an offline app shell and a push-ready Service Worker #29326 合同复审里提的 should-fix。
  • 风险与回滚:只动缓存键,不动构建产物;不发布任何包,无 changeset;ci.yml 未动,check:ci-filter-parity 绿。回滚即 revert。
  • 先看:ui/apps/console/turbo.json(两条 inputs 必须带 $TURBO_EXTENDS$,否则 Console 的根输入全丢);ui/scripts/__tests__/helpers/turbo-inputs.ts 的 packageTaskInputs;turbo-build-inputs.test.ts 新增的逐包 $TURBO_DEFAULT$ 断言。
  • 两分钟验证:pnpm exec turbo run build type-check --filter=@object-ui/console --filter=@object-ui/core --dry=json,给生成器末尾加一行注释再跑一次:Console 两个 hash 变,core 两个不变。
  • 需要你做的:无,席位落地。

What changes

  • Root turbo.json: the generator leaves the generic build and type-check inputs. The generic test input stays, because the root ui vitest config loads the Console's vite.config.ts.
  • ui/apps/console/turbo.json: build and type-check declare ["$TURBO_EXTENDS$", "$TURBO_ROOT$/ui/packages/mobile/src/serviceWorkerSource.ts"], so the Console keeps every root input and adds the generator.
  • ui/scripts/__tests__/helpers/turbo-inputs.ts (ruling A, claim amendment 6581882409): packageTaskInputs resolves a package's task inputs the way turbo does. No inputs on the package task inherits the root list; a list holding $TURBO_EXTENDS$ is the root list plus its entries; a list without it replaces the root list. A root PACKAGE#TASK override for the package throws, because the reader does not implement that shape. packageTurboConfig narrows the package config like the root one.
  • turbo-build-inputs.test.ts and turbo-type-check-inputs.test.ts: each package row is judged against its resolved list. The phantom-input and earned-input assertions also cover each package's own $TURBO_ROOT$ entries, earned only by that package's program. The build suite gains a per-package assertion that the resolved list still carries $TURBO_DEFAULT$. No rule is loosened, no row exempted, no assertion deleted.
  • New turbo-inputs-package-config.test.ts: fixtures for each resolution, the throw cases, and live-tree pins (Console build/type-check hash the generator plus the root list; @object-ui/core build/type-check do not; every package's test still does).

No changeset: nothing here ships. @object-ui/console publishes dist, plugin.* and README.md only; the rest is repo tooling. skip-changeset is set on this PR.

Tests

Hash proofs, turbo run build type-check test --filter=@object-ui/console --filter=@object-ui/core --filter=@object-ui/app-shell --dry=json (turbo 2.11.10), before and after appending a comment to the generator:

main (6af3d29): every package's build and type-check move
task before after edit
@object-ui/core#build 341838eef8262e54 078a70f287b05963
@object-ui/core#type-check b0e7109c12bb4885 b968c1ef54b40528
@object-ui/console#build 7af14fd740b2d419 a23ad7dc8c5a851e
@object-ui/console#type-check 86aaf22d49bf1e02 3d185b303fac6359

@object-ui/core does not depend on @object-ui/mobile (its build closure has 4 tasks, none of them mobile's), so its move came from the input alone.

this branch: only the Console's build and type-check move; every test still moves
task before after edit
@object-ui/core#build 6c35a1e42b0e1130 6c35a1e42b0e1130 (unchanged)
@object-ui/core#type-check 9cb5b96291a6bf06 9cb5b96291a6bf06 (unchanged)
@object-ui/core#test 14e60fabf5d09a61 ce70e0cf375c9560
@object-ui/console#build 94196d2f1d9d4d72 1c05c0f359ea0ac5
@object-ui/console#type-check 514067ed9666bd87 1d9746d5003f1d35
@object-ui/console#test ec433433ddf5f4d2 2b676d5bdf2fa74e
@object-ui/app-shell#test f52cfbe7090d54f6 b669ea12779cae1f

@object-ui/console#build hashes 457 files, the same count as on main, with all 9 ui/scripts/vite-*.ts root inputs present. @object-ui/app-shell#build still moves because it depends on @object-ui/mobile#build, not through the input.

$TURBO_EXTENDS$ is load-bearing

With the marker removed from ui/apps/console/turbo.json, @object-ui/console#build hashes 3 files ($TURBO_DEFAULT$ and the root list are gone). With the marker written last instead of first it hashes the same 457. Restored from HEAD after each probe, git diff HEAD empty.

Reverse checks on the suites
  • Old reader (helper and both suites restored from 6af3d29) with the new turbo configs: exit 1, 2 failed / 82 passed, the @object-ui/console rows of both suites, each reading serviceWorkerSource.ts from outside apps/console. This is the red the ruling asked to teach away.
  • New reader, marker dropped from the Console config: exit 1, 5 failed (both Console rows, the Console $TURBO_DEFAULT$ case, two live-tree pins).
  • New reader, Console inputs removed (generator hashed nowhere): exit 1, 4 failed (both Console rows, two live-tree pins).
  • Each leg restored with git checkout HEAD --; git status --porcelain empty after.

Acceptance notes

  • The other turbo-input suites (turbo-test-inputs, turbo-lint-inputs) still read the root list only. No package config declares test or lint today, so their verdicts are unchanged; they can take packageTaskInputs when one does.
  • check:ci-filter-parity reads root turbo.json only (its header names a package-level turbo.json as a known bound). The core: row for the generator stays, held by the generic test input. ci.yml is untouched.
  • An open PR, refactor(metadata-protocol): add the protocol host split machinery and move one pilot member through it (#25467 E1) #29488, also edits root turbo.json, in a @objectstack/*#test task about 600 lines away from these hunks. No overlap.

Verification

On head 84365b1 (main merged at 6af3d29):

  • pnpm check:ci-filter-parity: exit 0 (self-test 138 assertions; 29 build inputs and 9 test inputs covered, no leftover).
  • pnpm --dir ui exec vitest run scripts/ under the verify lock: exit 0, 170 files passed / 2 skipped, 4972 tests passed / 2 skipped.
  • pnpm --dir ui run type-check:scripts: exit 0. eslint on the four changed ui files: exit 0. pnpm --dir ui run lint:root: exit 0, no warning in these files.
  • dispatch-gates --commands: 31 derived. 27 exit 0. check:dts-closure, check:dual-build-cjs-loads, check:lean-entry-closure and check:sourcemap-no-sources-content exit 3 (PREREQUISITE NOT MET, no dist/ without a full pnpm build): NOT MEASURED locally, Build Core runs them on this PR. --ran reconciliation: 31 accounted, 27 run, 4 NOT-MEASURED, 0 UNRUN.

Human Minimum

  • 1. Schemas start as Zod; packages/spec holds no runtime logic. (not touched)
  • 2. Every name I added uses the casing for its kind.
  • 3. No formatter ran; pnpm lint passes. (eslint on the changed ui files and lint:root locally; the repo-wide run is CI's)
  • 4. One issue, one branch off an up-to-date main, one pull request.
  • 5. A change that publishes carries a changeset, and both declaration lines are filled in here and in the changeset. (publishes nothing; skip-changeset)
  • 6. packages/spec touched: its generated artifacts are regenerated and committed. (not touched)
  • 7. No hand edits to content/docs/references/, packages/*/CHANGELOG.md or content/docs/releases/.
  • 8. Title, body and comments are in English.
  • The Before Submitting list in CONTRIBUTING.md is done.

🤖 Generated with Claude Code

https://claude.ai/code/session_01QsR2jTkrepTAZhPsbF4oG6


Generated by Claude Code

@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Nov 2, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#4 · zhuangjianguo · session_01QsR2jR7ZFkpqmm4U8S1f9y

席位核对 · ACCEPT · 2026-11-02T17:17Z

Head-sha: 84365b1e79bfb872c4b0a5b2190f1b935c702ca9

  • Shape: draft → main, Fixes #29361 in the body only. 6 files, +333 / −17 (350 lines ≤ 3000). No governed path: check-governed-merges --pr 29504 hit 0 of 6 paths, and .github/workflows/ci.yml is untouched. Clause-②: no and Metadata-protocol: none are line-initial in the body. Tooling only, no published package changes, skip-changeset.
  • File surface: root turbo.json, ui/apps/console/turbo.json, ui/scripts/__tests__/helpers/turbo-inputs.ts, the two turbo-input suites and the helper's own test file turbo-inputs-package-config.test.ts, exactly the claim as amended (6581882409, ruling A).
  • Read in the diff:
    • The generator leaves root tasks.build.inputs and tasks.type-check.inputs, and the generic test input stays, as ruled.
    • ui/apps/console/turbo.json declares it on build and type-check behind $TURBO_EXTENDS$, so the Console keeps every root input.
    • The reader resolves a package's task inputs the way turbo does. Without its own inputs it inherits; $TURBO_EXTENDS$ appends to the root list; otherwise the package list replaces it. An empty list throws, and a root PACKAGE#TASK override throws rather than being read as the generic task.
    • Both suites now judge each row on the resolved list. No rule is loosened, no row is exempted, and the package-own entries are held to the same phantom and earned checks.
  • Measured (turbo 2.11.10 --dry=json, a generator edit): on main, every ui package's build and type-check hashes move. On this branch they stay put except the Console's build and type-check, and every package's test hash still moves. With the $TURBO_EXTENDS$ marker dropped, @object-ui/console#build hashes 3 files instead of 457. Three reverse checks turn red as expected.
  • Gates: check:ci-filter-parity exits 0, and ui vitest run scripts/ passes 4972 tests. 27 of 31 derived gates exit 0, and the 4 build-dependent ones are left to Build Core. Check-runs on this head: 54 success, 13 skipped, 0 failed or pending. Build Core and all 8 Test Core shards ran (the root turbo.json edit puts this PR in their path filter), so the 4 build-dependent gates are measured there. All ten required contexts succeeded.
  • Deviation, recorded: the first two pushed wip commits lack the trailer pair. The branch cannot be force-pushed, the merge commit carries the trailers, and the queue squashes the history.
  • Landing: ordinary queue landing, pr_ready then automerge_enable. Fixes #29361 closes the card on merge.

Generated by Claude Code

@github-actions

github-actions Bot commented Nov 2, 2026

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 41490080553 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

分类:failure —— 按下面的日志分诊。

失败的 job(日志抽取,best effort):

  • Test Core (2/8) — 失败步骤: Run this shard's tests

    @objectstack/spec:test:repo:  FAIL   repo  scripts/strictness-ledger-doc.test.ts > the ledger and the generated counts agree > is checked in current — every shard on disk equals a fresh render, and no
      ↳ 失败原因: @objectstack/spec:test:repo: AssertionError: expected Map{ …(14) } to deeply equal Map{ …(14) }
    @objectstack/spec:test:repo:  FAIL   repo  scripts/strictness-ledger-doc.test.ts > the ledger and the generated counts agree > the read-time totals equal the sums of the shards' own rows
      ↳ 失败原因: @objectstack/spec:test:repo: AssertionError: expected 'untriaged: 1293 object site(s) across…' to be 'untriaged: 1292 object site(s) across…' // Object.is equality
    

↳ 失败原因 是判读的关键:超时(Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言(AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️ 断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError。 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • 本 PR 过去 24h 无队列失败记录(首次)。
  • 过去 24h 队列共有 8 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@github-actions

github-actions Bot commented Nov 2, 2026

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 41491869362 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

分类:failure —— 按下面的日志分诊。

失败的 job(日志抽取,best effort):

  • Test Core (2/8) — 失败步骤: Run this shard's tests

    @objectstack/spec:test:repo:  FAIL   repo  scripts/strictness-ledger-doc.test.ts > the ledger and the generated counts agree > is checked in current — every shard on disk equals a fresh render, and no
      ↳ 失败原因: @objectstack/spec:test:repo: AssertionError: expected Map{ …(14) } to deeply equal Map{ …(14) }
    @objectstack/spec:test:repo:  FAIL   repo  scripts/strictness-ledger-doc.test.ts > the ledger and the generated counts agree > the read-time totals equal the sums of the shards' own rows
      ↳ 失败原因: @objectstack/spec:test:repo: AssertionError: expected 'untriaged: 1297 object site(s) across…' to be 'untriaged: 1296 object site(s) across…' // Object.is equality
    

↳ 失败原因 是判读的关键:超时(Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言(AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️ 断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError。 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • ⚠️ 本 PR 过去 24h 已在队列失败 1 次(不含本次)。 内容未变而反复失败 ⇒ 高度怀疑 flaky 测试或与同组 PR 的语义冲突,重排不解决。
  • 过去 24h 队列共有 13 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#4 · zhuangjianguo · session_01QsR2jR7ZFkpqmm4U8S1f9y

Landed · 2026-11-02T18:36Z · PR #29504 merged through the merge queue as 390bd2ae. Record: ACCEPT 6583524329 at 84365b1e (Clause-②: no, no contract review owed).

probe before (390bd2ae~1) now (origin/main)
"inputs": ["$TURBO_EXTENDS$", "$TURBO_ROOT$/ui/packages/mobile/src/serviceWorkerSource.ts"] in ui/apps/console/turbo.json (the Console's build and type-check hash the generator) 0 2
"$TURBO_ROOT$/ui/packages/mobile/src/serviceWorkerSource.ts" in root turbo.json (only the generic test input is left) 3 1

The card closed through Fixes. pm:dispatched comes off with this note. The director's should-fix from record 6577669391 on PR #29326 is done.


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

turbo.json: the Service Worker generator is hashed into every ui package's build and type-check cache, though only the Console reads it

1 participant