Skip to content

refactor(objectql): move the package registration bodies out of engine.ts into engine-package-registration.ts (#25522 cut 17) - #26881

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/project-thread-y39a3j
Oct 27, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/project-thread-y39a3j

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Leehom · project thread
Part of #25522
Clause-②: no
Metadata-protocol: none
Ruling-ref: 6348891160 + 6441032442

维护者速读

  • 改了什么:把 engine.ts 里包注册区的三个方法体(convertPackageBodyForward 注册门的协议前向转换、registerPlugin 嵌套插件注册、resolveNavObjectNames 导航对象名解析)原样搬进新文件 engine-package-registration.ts;类上的方法签名、注释、位置不变,方法体换成转交。两者与留下的 registerApp 共用的 stampPackage / stampObjectPackage 原地加 export、由新文件导入(不复制)。engine.ts 21,733 → 21,661 行。
  • 为什么改:[maintainer] refactor(objectql): split src/engine.ts (23,903 lines, +7,501 in 30 days) along its ObjectQL seams, one move-only slice per PR #25522 按缝拆分 engine.ts 的第 17 刀。本区原定价 −233,但 feat!: ownership queues and assignment rules (ADR-0142 phase 2) #26337 落地时在 packages/spec/liveness/ 里把 engine.ts#registerMetadataCollections 登记为生产者(18 处),它按系列规则留在原文件;剩下可搬的三个方法重定价 −75,仍高于 −30,按派单取本区。
  • 风险与回滚:纯搬移,无行为变化;公开 .d.ts 六个文件与基线逐字节相同(含正控);运行期导出名单相同;构建产物里只多出新文件一个区段,外部包的导入顺序一条不差(新文件在 engine.ts 原先导入 @objectstack/metadata-core 的位置导入);一次性晚绑定探针在新旧两版构建产物上逐行一致。回滚 = revert 本 PR。
  • 先看:packages/objectql/src/engine-package-registration.ts(三个函数与 PackageRegistrationHost)、packages/objectql/src/engine.ts 里三个转交桩、新的导入行与两处原地 export。
  • 两分钟验证:pnpm --filter @objectstack/objectql exec vitest run --project local --maxWorkers=2 src/engine-nested-plugin-collections.test.ts src/registry-installed-package-retired-page-declarations.test.ts,应见两个文件全部通过(嵌套插件注册与注册门前向转换各自的测试)。
  • 需要你做的:无,席位落地。

Cut 17 of the packages/objectql/src/engine.ts split (#24586 ruling A, recorded in 6270097840), under the claim 6450667239. Three bodies of the "Package, app, plugin and driver registration" section move unedited into a new module, packages/objectql/src/engine-package-registration.ts, behind PackageRegistrationHost: convertPackageBodyForward (ADR-0087 D2's forward conversion at the package registration door), registerPlugin (a nested plugin's objects, plugin-as-app and stack collections, registered under its parent package) and resolveNavObjectNames (the navigation objectName resolution). No behaviour changes. Every published declaration file is byte-identical to the base. #25522 stays open for the later cuts.

Branch claude/project-thread-y39a3j (the card thread's harness-pinned branch), cut from origin/main at e1bfde3601, which contains cut 16's merge (d8b943b620, #26792) and #26337.

What changes

Why this area, and why it now pays -75 and not -233

Re-measured on e1bfde3601 with cut 11's measure script and cut 14's price step (price3.mjs), both unchanged. engine.ts is 21,733 lines, 6,745 of them from the last 30 days (git blame --since=2026-09-27T18:45Z, clone deepened to 2026-09-21 first); class ObjectQL holds 324 members, 259 methods and accessors. Query-options erasure sites: 8 at base (lines 8372, 9690, 9936, 10000, 10332, 13612, 13911, 18522), none in this area.

Cut 16's table priced the section at -233 with four bodies, registerMetadataCollections (166 body lines) the largest. #26337, the PR that held the section out, landed with 18 producer pointers packages/objectql/src/engine.ts#registerMetadataCollections in packages/spec/liveness/queue.json (6) and assignment_rule.json (12). A body named by an engine.ts#NAME pointer under packages/spec stays in this series, and the claim forbids packages/spec/** edits, so registerMetadataCollections stays, and with it METADATA_ARRAY_KEYS, resolveMetadataItemName and permissionSetOwnerRegistrationRefusal (its helpers) and the check-stack-collection-maps.mjs row that names METADATA_ARRAY_KEYS in engine.ts. registerApp and registerDriver stay as the order says (public, inferred return type). The other three re-price at -75. That still pays more than -30, so under the order this cut takes the section rather than the fallback (Delete cascade and reference walking, -124). It is also the colder of the two: 36 of 98 body lines from the last 30 days, against 128 of 167.

Seam table on e1bfde3601 (43 sections of class ObjectQL; U = untyped receivers)
range section first … last member lines 30-day lines methods inferred public U private statics
range section first … last member lines 30-day lines methods inferred public U private statics
--- --- --- --- --- --- --- --- ---
4706-4937 Instance state and constructor txStore … constructor 232 56 0 (0 public) - 0 -
4938-4960 Status and registry accessor getStatus … registry 23 0 2 (2 public) getStatus 0 -
4961-5304 Hook and function registry registerHook … triggerHooks 344 6 16 (16 public) registerHook, triggerHooks 1 -
5305-5660 Hook dispatch internals hasHooksFor … assertBulkPerRowHookBudget 356 61 7 (0 public) - 0 -
5661-5823 Action registry and activation registerAction … setActionActive 163 24 10 (10 public) - 1 -
5824-6105 Registered resolvers and write-gate probes _heldFileResolver … registerInsertRecomputeListener 282 116 6 (6 public) - 0 -
6106-6747 Readable-ids security and permission/formula resolution readableIdsSecurityReported … resolveDefaultPermissions 642 509 16 (2 public) - 0 -
6748-6854 Authz invalidation and middleware registration writeEpoch … hasObjectMiddleware 107 18 4 (4 public) - 0 -
6855-7032 Execution plumbing (middleware, session, provenance, users) executeWithMiddleware … buildUser 178 45 7 (0 public) - 0 -
7033-7241 Driver options and engine posture buildDriverOptions … resolveEnginePosture 209 150 6 (0 public) - 0 -
7242-7451 Organization probe and system-insert organization probeInstallOrganizations … transactionCoversDriverFor 210 45 4 (0 public) - 0 -
7452-7817 Field defaults resolveNowDefault … defaultFieldEntries 366 4 6 (0 public) - 0 -
7818-8430 Autonumber applyAutonumbers … seedAutonumber 613 80 6 (0 public) - 1 -
8431-9095 Package, app, plugin and driver registration registrationConversionWarned … registerDriver 665 170 6 (3 public) registerApp, registerDriver 0 -
9096-9304 Datasource registry supplyFileColumnsMovedResolver … listUnavailableDatasources 209 1 8 (7 public) - 3 -
9305-9456 Write guard and realtime publish assertWriteAllowed … publishBulkDataEvent 152 18 4 (1 public) - 0 -
9457-9564 Validation-message i18n setI18nService … objectDisplayLabel 108 24 4 (1 public) - 0 -
9565-10273 Referential integrity and master-detail resolution assertReferencesResolve … masterDeclaredFields 709 308 12 (0 public) - 0 -
10274-10389 Dangling-reference and cascade inspection inspectDanglingReferences … inspectCascadeDelete 116 48 2 (2 public) - 0 -
10390-10484 Crypto and tenancy providers setCryptoProvider … setTenancyPostureProvider 95 20 4 (4 public) - 1 -
10485-10904 Secret and internal-field handling encryptSecretFields … resolveInternalField 420 61 9 (3 public) - 0 -
10905-10973 Schema lookup and filter judging getSchema … judgeFilter 69 25 3 (2 public) - 0 -
10974-11239 Datasource routing LIFECYCLE_DATASOURCE … setDatasourceMapping 266 20 8 (3 public) setDatasourceMapping 0 -
11240-11407 Lifecycle init and health init … resolvePrimaryDatasource 168 16 3 (3 public) init 0 -
11408-11481 Media and value-shape presence objectHasMediaField … valueShapeStrictFor 74 13 4 (0 public) - 0 mediaFieldPresence, coveredValueFieldPresence
11482-12071 Migration flags and attestations isFileReferencesMigrationVerified … wasDatastoreCreatedFromEmpty 590 96 17 (8 public) - 0 -
12072-12094 destroy destroy … destroy 23 0 1 (1 public) destroy 0 -
12095-12113 Expand/cascade limits (statics) MAX_EXPAND_DEPTH … MAX_BULK_PER_ROW_HOOK_ROWS 19 0 0 (0 public) - 0 MAX_EXPAND_DEPTH, MAX_CASCADE_DEPTH
12114-12564 Roll-up summaries autonumberCounters … recomputeSummaries 451 0 7 (1 public) - 0 -
12565-12818 Related-record expansion expandRelatedRecords … expandRelatedRecords 254 10 1 (0 public) - 0 -
12819-13016 File-reference hydration resolveFileReferences … readParentDerivedFiles 198 183 5 (0 public) - 0 -
13017-13495 Query lowering resolveWhereTokens … requireFindOnePredicate 479 271 9 (0 public) - 0 -
13496-14052 find / findOne find … staticReadonlyCreateStrip 557 142 4 (2 public) - 0 -
14053-14364 validate validate … assignment 312 224 4 (1 public) - 0 -
14365-15734 insert / insertMany readUpdatePriorRow … insertMany 1370 462 3 (2 public) - 1 -
15735-17682 update update … update 1948 290 1 (1 public) - 0 -
17683-19223 Delete cascade and reference walking planCascadeAtomicity … organizationOwnedRowCounts 1541 733 22 (1 public) - 0 storedReferenceIncludes, remainderAfterMemberRemoval, referenceCheckContext, outlivesOrganization, keepsOrganizationReference, organizationCountContext
19224-19793 delete delete … delete 570 47 1 (1 public) - 0 -
19794-20413 count / aggregate count … aggregate 620 192 3 (2 public) - 0 -
20414-20722 execute and transactions execute … enforceTransactionOrigin 309 74 8 (2 public) - 0 -
20723-21015 Compatibility API registerObject … create 293 52 16 (16 public) - 0 -

The per-member screen and the full price output are in the project files, pm-orders/server/25522-cut17-seam-table.md. (The seam script's section boundary after validate now lands on an orphaned docblock, so its "validate" row reads validate … assignment; the member screen is unaffected.)

price (net lines) section taken?
-233 priced at fe53769a8e, -75 re-priced, -72 landed Package, app, plugin and driver registration yes (convertPackageBodyForward 28 body lines, resolveNavObjectNames 15, registerPlugin 55; registerMetadataCollections stays, pointed at from packages/spec/liveness/)
-124 Delete cascade and reference walking no: the order takes the registration section while it pays more than -30
-71 or less count / aggregate, Query lowering, the rest pays less
-27 resolveFormulaRelated a later cut, as the order says

The shape

  • The class keeps each of the three methods with name, modifiers, signature, docblock and position. A body becomes one host literal of exactly the PackageRegistrationHost members it reads, plus a .call, marked [#25522 cut 17], as cuts 8 and 10 to 16 did. Fields go as getters over a readX closure (registrationConversionWarned, logger, _registry); methods go as late-bound closures (resolveNavObjectNames, registerMetadataCollections), so a spy or double set on the engine is still the one a call reaches. resolveNavObjectNames reads no member, so its stub calls the function directly. No body assigns a field (registrationConversionWarned.add( mutates the set the getter returns, the same object), so there is no setter.
  • Series limits. convertPackageBodyForward is public with a declared return type (any); registerPlugin and resolveNavObjectNames are private, so their .d.ts line is private NAME; either way. No moved body has a this.FIELD.delete/insert/update( site, a private-static read, a self-call to insert / update / delete, an engine.ts#NAME pointer or an ADR anchor paragraph of its own.
  • Helpers, under the cut-14 seat ruling. registerPlugin reads two top-level helpers, both also read by bodies that stay: stampObjectPackage (registerApp) and stampPackage (registerApp, registerMetadataCollections, stampObjectPackage). Both gain export in place, byte for byte otherwise (leading docblocks included), and the module imports them as values from ./engine.js; nothing is copied. Measured alone before the move (dts-export-variant-cut17.sh, base engine.ts plus the export, no move): each export and both together leave all published declaration files byte-identical. index.ts and core.ts name their exports singly, so nothing new is published.
  • The run-time cycle and the load order (measured). The value import makes engine.ts and the module import each other. The module runs nothing at evaluation (declarations only). The moved convertPackageBodyForward was engine.ts's only reader of @objectstack/metadata-core, so that import line leaves engine.ts with it. The first build imported the module after ./engine-validate.js (the cut-16 position); that kept the internal module order but moved @objectstack/metadata-core's first import in the ESM bundles three externals later (after @objectstack/metadata/view-container, ajv, ajv-formats). So the module is imported exactly where engine.ts imported @objectstack/metadata-core. The module's other imports (./registry.js, ./engine.js, a type from @objectstack/spec/data) are already loaded by then: the bundles place registry.ts long before that region. Measured on the built bundles (bundlecmp.py; index.mjs, core.mjs, index.js, core.js): with the // src/engine.ts markers set aside (9 at base, 10 at head, because the new region splits one), the // src/... region order at head equals the base's with one line added, // src/engine-package-registration.ts, inside the engine.ts region where the import sat. Every external import statement (113 and 96 in the ESM bundles) and every require( call (148 and 130 in the CJS bundles) is identical and in the same order. The late-binding probe answers the same on base and head (Tests).
  • Imports. The module imports ServiceObject (type), applyArtifactForwardConversions and resolveDeclaredRange (@objectstack/metadata-core), computeFQN (./registry.js), stampObjectPackage and stampPackage (values) and ObjectQL (type) from ./engine.js. engine.ts loses the @objectstack/metadata-core import line (its last reader moved) and computeFQN from the ./registry.js line (likewise).
  • The module header names what moved, how the bodies reach the class, why registerApp, registerMetadataCollections and registerDriver stay, and how the helpers were handled.

Net lines (the seat's rule)

engine.ts goes from 21,733 to 21,661 lines, net -72 (30 added, 102 removed). The module is 192 lines. The diff against origin/main is 324 changed lines over 2 files (+222 / -102).

Readers of the moved lines

  • No census, ADR anchor, docs row or test key names a moved member by engine.ts path. Searched: engine.ts#NAME and engine.ts … NAME spellings of all three methods, the two helpers, registrationConversionWarned, applyArtifactForwardConversions, resolveDeclaredRange and computeFQN under packages/**, scripts/**, docs/** and content/**: none. The prose mentions found (packages/cli/src/** comments, docs/qa/platform-checklist/areas/platform-core.json, ADR-0029 line 559) name registerApp, registerMetadataCollections, METADATA_ARRAY_KEYS or computeFQN's behaviour, not a moved body.
  • scripts/check-stack-collection-maps.mjs's METADATA_ARRAY_KEYS row reads packages/objectql/src/engine.ts, where the constant stays (it is registerMetadataCollections' helper), so it does not move. The gate is green at head.
  • The bodies call no canReadObject( (core's row-serving-door-exposure.pin.test.ts is unchanged), no referenceTargetOf( (second-object-read-exposure.pin.test.ts is unchanged), no isFederatedObject / resolveTenantFieldName (federated-injected-column-readers.test.ts keys none), and read no isSystem (system-context.mdx is unchanged; its census is green). They hold no query-options/no-any-erasure site and no tenant-audit untyped receiver.
  • No ADR anchor paragraph describes a moved body (the engine.ts entry is the transaction-seam paragraph), so the module needs no anchor entry of its own.
  • Read-seam census (the cut-21 trap of [maintainer] refactor(metadata-protocol): split src/protocol.ts (33,065 lines, +10,169 in 30 days) along its ObjectStackProtocolImplementation seams, one move-only slice per PR #25467). check-durability-degradation-log-level --list at base and head: identical once line numbers are masked (164 lines: 48 durability seams and 88 read seams, the same entries); none of the moved names appears.
  • Decision-letter count: check-adr-anchors at base e1bfde3601 (clean detached worktree): 1,898 decision-letter citations on the anchored surface, 59,997 resolving citations across 8,409 files. At head: 1,898, 59,998 across 8,410 (the new module: one more file, and the header's ADR-0087 D2). The moved bodies cite ADR-0087, ADR-0010 and ADR-0122 without a decision letter, so no letter leaves the anchored surface.

Why

#25522 splits engine.ts one seam at a time so that a change to one area reads, reviews and conflicts as that area alone. The registration section was next in cut 16's table and was held out only for #26337, which has landed. Nothing about what the engine does changes.

Tests

No test is added and none is edited: the move is proved by identity, not by new behaviour, and no test reads a moved line by path.

Public .d.ts: byte-identical (ruling 6348891160 item 1), with the 6441032442 report block and a positive control

Built @objectstack/objectql at the base (e1bfde3601: engine.ts blob fa37297312da, no engine-package-registration.ts) and at head (engine.ts blob f9aa217bc55d), each from a clean src/ (dts-cut17.sh: the base leg restores all of src/ from the base and deletes the module; both legs check the blob). The builds are the moved code, not a stale cache: convertPackageBodyForward.call(host occurs 0 times in the base dist/index.mjs and once in the head's.

Every declaration file is raw byte-identical base to head, so 6348891160 item 1 holds without any normalisation, and no chunk is renamed (util-DdsJlVTF at both). The 6441032442 block, for the record (normaliser pm-orders/server/evidence/25522/dtsnorm.mjs, sha256 25226e0677bdad24726e9eff76a7e247bf7319440d2430d28cbaab0db6add94b, run as node dtsnorm.mjs --diff BASE_FILE HEAD_FILE):

file raw sha256 (base = head) raw-diff lines statements base / head normalised sha256 (base = head) declared-surface digest (base = head) import / export specifier sets
core.d.ts 642b5a3662c27aabde2dae4bb5877f515cb89f4c1e8075f21c0598b7e6025206 0 14 / 14 40e2fde86719d3ccd1cf492314c426c977ab836a5ab54580800cd1f41352247a 4450aaf478092516… (1 decl) EQUAL 0 / 84, EQUAL
core.d.mts 07f5718a09a2662c2f94bc3d79a8e74b67310efa82071a5d81ed1b716adaa69c 0 14 / 14 171eab8743fd8dfabc014a162c40c3e034be29f262b26a2bcc24cb3e06be0459 4450aaf478092516… EQUAL 0 / 84, EQUAL
index.d.ts 6dd970eb9c7dc4364e2fe99d22460672edc270302287f812231d823090491122 0 122 / 122 2bb6a21a929b41c8dbb85c72c600f87ed3a3af12ca731d1cace61f5d4aa3d57e 9136189ce4d0aba3… (103 decls) EQUAL 18 / 284, EQUAL
index.d.mts 37153868b48bef0e9ad655d74b549a30f51510977cb704e0d6624db1d8e83781 0 122 / 122 c7d7bab765f3b0fb35241fa03bd0b716a93139c98e5638d209379dad96528f98 9136189ce4d0aba3… EQUAL 18 / 284, EQUAL
util-DdsJlVTF.d.ts 856d98e0e45ced3ec15bf72a1ac09420a2c4f73b5165ab598c1d7d0000705f02 0 185 / 185 a4372dd015163a46e90b82473d806423a5ca982b8bef21e6e8bfed2b0655b9dd 71a464127e0b90b1… (173 decls) EQUAL 55 / 147, EQUAL
util-DdsJlVTF.d.mts 856d98e0e45ced3ec15bf72a1ac09420a2c4f73b5165ab598c1d7d0000705f02 0 185 / 185 a4372dd015163a46e90b82473d806423a5ca982b8bef21e6e8bfed2b0655b9dd 71a464127e0b90b1… EQUAL 55 / 147, EQUAL
  • (a) Every file: statements in the same order, statements only in base: 0; only in head: 0, declared-surface digest EQUAL. (b) Every normalised sha256 EQUAL. (c) Specifier sets EQUAL; the run-time export lists are too (index 182 names and core 54, ESM and CJS, the same sorted list at base and head). (d) Raw diff: 0 lines in every file; the same 14 files in dist/ at both. (e) After the head build, the full turbo run build (116 of 116 tasks, the dependents' DTS builds included) and pnpm --filter @objectstack/core typecheck ran on the head dist/: exit 0 each.
  • Premises re-checked at this head: (P2) packages/objectql/package.json exports names ./dist/index.* and ./dist/core.* only, driver-sql's ./dist/index.* only; (P3) git grep -n -E 'objectql/dist|driver-sql/dist' -- scripts package.json: 0 hits.
  • Each in-place export alone, before the move (dts-export-variant-cut17.sh, base engine.ts with only export added): none, stampObjectPackage, stampPackage, and both together all print the same util-DdsJlVTF.d.ts, index.d.ts 6dd970eb9c7d… and core.d.ts 642b5a3662c2….

Positive control. On this head, one sed dropped private from private registerPlugin( (a moved member's stub; anchor count 1 to 0, replacement count 0 to 1; engine.ts blob f9aa217bc55d to 7c36c1c279c1) and rebuilt (posctl17.sh, build exit 0). The chunk is renamed util-Xhg7Kcdw and now publishes registerPlugin(plugin: any, parentId: string, parentNamespace?: string, parentVersion?: string): void; (line 7555); its normalised sha256 is 668033198231cb9bda8545cd23975b0ce61c92a81952ef2dc84f8f692e694852 ≠ a4372dd0…, its declared-surface digest a2448d3c517a96ad… ≠ 71a464127e0b90b1… (the declare class ObjectQL statement named as the one changed), and the raw core.d.ts (d1b150bdbefd…) and index.d.ts (ac50bb3fc445…) change with the chunk name. index.d.ts normalises EQUAL under the control (an entry only re-exports), which is why every declaration file is read. The restore leg put the blob back to f9aa217bc55d (git diff HEAD empty), and @objectstack/objectql was rebuilt and its chunk compared equal to the head build before anything else read dist/.

AST compare (move-only)

astcmp.mjs (TypeScript compiler API, comments and whitespace ignored, tokens compared) on base engine.ts against head engine.ts plus the module:

  • The three moved bodies are SAME against base e1bfde3601 (parameters, return type, async, body tokens, and every body line equal after one level of indent): convertPackageBodyForward 309 tokens / 28 lines, resolveNavObjectNames 122 / 15, registerPlugin 367 / 55.
  • class ObjectQL: 324 of 324 members kept, in the same order; 321 byte-identical, and the 3 stubs keep docblock, modifiers and signature identical.
  • Top level: 229 of 234 base statements byte-identical. The other 5 (exportcmp.mjs): the @objectstack/metadata-core import, moved verbatim to the module; the ./registry.js import, which loses computeFQN; stampPackage and stampObjectPackage, exported in place with statement and leading docblock otherwise identical; and the class. The head adds one statement: the module import.
Load order (base and head bundles)

bundlecmp.py on the four built bundles. // src/engine.ts markers set aside (9 at base, 10 at head: the new region splits one), the region order at head is the base's plus // src/engine-package-registration.ts, everything else in order (index.mjs 98 to 99 regions, core.mjs 87 to 88, index.js 99 to 100, core.js 88 to 89). The external module statements are identical and in the same order: 113 import statements in index.mjs and 96 in core.mjs, 148 require( calls in index.js and 130 in core.js. The first build of this cut, with the module imported after ./engine-validate.js, kept the region order but printed engine.ts's @objectstack/metadata-core import (now the module's) 41 statements later in the ESM bundles, after @objectstack/metadata/view-container, ajv and ajv-formats; the second commit moves the import to the line where engine.ts imported @objectstack/metadata-core, which closes that.

Late-binding probe (base and head dist)

zz-probe-cut17.mjs (a one-off, not committed; in the project files) loads the built ObjectQL and drives the three moved bodies with doubles set on the instance after construction. Cases: convertPackageBodyForward on a non-object, a number and an array (returned as is); on a body with no declared range (the same reference back); on HotCRM 4.0.1's published body from src/__fixtures__/hotcrm-4.0.1-published-pages.artifact.json (pages converted, four warn lines, the second call warns nothing and returns a new body); with logger replaced after construction (the replacement warns); with registrationConversionWarned replaced after construction by a set that already holds one pair (that pair is not warned again, and the replacement set is the one filled). resolveNavObjectNames on nested navigation and on an app with none (the same reference). registerPlugin with array objects, navigation and an actions collection (objects stamped with the parent's id and version, the plugin-as-app registered, resolveNavObjectNames and registerMetadataCollections reached through spies set on the instance); with map objects and no version; with a registry that throws on objects and on the app (both warn lines); with _registry and logger replaced after construction; with both host methods replaced by doubles; and registerApp reaching a nested plugin through the real registry. The 42 output lines are identical on base e1bfde3601 and head (the 15 construction log lines, which carry timestamps, set aside); stderr is identical too.

Suites

Under scripts/pm/os-verify-lock.sh on head 1e7a0e96f:

  • Full turbo run build (the root build filters): 116 of 116 tasks, exit 0.
  • typecheck of @objectstack/objectql and @objectstack/core: exit 0 each.
  • @objectstack/objectql vitest --project local: 475 files, 8,843 tests passed, exit 0 (engine-nested-plugin-collections.test.ts, engine-capability-provenance.test.ts and registry-installed-package-retired-page-declarations.test.ts, which drive these bodies, among them).
  • Every packages/*/vitest.repo-tests.json project: core 6 files / 83 tests, objectql 1 / 5, rest 5 / 280 passed and 18 skipped, runtime 3 / 947, spec 80 / 1,295, types 1 / 11. Exit 0 each.

Verification

Head 1e7a0e96fe6b004796be314008b5972bd2292c6f. The gate list is node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack on this diff at that head (48 commands, merge base e1bfde3601), each run with its exit code captured before any pipe, then reconciled with --ran.

  • Derived gates: 48 of 48 run, exit 0 each; --ran: 48 run, 0 NOT-MEASURED, 0 UNRUN. check:dual-build-cjs-loads and check:dts-closure ran after the full build; check-plugin-teardown-shape --self-test after fetching 621a4876 and deepening the clone. The exit codes are in pm-orders/server/evidence/25522/cut17-ran-final.txt.
  • Hand gates, exit 0 each: node scripts/check-adr-anchors.mjs (counts above), node scripts/check-tenant-audit-census.mjs, node scripts/check-adr-symbol-anchors.mjs, node scripts/check-platform-checklist.mjs --changed-from origin/main, node scripts/check-commit-card-trailers.mjs --range origin/main..HEAD, node scripts/check-system-context-census.mjs, node scripts/check-nul-bytes.mjs, node scripts/check-durability-degradation-log-level.mjs --list (base and head identical, masked), and eslint as pnpm lint runs it (--no-inline-config) on packages/objectql.

Acceptance notes

  • Serial order. Nothing was edited before the claim 6450667239 named this branch. The branch is origin/main at e1bfde3601 plus two commits.
  • Overlap. Open PRs touching packages/objectql/**, scripts/check-stack-collection-maps.mjs or the two liveness files, listed at start (each head fetched, git diff --name-only origin/main...pr/N) and again just before opening this. At start: none touched engine.ts. Before opening: fix(objectql)!: a restrict reference refuses the delete of the record it protects even when the referencing row also cascades from it (#26718) #26864 now edits engine.ts in the delete-cascade section (hunks at lines 4518 and 18680 to 19416) and feat(spec): a hook declares its refusal catalogue, and its body refuses by code, answered in the caller's language #26831 edits engine-execution-plumbing.ts; neither touches this area, the two helpers or the import lines, and git merge-tree of this head with fix(objectql)!: a restrict reference refuses the delete of the record it protects even when the referencing row also cascades from it (#26718) #26864's head and with origin/main 20457f6f9 exits 0. Whichever lands second merges main.
  • Why -72 and not -233. feat!: ownership queues and assignment rules (ADR-0142 phase 2) #26337 made registerMetadataCollections a named producer in packages/spec/liveness/queue.json and assignment_rule.json (engine.ts#registerMetadataCollections, 18 rows). Moving it would mean re-keying those rows, a packages/spec edit this claim forbids, so it stays. Should a later card re-key them, registerMetadataCollections (about -150) and its three helpers can follow this module as their own cut.
  • The second commit. The first commit imported the module where cut 16 imported its own (after ./engine-validate.js). Measured, that moved the first @objectstack/metadata-core import in the ESM bundles three externals later; the second commit moves the import line so the external order is identical (Load order, above). Both commits are in the PR so the measurement can be re-run on either.
  • Receivers. The host literal carries registrationConversionWarned, logger and _registry as getters, so a body reads whatever the instance holds when it reads it: the probe replaces each after construction and sees the moved bodies use the replacements. No body assigns a field, so there is no setter.
  • Left as is. The interface docblock on PackageRegistrationHost is the series' shared paragraph, word for word as on ValidateHost. The moved registerPlugin body's comment "the manifest map branch above" now points across files; it is moved verbatim, not edited. ADR-0029 line 559 says computeFQN lives in engine.ts; it has lived in registry.ts since before this cut, and docs/adr/** is outside this claim.

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 (PackageRegistrationHost, engine-package-registration.ts).
  • 3. No formatter ran; pnpm lint passes on the touched package.
  • 4. One issue, one branch off an up-to-date main, one pull request.
  • 5. No publish-visible change, so skip-changeset; both declaration lines are filled in above.
  • 6. packages/spec 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, apart from the maintainer quick-read block the project asks for.
  • The Before Submitting list in CONTRIBUTING.md is done.

skip-changeset: move-only inside @objectstack/objectql, with every published declaration file byte-identical and no behaviour change, so there is nothing for a release note to say.

🤖 Generated with Claude Code

https://claude.ai/code/session_01FEkQ5psvNaMgx2AgmL3NgA

…e.ts into engine-package-registration.ts

The bodies of ObjectQL#convertPackageBodyForward, #resolveNavObjectNames and
#registerPlugin move unedited into engine-package-registration.ts behind
PackageRegistrationHost; the class keeps each method's signature, docblock
and position. stampPackage and stampObjectPackage, which registerApp and
registerMetadataCollections still read, gain export in place and are
imported back, never copied.

Claude-Session: https://claude.ai/code/session_01FEkQ5psvNaMgx2AgmL3NgA
Co-authored-by: Claude <noreply@anthropic.com>
…ported metadata-core

Keeps the evaluation order of every external package as it was in the
ESM bundles (the module brings the only metadata-core import engine.ts had).

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

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/objectql, touching 12 documentable anchor(s).

1 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/protocol/kernel/lifecycle.mdx (via registerPlugin (symbol, a method of class ObjectQL; a top-level function))
What this run could not see
  • 1 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 55 of 207 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • 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 — 19 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 1a8b7ebef23aefacc78f5ed0f1850ba9fe6f59dc → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 0514c3ad6440c509247d70b731b2e1fd0853b8a3 — the merge of head 1e7a0e96fe6b004796be314008b5972bd2292c6f into base 1a8b7ebef23aefacc78f5ed0f1850ba9fe6f59dc, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 0514c3ad6440c509247d70b731b2e1fd0853b8a3 && git checkout 0514c3ad6440c509247d70b731b2e1fd0853b8a3
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 1a8b7ebef23aefacc78f5ed0f1850ba9fe6f59dc 1e7a0e96fe6b004796be314008b5972bd2292c6f && git checkout -B drift-repro 1a8b7ebef23aefacc78f5ed0f1850ba9fe6f59dc && git merge --no-ff 1e7a0e96fe6b004796be314008b5972bd2292c6f

node scripts/docs-audit/affected-docs.mjs --json 1a8b7ebef23aefacc78f5ed0f1850ba9fe6f59dc

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 1a8b7ebef23aefacc78f5ed0f1850ba9fe6f59dc → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对

ACCEPT · domain:server seat 2 · os-project-manager · session_0186vfjHHMsRXC9EUT9hTFMo · 2026-10-27T20:13Z · head 1e7a0e96fe · #25522 cut 17

The maintainer's standing authorisation for landing reviewed PRs, verbatim: 「按照 pm-dispatch 规则复核过的都授权」.

  1. Shape:

    • A draft against main, assignee os-project-manager, labelled skip-changeset because this is a move.
    • The body opens with Part of #25522 (not Fixes). Clause-②: no and Metadata-protocol: none are line-initial, and Ruling-ref: 6348891160 + 6441032442 is present.
    • check-governed-merges --pr 26881 reads NOT governed, 324 changed lines.
    • The claim (6450667239) names this PR's branch.
  2. Area and price, against the order:

    • The order priced the registration section at −233. feat!: ownership queues and assignment rules (ADR-0142 phase 2) #26337 has since landed 18 engine.ts#registerMetadataCollections producer pointers under packages/spec/liveness/. A body named by such a pointer stays in engine.ts for this series, and the claim forbids packages/spec/**, so it stays, together with its helpers.
    • registerApp and registerDriver stay as the order says.
    • The other three bodies re-price at −75, which is still above the −30 bar. The order says this section is taken, not the fallback, at that price. The seat accepts the re-price.
  3. Move check (the seat's own): every non-blank line removed from engine.ts against origin/main was compared with the lines added to engine-package-registration.ts, indentation stripped. Every one is present there except these:

    • the ./registry.js import, which loses computeFQN (its last reader moved);
    • stampPackage and stampObjectPackage, which gain export in place and are imported, ⛔ not copied.

    What engine.ts gains is three delegating bodies, each one host literal of exactly the PackageRegistrationHost members it reads. The fields registrationConversionWarned, logger and _registry are getters. resolveNavObjectNames and registerMetadataCollections are late-bound closures. No moved body assigns a field: the warned-set is mutated through its getter, as before.

    • One wording nit is accepted, not fixed: the PackageRegistrationHost docblock ends with "and which one also has a setter", but no member has one.
  4. Published .d.ts under 6441032442: all six declaration files are raw byte-identical to the base, and the positive control is caught. check:adr-anchors counts 1,898 at both base and head.

  5. Overlap: git merge-tree exits 0 against main 161e00e4 and against fix(objectql)!: a restrict reference refuses the delete of the record it protects even when the referencing row also cascades from it (#26718) #26864, which is open in engine.ts's delete-cascade area.

  6. Gates:

    • At this stamp every check-run on the head reads success or skipped: 34 success, 16 skipped, none failed or in progress.
    • Locally the dev reports:
      • 48 of 48 derived gates exit 0, with 0 NOT-MEASURED;
      • a full build of 116/116;
      • objectql at 8,843 tests.
  7. Size: engine.ts goes from 21,733 to 21,661 lines (net −72).


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 27, 2026 20:14
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 27, 2026
Merged via the queue into main with commit cea0871 Oct 27, 2026
51 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-y39a3j branch October 27, 2026 20:52
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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants