Skip to content

refactor(objectql): move the master-detail and related-record bodies out of engine.ts into engine-master-detail-related.ts (#25522 cut 14) - #26489

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

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

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

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

维护者速读

  • 改了什么:把 engine.ts 里「引用完整性与主从解析」一段的 4 个方法体(主从外键落地、写入预检的主记录、校验规则谓词读取的关联记录、一跳关联读取)原样搬进新文件 engine-master-detail-related.ts,类上方法签名与注释不变,只把方法体换成转交;只被这些方法体用到的 2 个小工具函数随之搬走,与留下的方法共用的 4 个工具函数原地加 export、由新文件导入(不复制)。engine.ts 22,423 → 22,085 行。
  • 为什么改:[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 的第 14 刀。缝表重测后净减行最多的是 validate(约 −438),但它要共用的一个工具函数一旦导出,发布的 .d.ts 就会重新排序(内容相同、字节不同、分块文件名改变),不符合本刀「字节相同」的硬线,所以本刀改取第二名。
  • 风险与回滚:纯搬移,无行为变化;公开 .d.ts 六个文件逐字节不变;新文件与 engine.ts 互相导入(运行期循环),已用一次性探针在新旧两版构建产物上逐行对照(33 行完全一致),且发布包把两者打成一个文件。回滚 = revert 本 PR。
  • 先看:packages/objectql/src/engine-master-detail-related.ts(搬入的方法体、两个随迁工具函数与 MasterDetailRelatedHost)、packages/objectql/src/engine.ts 里 resolveOneHopRelated 的转交桩与四处原地 export、packages/core/src/security/second-object-read-exposure.pin.test.ts(新文件的一行分类)。
  • 两分钟验证:pnpm --filter @objectstack/objectql exec vitest run --project local --maxWorkers=2,应见 472 个测试文件全部通过。
  • 需要你做的:无,席位落地。

Cut 14 of the packages/objectql/src/engine.ts split (#24586 ruling A, recorded in 6270097840). The claim is 6432376812, which carries the seat's shared-module-helper ruling for this cut. Four bodies of the "Referential integrity and master-detail resolution" section move unedited into a new module, packages/objectql/src/engine-master-detail-related.ts: settleMasterDetailLanding (the UPDATE payload a master-detail FK repoint actually lands with), resolvePreviewParents (the write preview's master-detail headers), resolvePredicateRelated (the related rows a validation-rule predicate reads) and resolveOneHopRelated (the one-hop reference reads it and formula fields go through). No behaviour changes. #25522 stays open for the later cuts.

Branch claude/project-thread-39lx12 (the card thread's harness-pinned branch), cut from origin/main at 5507225e3, which contains cut 13's merge (b7f8a2ebdb, #26403), with main merged in once at 6746140361 (clean; no merged commit touches packages/objectql/**, and engine.ts is the same blob at both bases). Nothing was edited before that merge landed: the move was measured and dry-run on #26403's head while it was in the queue. engine.ts at 5507225e3 is the same blob (4e64ab23e37a) as at #26403's head, and the five files committed here are byte-identical to that dry run (git hash-object, 5 of 5; the move script regenerates both source files byte for byte).

What changes

Why this area

Re-measured on 5507225e3 (after cut 13's merge) with cut 11's measure script, unchanged, and a price step under this cut's seat ruling (price3.mjs: a helper a paying body shares with a staying body is exported in place and blocks nothing; one read only by moving bodies moves with them). Both earlier screens still apply (query-options erasure sites, 8 at base; module-helper closure). engine.ts is 22,423 lines, 7,148 of them from the last 30 days (git blame --since=2026-09-27T03:05Z). scripts/pm/git-history.mjs count: 131 commits in 30 days, 36 in 7. class ObjectQL holds 318 members, 255 methods and accessors.

Seam table on 5507225e3 (41 sections of class ObjectQL; U = untyped receivers)
range section first … last member lines 30-day lines methods inferred public U private statics
4697-4922 Instance state and constructor txStore … constructor 226 50 0 (0 public) - 0 -
4923-4945 Status and registry accessor getStatus … registry 23 0 2 (2 public) getStatus 0 -
4946-5289 Hook and function registry registerHook … triggerHooks 344 6 16 (16 public) registerHook, triggerHooks 1 -
5290-5645 Hook dispatch internals hasHooksFor … assertBulkPerRowHookBudget 356 61 7 (0 public) - 0 -
5646-5806 Action registry and activation registerAction … setActionActive 161 19 10 (10 public) - 1 -
5807-6088 Registered resolvers and write-gate probes _heldFileResolver … registerInsertRecomputeListener 282 116 6 (6 public) - 0 -
6089-6842 Readable-ids security and permission/formula resolution readableIdsSecurityReported … resolveDefaultPermissions 754 546 15 (2 public) - 0 -
6843-6949 Authz invalidation and middleware registration writeEpoch … hasObjectMiddleware 107 18 4 (4 public) - 0 -
6950-7127 Execution plumbing (middleware, session, provenance, users) executeWithMiddleware … buildUser 178 45 7 (0 public) - 0 -
7128-7336 Driver options and engine posture buildDriverOptions … resolveEnginePosture 209 150 6 (0 public) - 0 -
7337-7546 Organization probe and system-insert organization probeInstallOrganizations … transactionCoversDriverFor 210 45 4 (0 public) - 0 -
7547-7912 Field defaults resolveNowDefault … defaultFieldEntries 366 4 6 (0 public) - 0 -
7913-8525 Autonumber applyAutonumbers … seedAutonumber 613 80 6 (0 public) - 1 -
8526-9190 Package, app, plugin and driver registration registrationConversionWarned … registerDriver 665 170 6 (3 public) registerApp, registerDriver 0 -
9191-9399 Datasource registry supplyFileColumnsMovedResolver … listUnavailableDatasources 209 1 8 (7 public) - 3 -
9400-9551 Write guard and realtime publish assertWriteAllowed … publishBulkDataEvent 152 18 4 (1 public) - 0 -
9552-9650 Validation-message i18n setI18nService … objectDisplayLabel 99 14 4 (1 public) - 0 -
9651-10654 Referential integrity and master-detail resolution (this PR) assertReferencesResolve … masterDeclaredFields 1004 415 12 (0 public) - 0 -
10655-10770 Dangling-reference and cascade inspection inspectDanglingReferences … inspectCascadeDelete 116 48 2 (2 public) - 0 -
10771-10865 Crypto and tenancy providers setCryptoProvider … setTenancyPostureProvider 95 20 4 (4 public) - 1 -
10866-11285 Secret and internal-field handling encryptSecretFields … resolveInternalField 420 61 9 (3 public) - 0 -
11286-11354 Schema lookup and filter judging getSchema … judgeFilter 69 48 3 (2 public) - 0 -
11355-11620 Datasource routing LIFECYCLE_DATASOURCE … setDatasourceMapping 266 20 8 (3 public) setDatasourceMapping 0 -
11621-11788 Lifecycle init and health init … resolvePrimaryDatasource 168 16 3 (3 public) init 0 -
11789-11862 Media and value-shape presence objectHasMediaField … valueShapeStrictFor 74 13 4 (0 public) - 0 mediaFieldPresence, coveredValueFieldPresence
11863-12452 Migration flags and attestations isFileReferencesMigrationVerified … wasDatastoreCreatedFromEmpty 590 96 17 (8 public) - 0 -
12453-12475 destroy destroy … destroy 23 0 1 (1 public) destroy 0 -
12476-12494 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
12495-12945 Roll-up summaries autonumberCounters … recomputeSummaries 451 0 7 (1 public) - 0 -
12946-13199 Related-record expansion expandRelatedRecords … expandRelatedRecords 254 10 1 (0 public) - 0 -
13200-13397 File-reference hydration resolveFileReferences … readParentDerivedFiles 198 183 5 (0 public) - 0 -
13398-13876 Query lowering resolveWhereTokens … requireFindOnePredicate 479 272 9 (0 public) - 0 -
13877-14433 find / findOne find … staticReadonlyCreateStrip 557 142 4 (2 public) - 0 -
14434-15091 validate validate … validate 658 447 1 (1 public) - 0 -
15092-16438 insert / insertMany readUpdatePriorRow … insertMany 1347 439 3 (2 public) - 1 -
16439-18372 update update … update 1934 275 1 (1 public) - 0 -
18373-19913 Delete cascade and reference walking planCascadeAtomicity … organizationOwnedRowCounts 1541 733 22 (1 public) - 0 storedReferenceIncludes, remainderAfterMemberRemoval, referenceCheckContext, outlivesOrganization, keepsOrganizationReference, organizationCountContext
19914-20483 delete delete … delete 570 47 1 (1 public) - 0 -
20484-21103 count / aggregate count … aggregate 620 236 3 (2 public) - 0 -
21104-21412 execute and transactions execute … enforceTransactionOrigin 309 74 8 (2 public) - 0 -
21413-21705 Compatibility API registerObject … create 293 52 16 (16 public) - 0 -

The full per-method screen and price list are in the project file pm-orders/server/25522-cut14-seam-table.md; the move script is pm-orders/server/evidence/25522/move-cut14.mjs.

area paying bodies net verdict
validate 1 body (469 lines, 345 from the last 30 days), 7 shared helpers exported in place priced -439; -438 measured on a dry run (suite green) out under the ruling: exporting insertStaticReadonlySubject alone changes the published .d.ts bytes (below)
Referential integrity and master-detail resolution (this PR) 4 bodies (327 lines) + 2 helpers that move with them; 4 shared helpers exported in place priced -292 (bodies only); -338 measured pays most of what the ruling admits; no open PR touches it
Package, app, plugin and driver registration 4 bodies (264 lines; registerMetadataCollections 166) about -233 overlaps #26337: METADATA_ARRAY_KEYS, read only by registerMetadataCollections and so moving with it, gains 'queues', 'assignmentRules' in that PR
Readable-ids security and permission/formula resolution 9 bodies (216 lines) under the ruling; resolveReadableIdsSecurity stays (this as a value), the two ADR-0139 bodies stay about -156 pays less: nine short bodies, nine stubs
Delete cascade and reference walking 7 bodies (167 lines) about -124 pays less; most of the section reads private statics
count / aggregate, Query lowering, the rest - -71 or less pays less

Why validate is out. Measured by exporting each of its seven shared helpers on its own on the base and building (dts-helper-export-variant.sh): six leave all six declaration files byte-identical; insertStaticReadonlySubject, whose return type is ReturnType<typeof staticReadonlyInsertSubject>, makes the declaration bundler pull the rule-validator declarations in earlier. The result is the same declarations in another order (sorted lines equal for index.d.ts and core.d.ts; the chunk differs only in the order of two import specifier lists), but index.d.ts, core.d.ts and the shared chunk change bytes and the chunk's file name changes (util-CVzOGxAn to util-lvlZ9vs9). The seat's ruling for this cut asks for byte-identical, so this cut takes the next area. The validate dry run (move-cut14-validate-dryrun.mjs; 472 files / 8,789 tests green, AST compare SAME) is kept in the project files for whoever decides it; see Acceptance notes.

The shape

  • The class keeps all four methods with name, modifiers, signature, docblock and position. Three bodies become one host literal of exactly the MasterDetailRelatedHost members they read, plus a .call, marked [#25522 cut 14], as cuts 8 and 10 to 13 did. Fields go as getters over a readX closure (logger, _registry, formulaHopReads); methods go as late-bound closures (find, resolveMasterDetailParents, materializeParentHeader, buildReferentialFieldClear, resolveOneHopRelated, resolveEnginePosture, masterDetailHeadersCallerReads), so a spy or double set on the engine is still the one a call reaches. No moved body assigns a field, so there is no setter (cut 13's setter ruling has nothing to apply to).
  • settleMasterDetailLanding reads no member of the class, so its method calls the function directly with its type parameter (return settleMasterDetailLanding<H>(args);), the shape cuts 3 and 16 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 used for a body that reads no this.
  • Helpers, under this cut's seat ruling. withoutKey and staticReadonlyStripTakes are read only by settleMasterDetailLanding, so they moved with it, docblocks included and byte for byte (staticReadonlyStripTakes reads staticReadonlyStoredView). Four helpers are read by these bodies AND by bodies that stay: masterIdOf (resolveMasterDetailParent, resolveMasterDetailParents, update), servedAsStored (validate), undeclaredRelatedFields (resolveFormulaRelated) and staticReadonlyStoredView (update, and the moved staticReadonlyStripTakes). Each stays declared where it was, byte for byte, and gains export in place; the module imports the four from ./engine.js. Before that import the module imported only the type ObjectQL from ./engine.js. index.ts and core.ts name their exports singly, so nothing new is published (below).
  • The run-time cycle (measured). That value import makes engine.ts and the module import each other. The module runs nothing at evaluation (declarations only), and engine.ts imports it right after ./federated-object.js, after every module the moved bodies import, so the evaluation order of every other module is unchanged. Measured on the built bundles: the // src/... region order of index.mjs, core.mjs, index.js and core.js at head equals the base's with one line added, the new module, between federated-object and in-memory-aggregation (100 to 101 regions in index.mjs). The bundles hold both files in one module, so the published build has no cycle. The late-binding probe answers the same on base and head (Tests).
  • Imports. The module imports referenceTargetOf, EngineQueryOptions (type), isStoredMetadataBodyObject, ExecutionContext (type), postureEnforcesWall, resolveTenantFieldName, carriesOrganization, collectPredicateRelationships, referentialClearBinding, readonlyWhenFkJudgementReadsParent, the two binding types, resolveMasterDetailRelation and isFederatedObject. engine.ts drops postureEnforcesWall, collectPredicateRelationships, referentialClearBinding and readonlyWhenFkJudgementReadsParent, whose last readers moved; the last one's own two-line comment moves with its import into the module.
  • The module header names what moved, what stays and why, and how the helpers were handled.

Net lines (the seat's rule)

engine.ts goes from 22,423 to 22,085 lines, net -338 (40 added, 378 removed). The module is 512 lines. With the three path readers below, the diff against origin/main is 949 changed lines over 5 files (+565 / -384).

Readers of the moved lines

  • content/docs/permissions/system-context.mdx row 29b anchored packages/objectql/src/engine.ts#resolveOneHopRelated, the elevation read site that moved; it now anchors engine-master-detail-related.ts#resolveOneHopRelated. check-system-context-census passes at 153 read sites, as at base. No NON_READ_ANCHORS row names a moved member.
  • federated-injected-column-readers.test.ts keys its rows FILE#FUNCTION :: SEAM: the three resolveOneHopRelated rows and the resolvePredicateRelated row are re-keyed to the new file, dispositions and reasons unchanged.
  • packages/core/src/security/second-object-read-exposure.pin.test.ts lists every non-test file that calls referenceTargetOf(: the new module gets a not-a-served-read row. Its reason carries the half of the engine.ts row's also text that described resolvePredicateRelated, which now has one home with the body, plus resolveOneHopRelated, the one-hop loader those reads and the formula fields go through as the caller's own find (as the federated test's row for it says). The engine.ts row keeps its executes classification for expandRelatedRecords, which stays, and the sentence on the engine's privileged callers.
  • No objectql/src/engine.ts#NAME pointer under packages/spec/**, docs/adr/**, docs/qa/**, content/** or scripts/** names a moved member or helper. No ADR anchor paragraph describes a moved body (the engine.ts entry is the transaction-seam paragraph). No moved body comment cites an ADR decision letter. No moved body holds a query-options/no-any-erasure site (8 at base and head), a tenant-audit untyped receiver, a private static read, or a .canReadObject( call.
  • 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 names two moved bodies in read-seam entries (guards find() in resolvePreviewParents() and in resolveOneHopRelated()). Its --list at head names the same 82 seams with those two now under the new file, and nothing else differs once line numbers are masked; the gate passes.
  • Decision-letter count: check-adr-anchors at base 5507225e3 (clean detached worktree): 91 anchored files, 1,877 decision-letter citations, 59,295 resolving citations across 8,376 files. At main 6746140361: 1,876 (main's own movement since). At head 85d27dca7 (that main merged in): 1,876, the same. On the pre-merge pair it was 1,877 and 1,877. The count is unchanged because the moved bodies carry no decision-letter citation.

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. This cut takes the section that pays most under this cut's rulings and that no open PR touches (table above). Nothing about what the engine does changes.

Tests

No test is added or changed apart from the re-keyed rows above: the move is proved by identity, not by new behaviour.

Public .d.ts byte-identical, with a positive control

Built @objectstack/objectql at the base and at head, each from a clean tree, twice: on the pre-merge pair (base 5507225e3, head e77aac466) and again after merging main (base 6746140361, head 85d27dca7), with the same hashes both times, and hashed every declaration file (dts-cut14.sh). All six are byte-identical:

file sha256 (base = head)
core.d.mts ef178bf0725eccc2f358f082ce13dc5122a6d5a53c19ef630017256f67de03b4
core.d.ts 1d20303a455a909e4b55fd0802fb0b0bcb59ec51784daf27d5ef4c0659410953
index.d.mts ccf900240aa137abe72f82b8abfc872d42c5645467400134c580285f2a3b3090
index.d.ts 0454d5e70b156d8effbf27b8915587c7cbee241dcd7d698013c0a4c369a02aa5
util-CVzOGxAn.d.mts 85ef1cbec2b016d5f7afabaa21689c86c6dffc573076fdfdeb8ea0f1a1b1ad53
util-CVzOGxAn.d.ts 85ef1cbec2b016d5f7afabaa21689c86c6dffc573076fdfdeb8ea0f1a1b1ad53

The builds are the moved code, not a stale cache: resolvePreviewParents.call(host occurs 0 times in the base dist and once in the head dist.

Positive control. scripts/pm/ablation-replace.mjs --hold dropped private from private async resolvePreviewParents( on the head (anchor count 1 to 0; engine.ts blob 3039d4e812dc to 0cbfb05d94c8). Rebuilt, every declaration hash changed (core.d.mts 051b65f5…, core.d.ts 358f0ddf…, index.d.mts 43c1a222…, index.d.ts f0ef609c…) and the chunk was renamed util-m88nBnDJ.d.ts, which now publishes resolvePreviewParents(...) (line 7673). So the comparison sees a one-word change to a moved member's signature. --restore put the blob back to 3039d4e812dc (git diff HEAD empty), and a rebuild matched the head build again.

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:

  • Moved bodies, all four SAME: settleMasterDetailLanding 204 tokens / 22 lines, resolvePreviewParents 607 / 51, resolvePredicateRelated 463 / 70, resolveOneHopRelated 1,158 / 184.
  • class ObjectQL: 318 of 318 members kept, in the same order; 314 byte-identical, and the 4 stubs keep docblock, modifiers and signature identical.
  • Top level: 220 of 230 base statements byte-identical. The other 10 are the 3 import edits, the 4 helpers that gained export (byte-identical after it), the 2 helpers that moved (byte-identical in the module: withoutKey 526 characters, staticReadonlyStripTakes 1,464) and the class. The head adds one statement, the module import.
Late-binding probe (base and head dist)

zz-probe-cut14.mjs (a one-off, not committed) loads the built ObjectQL and drives all four methods with doubles set on the instance for every member the host reads (find, resolveMasterDetailParents, materializeParentHeader, buildReferentialFieldClear, resolveOneHopRelated, masterDetailHeadersCallerReads, _registry, formulaHopReads, logger), then prints results, the call order on the doubles and the log lines. Cases: the repoint and system arms of settleMasterDetailLanding; resolvePreviewParents for a caller, for system, and with a throwing find (the warn arm); resolvePredicateRelated for a caller, for system, and under the referential-clear binding; resolveOneHopRelated through the predicate path, the caller path, and with a formulaHopReads double. The 33 output lines are identical on base and head, which is the check the seat's ruling asks for on the run-time cycle. The module-order measurement on the bundles is under "The run-time cycle" above.

Suites

Under scripts/pm/os-verify-lock.sh on head 85d27dca7 (again after merging main; the pre-merge run on e77aac466 was green too):

  • ^... closure build of @objectstack/objectql, plus @objectstack/rest^..., @objectstack/runtime^..., @objectstack/client... and @objectstack/client-react...: exit 0.
  • typecheck of @objectstack/objectql and @objectstack/core: exit 0 each.
  • @objectstack/objectql vitest --project local: 472 files, 8,789 tests passed, exit 0.
  • 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,293, types 1 / 11. Exit 0 each.

Verification

Head 85d27dca7b0789e71c1d8074bd985bbdb0646ee0. The gate list is node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack on this diff at that head (89 commands, no STALE warning), each run with its exit code captured before any pipe, then reconciled with --ran.

  • Derived gates: 89 of 89 exit 0. --ran reconciliation: 89 derived, 89 run, 0 NOT-MEASURED, 0 UNRUN. That includes check:skill-examples (265 prose examples type-check) and check:dual-build-cjs-loads (112 entry points across 69 packages load), which refuse without a build and so ran after the client closure build; and check-engine-split-ratio --days 90 and check-plugin-teardown-shape --self-test, which refuse on a shallow clone and so ran after git fetch --shallow-since=2026-07-22 origin main and git fetch origin 621a487607881c66b2899b7e3477115229a156b4.
  • 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-durability-degradation-log-level.mjs --list, and eslint as pnpm lint runs it (--no-inline-config) on packages/objectql and the core pin test.
  • A first full pass on e77aac466, before main moved, derived 95 commands: 93 exit 0 and the two build-reading gates refused (exit 3, not measured). The re-run above replaces it.

Acceptance notes

  • Serial order. Nothing was edited until origin/main held cut 13's merge (b7f8a2ebdb). The branch is cut from 5507225e3, which contains it.
  • Overlap. Open PRs touching packages/objectql/** or the three re-keyed files, listed just before opening this: feat!: ownership queues and assignment rules (ADR-0142 phase 2) #26337 and feat(spec,cli)!: retire the ≤16.x retirement kits; the strict unknown-key refusal names the protocol upgrade guide #26138 (validation/rule-validator.test.ts only, not touched here). feat!: ownership queues and assignment rules (ADR-0142 phase 2) #26337 edits engine.ts in the imports, OperationContext, METADATA_ARRAY_KEYS, after SECURITY_SERVICE_SOURCE_SLOT, and in readUpdatePriorRow / insert / update, none of them moved here, and it edits a different row of federated-injected-column-readers.test.ts. git merge-tree of this head with feat!: ownership queues and assignment rules (ADR-0142 phase 2) #26337's current head (290a435afc) exits 0, no conflict. Whichever lands second merges main.
  • validate is left for a decision, not taken. It pays the most (-438) but needs insertStaticReadonlySubject exported, which reorders the published declarations (Why this area). Two ways a later cut could take it, neither tried here: a ruling that accepts a declaration reorder with sorted-line equality as the identity bar, or a ruling that lets a helper whose return type names another module's symbol move with the body under a different rule. The dry run is in the project files.
  • Receivers. The host literal for resolveOneHopRelated carries formulaHopReads as a getter, so the body reads whatever set the instance holds when it reads it: the probe replaces it on the instance after construction and sees the moved body call add on the replacement. No body assigns a field, so there is no setter.
  • Left as is. staticReadonlyStoredView's docblock in engine.ts still says {@link staticReadonlyStripTakes}, which now lives in the module. Editing the docblock would break byte identity of a staying statement, so it is noted here rather than touched.
  • Core pin row. The new not-a-served-read row is a re-key: its reason is the sentence the engine.ts row already carried for resolvePredicateRelated, plus the existing classification of resolveOneHopRelated from the federated readers test. No classification is new.
  • Next cut. The split lines pause after this cut. When they restart, the same price step names Package, app, plugin and driver registration (about -233, once feat!: ownership queues and assignment rules (ADR-0142 phase 2) #26337 lands) first.

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 (MasterDetailRelatedHost, engine-master-detail-related.ts).
  • 3. No formatter ran; pnpm lint passes on the touched files.
  • 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 the published declarations 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_01FEkQ5oZ6me4dwmHNpWg6kT


Generated by Claude Code

…out of engine.ts into engine-master-detail-related.ts

Four bodies of the "Referential integrity and master-detail resolution"
section move unedited into a new module behind MasterDetailRelatedHost:
settleMasterDetailLanding, resolvePreviewParents, resolvePredicateRelated
and resolveOneHopRelated. Each method stays on the class with its
signature and docblock and hands its body the members it reads.

withoutKey and staticReadonlyStripTakes, read only by the moved landing
body, move with it. masterIdOf, servedAsStored, undeclaredRelatedFields
and staticReadonlyStoredView, shared with bodies that stay, gain export
in place and are imported back. Path readers re-keyed: the
system-context census row 29b, the federated injected-column reader
keys, and core's second-object-read CALLERS row.

Claude-Session: https://claude.ai/code/session_01FEkQ5oZ6me4dwmHNpWg6kT
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Oct 27, 2026
@github-actions github-actions Bot added size/l documentation Improvements or additions to documentation tests labels Oct 27, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

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

  • content/docs/permissions/system-context.mdx (via masterDetailHeadersCallerReads (symbol, a field of interface MasterDetailRelatedHost), resolveOneHopRelated (symbol, a field of interface MasterDetailRelatedHost; a method of class ObjectQL; a top-level function), masterDetailHeadersCallerReads (literal, a string literal in masterDetailHeadersCallerReads; a string literal in resolveOneHopRelated), resolveOneHopRelated (literal, a string literal in resolveOneHopRelated; a string literal in resolvePredicateRelated))
What this run could not see
  • 1 anchor(s) matched too much of the corpus to be a work list: master_detail (literal, 35 pages)
  • 2 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 d0a7e748a933a7fb549c38c3105c1c49f5935dec → packageMentionDocs.

Which tree this was computed on

This run read content/docs from c48b0de9994ac153888a0da933ef1bc810840582 — the merge of head 85d27dca7b0789e71c1d8074bd985bbdb0646ee0 into base d0a7e748a933a7fb549c38c3105c1c49f5935dec, 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 c48b0de9994ac153888a0da933ef1bc810840582 && git checkout c48b0de9994ac153888a0da933ef1bc810840582
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin d0a7e748a933a7fb549c38c3105c1c49f5935dec 85d27dca7b0789e71c1d8074bd985bbdb0646ee0 && git checkout -B drift-repro d0a7e748a933a7fb549c38c3105c1c49f5935dec && git merge --no-ff 85d27dca7b0789e71c1d8074bd985bbdb0646ee0

node scripts/docs-audit/affected-docs.mjs --json d0a7e748a933a7fb549c38c3105c1c49f5935dec

⚠️ 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 d0a7e748a933a7fb549c38c3105c1c49f5935dec → 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-27T05:29Z · head 85d27dca7b · #25522 cut 14

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

  1. Shape: a draft against main. The body opens with Part of #25522 (not Fixes). Clause-②: no and Metadata-protocol: none are line-initial. skip-changeset applies because this is a move. check-governed-merges --pr 26489 reads NOT governed, 949 changed lines. None of the four contract faces is touched, so no at-tier review is owed. This is the last cut before the pause (「拆分线已经做了一半的继续完成,然后暂停,等新版本发布后继续」, 2026-10-27T04:17Z): it was in progress when that line was written.
  2. Premise: the claim 6432376812 asked for a seam table re-measured after cut 13, and it is in the body. "Referential integrity and master-detail resolution" lands at −338 (22,423 → 22,085). validate priced −438 but was excluded on a measurement: exporting insertStaticReadonlySubject reorders the published .d.ts chunk, which the byte-identical ruling forbids. Four bodies move into engine-master-detail-related.ts behind MasterDetailRelatedHost: settleMasterDetailLanding, resolvePreviewParents, resolvePredicateRelated and resolveOneHopRelated. No moved body assigns a field, so the host has no setter.
  3. Move check (the seat's own): every line removed from engine.ts was compared with the new module after stripping indentation. All 370 non-blank lines are present there except six:
    • Two import lines lose the names that only the moved bodies read; the module imports those names itself.
    • Four helpers are shared with staying code and gain export in place under the claim's shared-helper ruling: masterIdOf, servedAsStored, undeclaredRelatedFields and staticReadonlyStoredView. The module imports them back from ./engine.js. That is a value import and a run-time cycle, so the ruling's measurement applies:
      • The engine imports the module after every module the moved bodies import.
      • The late-binding probe reads the same 33 lines on base and head dist.
      • The bundle's source-region order is unchanged except for the one added module.
    • The two helpers only the moved bodies read (withoutKey, staticReadonlyStripTakes) move with them.
    • The six published .d.ts files are byte-identical on both base/head pairs, with a positive control.
    • The AST comparison reads 4 of 4 bodies the same and 318 of 318 class members accounted for.
  4. Path readers, migrated in the same PR: re-keyings of existing rows; no gate rule changes.
    • system-context.mdx row 29b now names engine-master-detail-related.ts#resolveOneHopRelated.
    • Four keys in federated-injected-column-readers.test.ts move with their bodies.
    • core's second-object-read-exposure.pin.test.ts gives the new module its own not-a-served-read row and drops the moved sentence from engine.ts's row. That census is the trap cut 10 met.
  5. Gates: at this stamp every check-run on the head reads success or skipped (path-filtered UI, docs build, dogfood matrix placeholder, opt-in harnesses, skipped changeset check). None reads failed or in progress; Test Core 1–8, Lint & Repo Gates and TypeScript Type Check are green. Locally, on the merged head, the dev reports:
  6. Out of scope: none filed.
    • staticReadonlyStoredView's docblock still links staticReadonlyStripTakes, which now lives in the module. It is kept so the staying statement stays byte-identical.
    • validate stays available for a later cut, but only under a ruling on the .d.ts reorder. That question goes with the restart, not now.
  7. Landing: this seat runs pr_ready and then automerge_enable through the relay, confirms MERGED on both readings, and records the landing on [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. The same act moves the card to pm:on-hold with a Restart-when: naming the next release.

Generated by Claude Code

Merged via the queue into main with commit 00874b9 Oct 27, 2026
56 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-39lx12 branch October 27, 2026 05:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/l skip-changeset PR has no user-facing published change; bypasses the changeset gate tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants