Skip to content

fix(ci): the Lint turbo cache replays, and every turbo cache restores only its own job's (objectui#12276) - #12290

Merged
os-zhuang merged 6 commits into
mainfrom
claude/issue-12276-lint-turbo-cache
Oct 13, 2026
Merged

os-zhuang merged 6 commits into
mainfrom
claude/issue-12276-lint-turbo-cache

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #12276
Clause-②: no

The Lint job's turbo cache could not replay anything, and four jobs could restore each other's caches. This PR changes two things:

  1. turbo.json: //#lint:root now declares its own inputs. They are the extensions ESLint lints at that scope, minus the four trees the lint:root script passes as --ignore-pattern, plus !**/node_modules/**. Before this, the task had no inputs, so turbo hashed every file in the repository for it. Every package's lint depends on it, so any change anywhere moved every lint hash.
  2. The four turbo cache steps (Lint, Build Docs, Changeset Release, Bundle Analysis) now copy the shape of the type-check job (objectui#12222). Each job gets its own key prefix (lint, docs, release, perf) and restores by that prefix only. Pull-request and queue runs restore but never save. The runs that do save prune the cache to what they used, and every run posts one Turbo cache notice saying what it replayed and which key it restored.

Changeset: an empty-frontmatter declaration. No published package's files change, and check-changeset-presence.mjs agrees: "no changeset is owed".

Before reading (taken before any change)

  • Run linter in the Lint job, last 20 pull_request runs where the step ran (window 2026-10-12T15:02Z to 2026-10-13T00:57Z, Actions jobs API, step completed_at minus started_at): median 276.5 s, min 179 s, max 296 s. Measuring the after value on main is the PM's job once this lands.
  • Restore Turbo Cache in the same 20 runs: median 2 s, but 8 of 20 took between 12 and 23 s. The latest main push run took 29 s (run 38329504101). The lint cache itself holds only logs, so a restore that slow means some other job's build cache was pulled in. Job logs answer 403 to this container, so the restored key cannot be read directly. The new notice reports it on every run.

What was measured on origin/main before the change (74964ad6)

  • H1 (confirmed, re-measured). turbo run lint --dry=json: //#lint:root hashed 8652 input files. One untracked file under docs/ moved 46 of 46 package lint hashes.

  • H3 (confirmed). The four bare-fallback restores and their saves sit where the dispatch said (lint.yml, the ci.yml docs job, changeset-release.yml, performance-budget.yml). Lint, Bundle Analysis and the release job also saved under the same turbo-OS-SHA key, and the bare turbo-OS- fallback also matched the docs and type-check prefixes.

  • H5 (falsified). Most main push runs do complete:

    • Lint: 14 of the 20 newest main push runs succeeded and 6 were cancelled (window 2026-10-12T13:39Z to 2026-10-13T00:53Z).
    • Bundle Analysis: 16 succeeded, 3 were cancelled and 1 was still running.
    • The release job runs on the 6-hourly schedule and on publish pushes, all on main.

    So a push-only save does warm the cache. No schedule was added to lint.yml.

A turbo mechanism found while doing this

With explicit inputs, turbo 2.10.9 also hashes git-ignored files, which $TURBO_DEFAULT$ never does. The first version of the inputs had 64008 inputs, 63598 of them under the root node_modules. A git-ignored dist/ probe file at the root was hashed too. That is why !**/node_modules/** is in the list. The new pin's docblock records the remaining limit: a local git-ignored output dir with linted extensions outside the four trees would also be hashed, and none exists when Lint runs pnpm lint.

Proof that the inputs are right (H2, done three ways and then more)

The linted set comes from ESLint itself, not from the card: eslint . --ignore-pattern ... (the lint:root script) with --format json reported 407 files (34 under e2e/, 23 under eslint-rules/, 335 under scripts/, 15 root files).

turbo run lint --dry=json after the change: //#lint:root has 410 inputs:

  • exactly those 407;
  • public/mockServiceWorker.js, which ESLint ignores through **/public;
  • the root package.json and turbo.json, which turbo adds to every root task by itself.

Probes, each compared with the after baseline taken at a3ea39d0 (the same turbo.json as the head):

probe package lint hashes moved //#lint:root hash
untracked docs/ file 0 of 46 (46 of 46 before) same
untracked .changeset/ file 0 of 46 same
untracked content/ file 0 of 46 same
untracked file in packages/core/src/ 1 of 46 same
untracked scripts/ module (positive control) 46 of 46 moved
edit to the tracked scripts/invoked-as.mjs (positive control) 46 of 46 moved
untracked e2e/ spec 46 of 46 moved
edit to eslint.config.js 46 of 46 moved
edit to the root package.json 46 of 46 moved (turbo's own input)

Replay, run for real (TURBO_RUN_SUMMARY=true turbo run lint --filter=@object-ui/types, three runs under the verify lock):

  • cold run: Cached: 0 cached, 2 total;
  • second run on the same tree: 1 cached, 2 total;
  • third run after adding an untracked docs/ file: still 1 cached, 2 total.

//#lint:root stays uncached and re-runs every time. Its stable hash is what lets the package task replay. The report script's notice on that summary: lint 1/1 replayed · uncached //#lint:root (410 input files) · cache directory absent · restored nothing (cold). The script took 87 ms, which is where each new step's timeout-minutes: 1 comes from. "Cache directory absent" is a worktree effect: in a linked git worktree, turbo writes its cache under the main checkout's .turbo/cache. CI checkouts are not worktrees.

What a pull request can expect afterwards (a reading of the last 200 merges up to 74964ad6, not a live count):

  • 28 of 200 touched a //#lint:root input (scripts/, e2e/, a root .ts/.js/.mjs, the root package.json or turbo.json). Those still re-lint all 46 packages.
  • 2 touched eslint.config.js, eslint-rules/ or the lockfile.
  • Of the remaining 171, 80 touched exactly one package. On those, everything except that package's lint should replay.

Pins (new and changed)

  • turbo-lint-inputs.test.ts, new block "//#lint:root hashes the files it lints". It checks that:

    • //#lint:root declares inputs and $TURBO_DEFAULT$ is not one of them;
    • the ! inputs equal the lint:root script's --ignore-pattern values plus **/node_modules/** (read from package.json, not copied);
    • every file ESLint walks at that scope is hashed (the walk uses ESLint#isPathIgnored through check-lint-rule-coverage.mjs's walkedFiles);
    • no hashed file has an extension ESLint does not lint;
    • the task stays uncached.
  • report-turbo-cache.test.ts, new population block covering all five .turbo/cache jobs across .github/workflows/. It checks:

    • each key is turbo-OS-TAG-SHA, with exactly one restore key, its own turbo-OS-TAG-;
    • no tag is a prefix of another job's key;
    • no save on pull requests or queue builds in a workflow that has them;
    • a prune step before the save, running on the same events;
    • a report step with !cancelled() and RESTORED_CACHE_KEY from the restore step;
    • TURBO_RUN_SUMMARY set on a step between the restore and the report.
  • turbo-task-guard-coverage.test.ts: docblock only. //#lint:root has inputs now and is still on the uncached side of the partition.

  • render-budget-comment.test.ts: it pins that every script Bundle Analysis runs is in its paths: trigger closure, counts the job's !cancelled() steps, and reads the renderer's env contract. The report step added one script, one condition and one steps.* env var, so:

    • scripts/report-turbo-cache.mjs joins the perf paths: lists and TRIGGER_CLOSURE;
    • the count is 4, with a comment saying why;
    • the env scan is scoped to the render step's own block.

    This pin was not on the dispatch's H6 list. The full scripts/__tests__ run caught it.

  • content/docs/guide/ci-cd-pipeline.md: ci-cd-pipeline-doc.test.ts pairs every first-party command a job runs with its doc section, so the report script is named in the docs row and in the Lint, Performance Budget and Changeset Release sections.

Ablations (one-time; mutation through ablation-replace.mjs, restore proven by blob hash equal to HEAD and an empty git diff HEAD)

leg mutation result
A1 //#lint:root inputs key renamed away 3 red in turbo-lint-inputs.test.ts
A2c !**/node_modules/** renamed 1 red ("excludes exactly the trees ...")
A3b bare turbo-OS- restore key re-added in lint.yml 1 red ("keys each cache with its own prefix ...")
A4b perf save condition loses github.event_name == 'push' 1 red ("saves only on runs that are not a pull request ...")
A5b docs Build Site loses TURBO_RUN_SUMMARY 1 red ("reports what it replayed ...")
A6 release key back to the shared turbo-OS-SHA 1 red ("keys each cache ...")
A7 perf prefix nested under lint- 1 red ("gives no two jobs a prefix ...")

The first attempts at A2 through A5 were refused by the tool: the replacement was a substring of the anchor, or the anchor was ambiguous, so nothing was measured. They were re-run with valid anchors as A2c to A5b.

Local verification at 741484ae (the head)

Every exit code was captured before any pipe:

command exit verdict
pnpm exec vitest run --maxWorkers=2 --shard=1/2 scripts/__tests__ 0 Test Files 93 passed, 1 skipped (94), Tests 3228 passed, 1 skipped
pnpm exec vitest run --maxWorkers=2 --shard=2/2 scripts/__tests__ 0 Test Files 94 passed, 1 skipped (95), Tests 2403 passed, 1 skipped
pnpm type-check:scripts 0 lock VERDICT command-exit 0; --listFilesOnly lists all four changed test files
pnpm exec eslint on the four changed test files 0 no output
pnpm check:control-bytes 0 OK (scanned 8568 tracked text file(s); skipped 85 binary)
pnpm check:new-line-citations 0 0 new citation(s), enforcement report-only -> exit 0
pnpm check:action-ref-convention 0 109 action reference(s) in 29 workflow file(s) ... 0 off-convention
node scripts/check-pre-install-import-graph.mjs --self-test, then the real run 0, 0 16 cases pass; 39 pre-install step(s) in 19 job(s) ... every non-relative leaf a node builtin
node scripts/check-shell-escape-residue.mjs 0 OK (5/5 root(s) resolved ...)
node scripts/check-doc-links.mjs 0 Links are valid across 17 scan roots.
node scripts/check-doc-fence-languages.mjs --self-test, then the real run 0, 0 26 cases pass; every TypeScript block in 227 document(s) is fenced ...
node scripts/check-changeset-presence.mjs 0 no changeset is owed
node scripts/check-changeset-no-major.mjs 0 No changeset declares a major bump.
node scripts/check-governed-queue-guard.mjs --test (all 11 paths) 0 NOT GOVERNED. objectui#12255 lands the workflow row in parallel; the seat treats this PR as Tier H

Not run locally, left to CI:

  • the repo-wide pnpm lint;
  • the gates that need a full package build (doc snippet, example and JSON fence types; skill examples). This diff adds no fenced block;
  • the workflows themselves (the PR exercises Lint, Build Docs through turbo.json and ci.yml in its pathspec, and Bundle Analysis through its own file; the release job runs only on main).

Acceptance notes (observations, not filed)

  • The root package.json / turbo.json coupling remains. Turbo adds both to every root task's inputs, and every package's lint depends on //#lint:root. So an edit to either, or to anything lint:root lints, still re-lints all 46 packages (28 of the last 200 merges). Removing that would mean changing the lint task's dependsOn, and the card fences that task off.
  • Lint on push replays without TURBO_FORCE. The type-check full battery forces; this job does not. It trusts the inputs that the derived guard (turbo-lint-inputs.test.ts) pins. I considered forcing it and did not, because the card does not ask for it.
  • The //#type-check:e2e input e2e/** has the same git-ignored-files behaviour described above, but only on a local tree. CI checks out fresh.
  • The scripts/report-turbo-cache.mjs header still describes only the ci.yml type-check use. It is still true and now incomplete. It was left alone to keep the surface small; the next edit of that script can carry it.
  • objectui#12278 edits two comments in changeset-release.yml in parallel. This diff stays off them (its nearest change is the Build packages step's new env:, below the comment block that card edits).

维护者速读(草稿)

改了什么: 四个 CI 任务(Lint、Build Docs、Changeset Release、Bundle Analysis)的 turbo 缓存改为各用各的 key 前缀,只由 main 上的运行写入,写入前裁剪到本次实际用到的条目,每次运行发一条注解说明复用了什么、恢复的是哪个 key。另外 turbo.json 里根任务 //#lint:root 只按它真正检查的文件计算 hash。

为什么改: 以前仓库里任何一个文件变动都会让 46 个包的 lint 缓存全部失效,每个 PR 的 Lint 都要全量跑(实测中位数约 4.6 分钟);而且几个任务会互相恢复对方的大缓存,白白花时间、占缓存配额。

风险与代价(含回滚): 落地后第一次运行是冷缓存。PR 不再保存缓存,同一个 PR 再次推送时也从 main 读。改动 scripts/、e2e/、根 package.json 的 PR 仍会全量 lint(过去 200 次合并里约 14%)。不改变任何门禁的判定,只影响耗时。回滚:直接 revert 本 PR。

席位意见:

你要做的: 本 PR 改了 .github/workflows/**,按「两仓加治理行」作为需人工批准的受管改动保持 draft;请审阅后批准,其余由席位落地。


Generated by Claude Code

…ository (objectui#12276)

`//#lint:root` declared no inputs, so turbo hashed every tracked file for it,
and every package's `lint` dependsOn it: one untracked docs/ file moved all
46 package lint hashes and the restored cache could replay nothing. Its
inputs are now the extensions ESLint lints at that scope minus the four
trees the `lint:root` script ignores, which is what that script walks.

Claude-Session: https://claude.ai/code/session_011oKFDZMRoPGDbStvxQa172
Co-authored-by: Claude <noreply@anthropic.com>
…hes under explicit globs (objectui#12276)

Measured: with explicit inputs turbo 2.10.9 hashes git-ignored files too (64008 inputs, 63598 of them under the root node_modules).

Claude-Session: https://claude.ai/code/session_011oKFDZMRoPGDbStvxQa172
Co-authored-by: Claude <noreply@anthropic.com>
…ned and reported (objectui#12276)

`Lint`, the `docs` job, the release job and `Bundle Analysis` restored with a
bare `turbo-<os>-` fallback, and three of them saved under one shared
`turbo-<os>-<sha>` key, so each restored whichever job's cache reached the
ref first and saved it again. They now take the `type-check` job's shape
(objectui#12222): a per-job prefix and no other restore key, pull-request and
queue runs restore and never save, the saving runs prune to what they used,
and every run reports what the cache replayed and which key it restored.

Pins: `//#lint:root`'s inputs against ESLint's own walk at that scope, and
the five turbo caches' keys, save events, prune and report as a population.

Claude-Session: https://claude.ai/code/session_011oKFDZMRoPGDbStvxQa172
Co-authored-by: Claude <noreply@anthropic.com>
…d its contract pins scope to their steps (objectui#12276)

`render-budget-comment.test.ts` holds that every script the job runs is in its
`paths:` trigger closure, counts the job's `!cancelled()` steps, and reads the
renderer's env contract; the report step added one script, one condition and
one `steps.*` env var outside the render step.

Claude-Session: https://claude.ai/code/session_011oKFDZMRoPGDbStvxQa172
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 72 chunks) 3131.3 KB 3204.6 KB
Main entry chunk (gzip) 73.5 KB 350 KB
Entry file index-CJvpW5n9.js —
Status PASS —

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 20.69KB 7.62KB
app-shell (runtime-config.js) 22.59KB 7.89KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.11KB 3.87KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.40KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.70KB 3.93KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 41.19KB 11.12KB
auth (createAuthenticatedFetch.js) 8.54KB 3.46KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.26KB 8.03KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.28KB 2.60KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (commentAuthorship.js) 1.39KB 0.78KB
collaboration (index.js) 1.93KB 0.85KB
collaboration (useCollaborationTranslation.js) 6.50KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 588.51KB 141.71KB
core (index.js) 10.18KB 4.04KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 244.49KB 68.27KB
fields (index.js) 280.08KB 71.40KB
i18n (LocalizationContext.js) 2.92KB 1.42KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.52KB 2.39KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.35KB 12.88KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 36.87KB 9.88KB
i18n (useSafeTranslation.js) 7.14KB 2.92KB
layout (index.js) 40.26KB 11.71KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 15.35KB 5.51KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.82KB 2.38KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.04KB 3.92KB
plugin-calendar (index.js) 54.31KB 15.86KB
plugin-charts (index.js) 84.72KB 23.27KB
plugin-chatbot (index.js) 201.72KB 48.07KB
plugin-dashboard (index.js) 143.63KB 38.79KB
plugin-designer (index.js) 233.77KB 50.02KB
plugin-detail (index.js) 254.49KB 67.01KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 178.28KB 46.22KB
plugin-gantt (index.js) 178.63KB 45.19KB
plugin-grid (index.js) 255.09KB 70.91KB
plugin-kanban (index.js) 53.23KB 16.71KB
plugin-list (index.js) 123.35KB 31.54KB
plugin-map (index.js) 27.24KB 9.03KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 45.65KB 12.81KB
plugin-timeline (index.js) 39.21KB 11.85KB
plugin-tree (index.js) 15.07KB 5.33KB
plugin-view (index.js) 92.11KB 23.27KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 12.07KB 3.68KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 120.63KB 39.56KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
react (schema-input.js) 4.31KB 2.07KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.07KB 1.30KB
sdui-parser (index.js) 7.30KB 3.12KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 25.28KB 7.81KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.29KB 0.24KB
sdui-parser (validate.js) 23.87KB 7.83KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
types (authoring-nodes.js) 0.20KB 0.19KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (cloud.js) 0.20KB 0.18KB
types (complex.js) 4.44KB 2.07KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (dashboard-widget-layout.js) 2.06KB 0.96KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 1.13KB 0.65KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 5.78KB 2.70KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (node-slots.js) 7.18KB 2.34KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 19.93KB 7.26KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.48KB 3.50KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

维护者速读(终稿)

席位 domain:devx @ objectui seat 1 · session_011oKFDZMRoPGDbStvxQa172 · 2026-10-13T02:18Z · 对照 head 741484ae 的 diff 校正。

改了什么:

  • 让 Lint 的 turbo 缓存真正能命中。turbo.json 给根任务 //#lint:root 列出它实际 lint 的文件,原来是全仓。
  • 四个 job(Lint、Build Docs、Changeset Release、Bundle Analysis)改用各自的缓存前缀,不再互相抢别人的缓存。PR 与合并队列只读不写;main 上 push 先裁剪掉没用的条目再保存。每次运行都打一条「缓存命中了多少」的 notice。

为什么改:原来任何一个文件变化都会让 46 个包的 lint 全部重跑,缓存从未命中过(实测:一个 docs 文件就改动 46/46 个哈希)。docs job 本次还读到一份 2.9 GB 用不上的旧缓存。

风险与代价(含回滚):

  • 只改 CI 配置与缓存,不改 lint 规则,也不改任何包。
  • 合并后第一次 main push 之前,各 job 的缓存是冷的,与今天的实际效果相同。
  • 改根 package.json、turbo.json 或根目录脚本仍会让 46 个包全量重跑,那是 dependsOn 结构所致,卡片划在范围外。
  • 回滚:revert 这一个 squash commit。

席位意见:可合。CI 全绿,本 PR 自己的运行已显示根任务只哈希 410 个文件。dev 的测量推翻了「push 运行多半被取消、存不上缓存」的假设(20 次里 14 次成功),所以没加定时任务。合并后由本席核对验收:接下来三个 PR 的 Lint 至少命中 40/46,并对照合并前中位 276.5 秒。

你要做的:批准本 PR(os-zhuang 或 hotlong 任一账号的 Approve)。它改的是 .github/workflows/**,按您「两仓加治理行」的裁决,本席按受管处理。


Generated by Claude Code

@os-zhuang
os-zhuang marked this pull request as ready for review October 13, 2026 02:21
@os-zhuang
os-zhuang enabled auto-merge October 13, 2026 02:21
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 72 chunks) 3131.3 KB 3204.6 KB
Main entry chunk (gzip) 73.5 KB 350 KB
Entry file index-CJvpW5n9.js —
Status PASS —

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 20.69KB 7.62KB
app-shell (runtime-config.js) 22.59KB 7.89KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.11KB 3.87KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.40KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.70KB 3.93KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 41.19KB 11.12KB
auth (createAuthenticatedFetch.js) 8.54KB 3.46KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.26KB 8.03KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.28KB 2.60KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (commentAuthorship.js) 1.39KB 0.78KB
collaboration (index.js) 1.93KB 0.85KB
collaboration (useCollaborationTranslation.js) 6.50KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 588.51KB 141.71KB
core (index.js) 10.18KB 4.04KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 244.49KB 68.27KB
fields (index.js) 280.08KB 71.40KB
i18n (LocalizationContext.js) 2.92KB 1.42KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.52KB 2.39KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.35KB 12.88KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 36.87KB 9.88KB
i18n (useSafeTranslation.js) 7.14KB 2.92KB
layout (index.js) 40.26KB 11.71KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 15.35KB 5.51KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.82KB 2.38KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.04KB 3.92KB
plugin-calendar (index.js) 54.31KB 15.86KB
plugin-charts (index.js) 84.72KB 23.27KB
plugin-chatbot (index.js) 201.72KB 48.07KB
plugin-dashboard (index.js) 143.63KB 38.79KB
plugin-designer (index.js) 233.77KB 50.02KB
plugin-detail (index.js) 254.49KB 67.01KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 178.28KB 46.22KB
plugin-gantt (index.js) 178.63KB 45.19KB
plugin-grid (index.js) 255.09KB 70.91KB
plugin-kanban (index.js) 53.23KB 16.71KB
plugin-list (index.js) 123.35KB 31.54KB
plugin-map (index.js) 27.24KB 9.03KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 45.65KB 12.81KB
plugin-timeline (index.js) 39.21KB 11.85KB
plugin-tree (index.js) 15.07KB 5.33KB
plugin-view (index.js) 92.11KB 23.27KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 12.07KB 3.68KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 120.63KB 39.56KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
react (schema-input.js) 4.31KB 2.07KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.07KB 1.30KB
sdui-parser (index.js) 7.30KB 3.12KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 25.28KB 7.81KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.29KB 0.24KB
sdui-parser (validate.js) 23.87KB 7.83KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
types (authoring-nodes.js) 0.20KB 0.19KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (cloud.js) 0.20KB 0.18KB
types (complex.js) 4.44KB 2.07KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (dashboard-widget-layout.js) 2.06KB 0.96KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 1.13KB 0.65KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 5.78KB 2.70KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (node-slots.js) 7.18KB 2.34KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 19.93KB 7.26KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.48KB 3.50KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@os-zhuang
os-zhuang added this pull request to the merge queue Oct 13, 2026
Merged via the queue into main with commit 9d313eb Oct 13, 2026
33 of 34 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-12276-lint-turbo-cache branch October 13, 2026 02:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cd configuration documentation Improvements or additions to documentation tests

Projects

None yet

3 participants