Skip to content

test(runtime,metadata-protocol,objectql): pay the lint root's first load at module top where a clocked case paid it - #29785

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-29627-warm-lint-cold-load
Nov 3, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-29627-warm-lint-cold-load

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by 田騏 · project thread
Fixes #29627

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:只改测试。三个包各加一个测试专用的预载模块(lint-root-preload.testkit.ts),在模块顶部加载 @objectstack/lint 包根;实测会在用例里冷加载它的 66 个测试文件(runtime 9、metadata-protocol 39、objectql 18)在文件顶部各引入一行。objectql 另加 lint 的 devDependency 和锚定源码别名。
  • 为什么改:第一次读视图或发布对象时,协议层才懒加载 lint 包根,vitest 下这次冷编译落在某个计时用例里:metadata-protocol 和 objectql 的这类用例空闲时约 0.7–0.8 秒,四路并发下最慢 2.3 秒,而 5 秒超时是同一个用例的预算,分片负载一高就可能超时变红。现在这笔开销在收集阶段付掉,这些用例降到几十毫秒。
  • 风险与回滚:不动产品代码,不加超时,不重试,不跳过,断言一条没改。回滚即还原本 PR。唯一越出测试文件的是 objectql 的 package.json/pnpm-lock.yaml 多一条 devDependency。
  • 先看:packages/objectql/package.json 与 packages/objectql/vitest.config.ts(新依赖与别名),packages/metadata-protocol/src/lint-root-preload.testkit.ts(为何不用 setupFiles 的实测),任一个测试文件顶部新增的两行。
  • 两分钟验证:pnpm --filter @objectstack/metadata-protocol exec vitest run src/protocol.lock-door-read-agree.test.ts --reporter=verbose,预期全绿,an overlay view declaring _lock=none 那个用例约 45 毫秒(main 上约 0.8 秒)。
  • 需要你做的:无,席位落地。

What changes

  • One preload testkit per package: packages/runtime/src/lint-root-preload.testkit.ts, packages/metadata-protocol/src/lint-root-preload.testkit.ts, packages/objectql/src/__fixtures__/lint-root-preload.testkit.ts. Each is a module-top import '@objectstack/lint' with a header naming the cost it moves and why it is not a setup file.
  • 66 test files import their package's testkit at module top (two lines each: a one-line comment and the import): runtime 9, metadata-protocol 39, objectql 18. Each is a file whose traced run paid the lint root's first load inside a case. No it, expect, timeout or helper changes.
  • objectql: @objectstack/lint becomes a devDependency (package.json, 3 lines in pnpm-lock.yaml), and vitest.config.ts gains the anchored ^@objectstack/lint$ alias to ../lint/src/index.ts. Both are forced, see Acceptance notes.
  • runtime vitest.config.ts: one comment on the existing #29603 alias now names the testkit. No resolution change.

Why

A served view read wraps its body in withFormLayoutWarnings, which awaits loadFormLayoutLint() (import('@objectstack/lint'), packages/metadata-protocol/src/metadata-diagnostics.ts). An object publish's build probes load the same root the same way (packages/metadata-protocol/src/build-probes.ts). Under vitest the first such load in a file is a cold transform of the lint graph, inside whichever case gets there first. A module-top import is paid during collection, which no timeout clocks, and the protocol's later import() gets the same instance back. The lazy import itself is product design and stays as it is.

How the population was measured

Not by grep. A scratch tracer (deleted) appended one counter increment to the lint root's transformed module and recorded, per test file, whether the counter moved during collection, in a hook, or inside a case. One run of each package's whole suite (both vitest projects) on 7dae3b3c0d:

package files load inside a case at module top already elsewhere
runtime 532 9 1 (meta-write-org-scope.test.ts) 0
metadata-protocol 308 39 4 1, in a describe-level beforeAll with a 120000 ms budget (protocol.read-lock-flags-write-door.test.ts), dropped
objectql 544 18 0 0

The card's coarse grep (real protocol plus a view read, minus files already importing the root) names 149 files in the three packages. The trace keeps 43 of them and drops 106, and it finds 23 positives the grep misses, mostly object publishes reaching the build probes. Of the card's three named runtime candidates, package-revert-stored-members and meta-save-preconditions-parity are kept; packages-seed-apply-org-scope is dropped (the lint root is never evaluated in that file).

Measurements

Each sample is one vitest process on one file (--maxWorkers=1, json reporter), under os-verify-lock.sh on a 4-vCPU shared box. "Idle" is 3 sequential runs. "4x" is the contention method from #29622: 4 identical copies of the same file run concurrently, 1 round, 4 samples. The number is the duration of the case the trace named. Before is 7dae3b3c0d; after is 4bab1b183f (the same base plus this change). 0 failed runs in 924 samples.

package before idle p50 / max before 4x p50 / max after idle p50 / max after 4x p50 / max
runtime (9 files) 237 / 542 436 / 920 119 / 384 118 / 408
metadata-protocol (39) 714 / 838 975 / 1241 25 / 65 29 / 88
objectql (18) 796 / 1139 1169 / 2282 108 / 231 139 / 347

In runtime the root already resolves to lint's source through #29622's alias, and that load is cheaper (roughly 100 to 300 ms) than the 1.5 MB dist/ bundle metadata-protocol and objectql read (roughly 650 to 1100 ms). It still lands in the case, so the 9 files are kept. After the change, a targeted re-trace of the 66 files shows the root evaluated once per file, at collection, and 0 in-case loads.

Per file, ms p50 / max

packages/runtime (9 files), first lint-loading case, ms p50 / max

file before idle before 4x after idle after 4x
domains/meta-app-package-id-key.test.ts 174 / 193 323 / 362 42 / 57 52 / 58
domains/meta-package-all-fold.test.ts 231 / 237 348 / 493 78 / 81 110 / 112
domains/meta-published-runtime-publish.test.ts 168 / 216 309 / 349 58 / 79 63 / 90
domains/meta-save-preconditions-parity.test.ts 232 / 263 436 / 444 119 / 126 114 / 135
domains/packages-export-base-unchanged-per-type.integration.test.ts 496 / 542 855 / 920 316 / 384 388 / 408
domains/packages-publish-drafts-change-note.integration.test.ts 353 / 375 563 / 610 228 / 241 262 / 297
package-list-commits-org-scope.integration.test.ts 328 / 344 525 / 685 167 / 178 236 / 260
package-promote-runtime-owner-refusal.integration.test.ts 216 / 218 270 / 352 67 / 69 97 / 106
package-revert-stored-members.integration.test.ts 394 / 395 508 / 772 212 / 240 247 / 274

packages/metadata-protocol (39 files), first lint-loading case, ms p50 / max

file before idle before 4x after idle after 4x
protocol-publish-drafts-closure.test.ts 728 / 759 922 / 1114 43 / 45 50 / 64
protocol-publish-drafts-endpoint-gate.test.ts 763 / 826 1001 / 1085 43 / 43 53 / 81
protocol-publish-drafts-object-field-refs.test.ts 751 / 816 851 / 880 31 / 31 43 / 52
protocol-publish-drafts-org-scope.test.ts 755 / 758 1026 / 1038 35 / 46 46 / 57
protocol-publish-drafts-package-scope.test.ts 770 / 834 958 / 967 35 / 36 32 / 36
protocol.app-package-id-key.test.ts 715 / 754 951 / 958 9 / 11 13 / 14
protocol.draft-publish-verdict.test.ts 738 / 777 1103 / 1211 16 / 16 18 / 28
protocol.extension-field-suffix.test.ts 700 / 782 1046 / 1103 23 / 25 33 / 41
protocol.flow-by-name-shipped-name.test.ts 761 / 766 1116 / 1159 14 / 14 16 / 25
protocol.list-slot-prefer-local.test.ts 675 / 796 967 / 1052 15 / 17 12 / 14
protocol.lock-door-read-agree.test.ts 810 / 814 823 / 872 45 / 47 40 / 58
protocol.lock-one-resolution.shard-1.test.ts 747 / 830 1049 / 1060 26 / 35 24 / 34
protocol.lock-one-resolution.shard-2.test.ts 725 / 759 1129 / 1194 21 / 29 24 / 32
protocol.lock-one-resolution.shard-3.test.ts 718 / 736 1034 / 1070 20 / 27 24 / 26
protocol.lock-one-resolution.shard-4.test.ts 681 / 718 1122 / 1200 20 / 32 28 / 34
protocol.lock-one-resolution.test.ts 787 / 824 1049 / 1083 27 / 35 33 / 42
protocol.lock-org-axis-agree.test.ts 676 / 775 1165 / 1241 29 / 29 41 / 51
protocol.named-base-overlay-write.test.ts 685 / 820 922 / 952 23 / 25 38 / 44
protocol.object-cascade-delete-objects.test.ts 668 / 694 978 / 999 3 / 3 3 / 4
protocol.object-disabled-actions.test.ts 641 / 648 934 / 969 3 / 3 6 / 13
protocol.object-registry-write-through-spelling.test.ts 735 / 838 1057 / 1143 32 / 33 39 / 52
protocol.org-scoped-write-refused.test.ts 665 / 714 846 / 913 32 / 38 45 / 69
protocol.package-publish-audit-rows.test.ts 683 / 684 1057 / 1134 11 / 11 16 / 23
protocol.packageless-save-address.test.ts 681 / 777 946 / 1077 12 / 18 19 / 25
protocol.publish-commit-capture-read-failure.test.ts 662 / 691 974 / 1054 46 / 48 69 / 82
protocol.publish-side-effects-canonical-type.test.ts 682 / 705 987 / 1022 34 / 36 38 / 42
protocol.publish-stored-type-canonical.test.ts 698 / 698 833 / 875 32 / 32 45 / 47
protocol.reads-environment-only.test.ts 678 / 686 965 / 1051 25 / 26 27 / 36
protocol.record-package-commit-durability.test.ts 673 / 738 955 / 1071 60 / 61 61 / 73
protocol.save-union-issues.test.ts 654 / 723 850 / 891 9 / 9 9 / 12
protocol.scoped-list-fallback.test.ts 736 / 741 853 / 977 10 / 11 17 / 20
protocol.seed-external-id-refusal.test.ts 764 / 786 1053 / 1115 26 / 33 28 / 32
protocol.served-content-hash.test.ts 763 / 770 1084 / 1130 22 / 24 19 / 24
protocol.stored-conversions.test.ts 738 / 776 997 / 1166 11 / 11 11 / 18
protocol.summary-rollup-publish-gate.test.ts 682 / 773 966 / 993 29 / 30 37 / 57
protocol.unknown-datasource-refusal.test.ts 754 / 757 923 / 976 37 / 48 46 / 52
protocol.unknown-grant-object-refusal.test.ts 672 / 725 818 / 888 11 / 15 11 / 12
protocol.view-container-reset-expansions.test.ts 788 / 795 1135 / 1200 58 / 65 83 / 88
view-container-runtime-expansion.test.ts 661 / 784 798 / 834 8 / 8 9 / 12

packages/objectql (18 files), first lint-loading case, ms p50 / max

file before idle before 4x after idle after 4x
build-probes.test.ts 707 / 741 1040 / 1416 11 / 12 15 / 16
environment-overlay-five-types.test.ts 931 / 983 1135 / 1368 92 / 101 126 / 140
package-disable-enforcement.test.ts 879 / 1101 1384 / 1489 162 / 162 223 / 225
protocol-commit-history.test.ts 713 / 761 1192 / 1268 10 / 16 16 / 28
protocol-custom-override-bare-slot-envelope.test.ts 676 / 724 1123 / 1222 33 / 40 55 / 66
protocol-custom-override-effective-read.test.ts 777 / 927 1222 / 1490 126 / 134 153 / 167
protocol-duplicate-package-names-in-step.test.ts 841 / 957 1713 / 2282 209 / 221 323 / 347
protocol-lock-artifact-package-axis.test.ts 809 / 871 1100 / 1170 27 / 28 28 / 36
protocol-meta.test.ts 711 / 798 1156 / 1288 25 / 26 38 / 43
protocol-packaged-view-base.test.ts 702 / 756 1080 / 1121 23 / 32 25 / 27
protocol-packageless-overlay-migration.test.ts 843 / 1139 1358 / 1420 130 / 169 167 / 224
protocol-packageless-read-dependent-package.test.ts 839 / 875 1225 / 1239 119 / 124 148 / 187
protocol-publish-package-drafts.test.ts 718 / 760 985 / 1058 16 / 22 35 / 47
protocol-publish-rollback.test.ts 703 / 719 1107 / 1179 46 / 48 49 / 71
protocol-revert-org-scope.test.ts 822 / 835 1246 / 1330 133 / 182 161 / 170
publish-package-drafts-backfill.test.ts 853 / 889 1084 / 1462 208 / 231 231 / 255
publish-package-drafts-response-conformance.test.ts 826 / 838 1052 / 1250 120 / 133 189 / 212
publish-package-drafts-seed-receipt-counts.test.ts 885 / 973 1390 / 1500 176 / 187 201 / 303
The 106 grep candidates the trace dropped (0 lint-root evaluations inside a case)

packages/runtime/src/audit-meta-item-org-scope.integration.test.ts, packages/runtime/src/domains/meta-item-query-decision.test.ts, packages/runtime/src/domains/meta-save-actor.test.ts, packages/runtime/src/domains/meta-save-destructive-remedy.test.ts, packages/runtime/src/domains/packages-adopt-orphans-readonly.test.ts, packages/runtime/src/domains/packages-customizations.test.ts, packages/runtime/src/domains/packages-publish-drafts-request-conformance.test.ts, packages/runtime/src/domains/packages-seed-apply-org-scope.test.ts, packages/runtime/src/http-dispatcher.test.ts, packages/runtime/src/meta-compound-arity-mint-door.test.ts, packages/runtime/src/meta-field-overlay-lock.test.ts, packages/runtime/src/meta-managed-content-seal.test.ts, packages/runtime/src/package-dependencies-patch.integration.test.ts, packages/runtime/src/package-duplicate-adopt-org-scope.integration.test.ts, packages/runtime/src/package-revert-code-shipped-members.integration.test.ts, packages/runtime/src/package-uninstall-org-scope.integration.test.ts, packages/runtime/src/package-uninstall-store-refusal.integration.test.ts, packages/runtime/src/stored-metadata-reader-seam.test.ts, packages/metadata-protocol/src/meta-overlay-cache.test.ts, packages/metadata-protocol/src/mutation-listeners.test.ts, packages/metadata-protocol/src/package-unmanaged-install.test.ts, packages/metadata-protocol/src/protocol-14078-audit-invalid-date-total-arm.test.ts, packages/metadata-protocol/src/protocol-publish-drafts-outcome.test.ts, packages/metadata-protocol/src/protocol.adr0005-org-override-rollback.test.ts, packages/metadata-protocol/src/protocol.audit-org-scope.test.ts, packages/metadata-protocol/src/protocol.audit-read-failure-propagation.test.ts, packages/metadata-protocol/src/protocol.batch-verb-driver-code.test.ts, packages/metadata-protocol/src/protocol.batch-verb-driver-text.test.ts, packages/metadata-protocol/src/protocol.bracketed-refusal-opener-absence.test.ts, packages/metadata-protocol/src/protocol.code-only-types.test.ts, packages/metadata-protocol/src/protocol.data-door-stored-content-hash.test.ts, packages/metadata-protocol/src/protocol.data-door-stored-metadata-filter-reads.test.ts, packages/metadata-protocol/src/protocol.data-door-stored-metadata-redaction.test.ts, packages/metadata-protocol/src/protocol.declines-stored-row-published.test.ts, packages/metadata-protocol/src/protocol.delete-object-registry-unregister.test.ts, packages/metadata-protocol/src/protocol.delete-receipt-wording.test.ts, packages/metadata-protocol/src/protocol.destructive-409-face-inventory.test.ts, packages/metadata-protocol/src/protocol.diff-canonical-type-and-history-outage.test.ts, packages/metadata-protocol/src/protocol.diff-credential-redaction.test.ts, packages/metadata-protocol/src/protocol.diff-dead-history-read.test.ts, packages/metadata-protocol/src/protocol.driver-text-disclosure.test.ts, packages/metadata-protocol/src/protocol.flow-layered-shipped-name.test.ts, packages/metadata-protocol/src/protocol.flow-org-override-closed.test.ts, packages/metadata-protocol/src/protocol.flow-stored-row-shipped-name.test.ts, packages/metadata-protocol/src/protocol.graft-folded-form-sections.test.ts, packages/metadata-protocol/src/protocol.hydrate-overlay-canonical-type.test.ts, packages/metadata-protocol/src/protocol.invalid-metadata-422-face-inventory.test.ts, packages/metadata-protocol/src/protocol.item-name-every-door.test.ts, packages/metadata-protocol/src/protocol.item-name-grammar.test.ts, packages/metadata-protocol/src/protocol.layered-code-unshipped-name.test.ts, packages/metadata-protocol/src/protocol.legacy-organization-read-seam.test.ts, packages/metadata-protocol/src/protocol.lifecycle-audit-rows.test.ts, packages/metadata-protocol/src/protocol.lock-gate-canonical-type.test.ts, packages/metadata-protocol/src/protocol.lock-gate-fail-closed.test.ts, packages/metadata-protocol/src/protocol.lock-one-resolution.kit.test.ts, packages/metadata-protocol/src/protocol.meta-types-degenerate-derivation.test.ts, packages/metadata-protocol/src/protocol.meta-types-mint-door-agreement.test.ts, packages/metadata-protocol/src/protocol.metadata-redaction.test.ts, packages/metadata-protocol/src/protocol.package-delete-refusal.test.ts, packages/metadata-protocol/src/protocol.package-door-before-gates.test.ts, packages/metadata-protocol/src/protocol.packaged-base-refusal.test.ts, packages/metadata-protocol/src/protocol.platform-schedule-org-gate.test.ts, packages/metadata-protocol/src/protocol.platform-store-system-opt-in.test.ts, packages/metadata-protocol/src/protocol.publish-item-draft-org-scope.test.ts, packages/metadata-protocol/src/protocol.read-lock-flags-write-door.test.ts, packages/metadata-protocol/src/protocol.read-seam-empty-accumulator.test.ts, packages/metadata-protocol/src/protocol.read-verb-canonical-fold.test.ts, packages/metadata-protocol/src/protocol.recovery-doors-mutation-projector.test.ts, packages/metadata-protocol/src/protocol.reference-derivation.test.ts, packages/metadata-protocol/src/protocol.revert-stored-type-canonical.test.ts, packages/metadata-protocol/src/protocol.rollback-carries-item-failures.test.ts, packages/metadata-protocol/src/protocol.row-binding-column.test.ts, packages/metadata-protocol/src/protocol.save-flow-canonicalization.test.ts, packages/metadata-protocol/src/protocol.save-meta-missing-item.test.ts, packages/metadata-protocol/src/protocol.save-receipt-wording.test.ts, packages/metadata-protocol/src/protocol.sealed-remedy-kind.test.ts, packages/metadata-protocol/src/protocol.stored-migration.test.ts, packages/metadata-protocol/src/protocol.stored-residue-resave.test.ts, packages/metadata-protocol/src/protocol.tenant-authored-write.test.ts, packages/metadata-protocol/src/protocol.ui-view-hidden-columns.test.ts, packages/metadata-protocol/src/protocol.ui-view-response-conformance.test.ts, packages/metadata-protocol/src/protocol.unrecognised-meta-type.test.ts, packages/metadata-protocol/src/sys-metadata-repository-14938-list-drafts-updated-at.test.ts, packages/metadata-protocol/src/sys-metadata-repository.package-writability.test.ts, packages/objectql/src/delete-meta-response-conformance.test.ts, packages/objectql/src/environment-overlay-email-locale-restart.test.ts, packages/objectql/src/overlay-precedence.test.ts, packages/objectql/src/protocol-bundle-member-base-address.test.ts, packages/objectql/src/protocol-destructive.test.ts, packages/objectql/src/protocol-environment-kernel-registry-write-through.test.ts, packages/objectql/src/protocol-flow-custom-override.test.ts, packages/objectql/src/protocol-layered-get.test.ts, packages/objectql/src/protocol-lock-enforcement.test.ts, packages/objectql/src/protocol-meta-types-rich.test.ts, packages/objectql/src/protocol-object-overlay-layer.test.ts, packages/objectql/src/protocol-org-overlay-registry-gate.test.ts, packages/objectql/src/protocol-package-lifecycle.test.ts, packages/objectql/src/protocol-per-package-layer-provenance.test.ts, packages/objectql/src/protocol-publish-canonical-fold.test.ts, packages/objectql/src/protocol-recorded-by-null.test.ts, packages/objectql/src/protocol-references.test.ts, packages/objectql/src/protocol-save-meta-repo-path-real-engine.test.ts, packages/objectql/src/protocol-save-meta-repo-path.test.ts, packages/objectql/src/protocol-view-identity-overlay.test.ts, packages/objectql/src/publish-meta-response-conformance.test.ts, packages/objectql/src/save-meta-response-conformance.test.ts

Why a testkit per file and not one setup file

Measured first: a setupFiles entry in each package (commit a89a1ccea2, superseded) did warm every case, but it made every file in the package pay the lint load, including the many that never reach it. Whole-suite runs, setup plus import worker time and wall, against the traced baseline on the same base:

package baseline setup+import / wall with setupFiles setup+import / wall
metadata-protocol 506 s / 207 s 707 s / 279 s
objectql 772 s / 312 s 1101 s / 433 s
runtime 1561 s / 608 s 1758 s / 689 s

So the shared piece is the one module per package, and only the files that reach the load import it. Its header tells the author of a new file when to add the import.

Acceptance notes

  • Deviation, objectql package.json and pnpm-lock.yaml. objectql does not depend on @objectstack/lint; metadata-protocol does, and loads it from its dist/. A static import of it from objectql code fails tsc with TS2882 (measured: src/__fixtures__/lint-root-preload.ts(1,8): error TS2882: Cannot find module or type declarations for side-effect import of '@objectstack/lint'), so the import needs the package declared. A devDependency is enough because the file sits under __fixtures__, which check:undeclared-dep-imports reads as test code (a .testkit.ts beside product source was red there).
  • The objectql alias is forced: removing it reds check:test-source-alias with @objectstack/objectql: NEW unaliased artifact import(s) ... @objectstack/lint. With it, the preload and metadata-protocol's own import() resolve to one source module.
  • The object-publish path is included. The card names the view read; the build probes load the same root on an object publish, and 23 of the 66 files reach it only that way. Same cost, same fix.
  • PM assumption 1, partly held: runtime's test(runtime): pay the view read's lint load at module top in meta-write-org-scope #29622 alias does make the cold load a source-graph transform, and it is about a third of the dist/ cost, but it still sits in 9 runtime cases. PM assumption 2, refuted: metadata-protocol resolves the root to dist/ (no alias; its check:test-source-alias row lists it).
  • No gate arm. check:test-source-alias is static, and this load is reached at run time through another package's code: whether a case's read serves a view record, or a publish includes an object, is data. A dependency-shaped arm (any package whose tests import the protocol must preload) would name about 40 packages beyond these three, most of them never reaching the load, and red untouched packages. The tracer above is the instrument that answers the question; it is a scratch tool and is not landed.
  • Not measured: packages outside runtime, metadata-protocol and objectql whose tests boot the real protocol.
  • No timeout, retry, skip or assertion changed. Forced test edits: none.

Verification

Head c1fdec3921 (branch merged with origin/main cba12d2218; delta vs main: 73 files, +240 / -1).

  • pnpm --filter @objectstack/metadata-protocol test: Test Files 310 passed | 4 skipped (314), Tests 22292 passed | 26 skipped, VERDICT command-exit 0.
  • pnpm --filter @objectstack/objectql test: Test Files 544 passed (544), Tests 10760 passed; test:repo: 1 passed, 5 passed. Both exit 0.
  • pnpm --filter @objectstack/runtime test: Test Files 533 passed (533), Tests 8540 passed | 19 skipped; test:repo: 3 passed, 1211 passed. Both exit 0.
  • pnpm --filter PKG typecheck for each of the three packages after a closure build with declarations: exit 0 each; tsc --listFiles includes each testkit.
  • eslint --no-inline-config over the 71 changed .ts files: exit 0.
  • node scripts/pm/dispatch-gates.mjs --commands on this head: 67 commands (73 paths, 241 changed lines). All 67 ran on this head. 66 exited 0 on the first pass; check:dual-build-cjs-loads exited 3 (PREREQUISITE NOT MET, no dist/ for 37 packages), then 0 after a full pnpm build. --ran reconciliation: 67 derived, 67 run, 0 NOT-MEASURED, 0 UNRUN. Also run, outside the derived set: check:authz-resolver, check:filter-alias-parity, check:route-ledger-census (rosters under touched directories) and check-nul-bytes, each exit 0.
  • No ablation: the diff changes no tested behaviour. The before/after table is the reverse reading.
  • Dogfood: n/a (no !, no (narrowing) arm).

🤖 Generated with Claude Code

…oad in a setup file, outside the clocked case

A served view read and an object publish load @objectstack/lint lazily;
under vitest that first load is a cold transform paid inside whichever
case makes the file's first such read. A traced full run of each suite
found it inside a case in 9 runtime, 39 metadata-protocol and 18 objectql
test files. Each package now imports the root in a setup file, which
vitest runs before collection and no timeout clocks. objectql gains the
anchored ^@objectstack/lint$ source alias, since it does not depend on
lint itself.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017ihL2gLghEqeDKTbDTC7Zd
…testkit at module top instead of a setup file

A setup file made every test file pay the lint root's load, measured at
about +200 s of worker time in metadata-protocol and runtime and +330 s in
objectql. A testkit per package, imported at module top by only the files
whose traced run showed the first load inside a case, moves exactly that
load into collection. objectql declares @objectstack/lint as a
devDependency so the import type-checks, and keeps the anchored source
alias that check:test-source-alias requires for it.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017ihL2gLghEqeDKTbDTC7Zd
…rm-lint-cold-load

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017ihL2gLghEqeDKTbDTC7Zd
@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Nov 3, 2026
@github-actions github-actions Bot added size/m dependencies Pull requests that update a dependency file tests labels Nov 3, 2026
@github-actions

github-actions Bot commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

⚠️ 4 changed file(s) yielded no anchor (packages/metadata-protocol/src/lint-root-preload.testkit.ts, packages/objectql/vitest.config.ts, packages/runtime/src/lint-root-preload.testkit.ts, …), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files. Nothing else in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 3 changed package(s)).

What this run could not see
  • 4 changed file(s) yielded no anchor (packages/metadata-protocol/src/lint-root-preload.testkit.ts, packages/objectql/vitest.config.ts, packages/runtime/src/lint-root-preload.testkit.ts, …) — pages documenting those are invisible to this run
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 42 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 8c289725301becbb07dec2e3105b8bacff714abb → packageMentionDocs.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:devx#2 · marchtian · session_017ihL2gLghEqeDKTbDTC7Zd

席位核对 — ACCEPT · domain:devx#2 · head c1fdec3921694b9b92c81f58c700a9a48796a3db · 2026-11-03T04:45Z

Checked against GitHub and the PR head, not against the report (os-dev-report 6595655176 on #29627).

  • Shape: a draft to main with Fixes #29627 once, Clause-②: no and Metadata-protocol: none. No assignees, no reviewers, skip-changeset. +240/−1 over 73 files: 66 test files gain a two-line module-top import, plus three lint-root-preload.testkit.ts modules, two vitest.config.ts alias lines, objectql's package.json and 3 lockfile lines.
  • Path surface: check-governed-merges --pr 29785 reads NOT governed (241 changed lines). No product code changes. The lazy import('@objectstack/lint') in metadata-protocol stays as designed.
  • Card rules, read in the diff and the report:
    • Measured, not grepped. A run-time tracer over each package's whole suite found the first lint-root load inside a case in 66 files (runtime 9, metadata-protocol 39, objectql 18). Of the card's 149 grep candidates, 43 are kept and 106 dropped with 0 in-case loads. packages-seed-apply-org-scope is dropped (it never loads the root), and so is one file that loads it only in a beforeAll with a 120 s budget.
    • The effect, p50 / max on the case that pays the first load (idle; 4-way contended): metadata-protocol 714/838 → 25/65 ms (975/1241 → 29/88); objectql 796/1139 → 108/231 ms (1169/2282 → 139/347); runtime 237/542 → 119/384 ms (436/920 → 118/408). That is 924 samples with none failed. A second trace shows every one of the 66 files loads the root once, before any case runs.
    • ⛔ No timeout, retry, skip or assertion changed.
  • Accepted deviations:
    1. objectql's devDependency on @objectstack/lint (workspace:*, plus 3 lockfile lines) is outside the claim's file list. It is forced, because tsc refuses an import of an undeclared package (TS2882). The seat checked for a cycle: lint depends on formula, sdui-parser and spec, none of which depends on objectql. Splitting objectql out would leave its 18 files cold. A shared setup file was measured and refused: about +39% suite wall time for objectql.
    2. skip-changeset: objectql ships only dist/, and a devDependency changes nothing a consumer installs. The seat lands on Check Changeset being green on this head.
    3. Scope includes object publishes (build-probes.ts), not only the view read the card names. 23 of the 66 files reach the same cold root only that way, at the same cost.
    4. The branch keeps a superseded setup-file commit and its reversal. The queue squash-merges, so main gets the net diff only.
  • Gate arm: not added. The reason holds: the load is reached at run time through metadata-protocol's code and depends on test data, so a static source rule would conscript about 40 untouched packages.
  • Gates (the dev's readings): dispatch-gates --commands 67 derived, 67 run, all exit 0 after a full build; --ran 0 NOT-MEASURED. Suites: metadata-protocol 22,292 tests, objectql 10,760 (+5 repo), runtime 8,540 (+1,211 repo), all exit 0. check:test-source-alias, type-check and eslint all exit 0. The seat lands once every check on this head is green.
  • Out of scope, not carded: the dev's two notes. Packages outside these three that start the real protocol were not traced; aliasing lint to source in metadata-protocol would shorten its check:test-source-alias ledger row. Each is an optimisation with no red behind it. Skills-docs: none — AGENTS.md's clocked-window rule already states the mechanism.
  • Landing: non-governed. Once every check on this head is green, the seat marks the PR ready and enables auto-merge.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review November 3, 2026 05:03
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge November 3, 2026 05:03
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Nov 3, 2026
Merged via the queue into main with commit 6d86e4a Nov 3, 2026
70 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-29627-warm-lint-cold-load branch November 3, 2026 05:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate tests

Projects

None yet

1 participant