Skip to content

refactor(objectql): move the validate body out of engine.ts into engine-validate.ts (#25522 cut 16) - #26792

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

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/project-thread-cjdmc4

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

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

维护者速读

  • 改了什么:把 engine.ts 里写入预览 validate(不落库、按写入路径自己的规则给出每行判定)的方法体原样搬进新文件 engine-validate.ts,类上的方法签名与注释不变,只把方法体换成转交;方法体与 insert / update 共用的 6 个工具函数原地加 export、由新文件导入(不复制)。顺带把上一刀遗留的两处 {@link READABLE_IDS_MAX_CHAIN_DEPTH} 用一个仅类型导入接回(注释文字一字不改)。engine.ts 22,189 → 21,733 行。fix(objectql)!: a child rule reading a parent summary counts the earlier admitted rows of the same bulk create #26680 与 fix(core)!: the import dry run previews create rows in the commit's batches (#26684) #26778(都改了 validate 方法体)已先合入,各自的三处改动随方法体一起搬进新文件。
  • 为什么改:[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 的第 16 刀。validate 是缝表上收益最大的一块(约 −439),之前被「公开 .d.ts 逐字节不变」挡住;新裁决 6441032442(B)允许仅声明顺序与分块文件名变化,本刀按该裁决切出。
  • 风险与回滚:纯搬移,无行为变化;公开 .d.ts 六个文件在裁决规定的归一化后完全相同(声明集、导出集、声明内容逐字节相同,只是分块里声明的先后与分块名变了);含正控。新文件与 engine.ts 互相导入,构建产物里模块顺序只多出新文件一行;一次性探针在新旧两版构建产物上逐行对照(69 行完全一致)。回滚 = revert 本 PR。
  • 先看:packages/objectql/src/engine-validate.ts(搬入的方法体与 ValidateHost)、packages/objectql/src/engine.ts 里 validate 的转交桩与 6 处原地 export、packages/objectql/src/validate-preview-parent.test.ts(对齐检查改为从新文件读预览调用)。
  • 两分钟验证:pnpm --filter @objectstack/objectql exec vitest run --project local --maxWorkers=2 src/validate-preview-parent.test.ts,应见该文件全部通过(其中 parity 一组从 engine-validate.ts 找到唯一的预览调用)。
  • 需要你做的:无,席位落地。

Cut 16 of the packages/objectql/src/engine.ts split (#24586 ruling A, recorded in 6270097840), under the claim 6441744054. The body of ObjectQL#validate, the write preview (the write path's own verdict over candidate rows, persisting nothing), moves unedited into a new module, packages/objectql/src/engine-validate.ts, behind ValidateHost. This is the first cut under the .d.ts declaration-reorder ruling 6441032442 (letter B): the published declaration chunk is reordered and renamed, and every declaration file is EQUAL under that ruling's normaliser (report block below, with its positive control). No behaviour changes. #25522 stays open for the later cuts.

Branch claude/project-thread-cjdmc4 (the card thread's harness-pinned branch), cut from origin/main at fe53769a8e, which contains cut 15's merge (378c940b16, #26645), with origin/main merged in at 1685a06dd9 (after #26680) and at ef7067fdaf (after #26778).

What changes

Why this area

Re-measured on fe53769a8e with cut 11's measure script and cut 14's price step (price3.mjs), both unchanged. engine.ts is 21,947 lines, 6,830 of them from the last 30 days (git blame --since=2026-09-27T12:20Z, clone deepened to 2026-09-21 first); class ObjectQL holds 318 members, 255 methods and accessors. Query-options erasure sites: 8 at base (lines 8286, 9595, 9841, 9905, 10237, 13517, 13816, 18736), none in validate's range.

Seam table on fe53769a8e (41 sections of class ObjectQL; U = untyped receivers)
range section first … last member lines 30-day lines methods inferred public U private statics
4643-4868 Instance state and constructor txStore … constructor 226 50 0 (0 public) - 0 -
4869-4891 Status and registry accessor getStatus … registry 23 0 2 (2 public) getStatus 0 -
4892-5235 Hook and function registry registerHook … triggerHooks 344 6 16 (16 public) registerHook, triggerHooks 1 -
5236-5591 Hook dispatch internals hasHooksFor … assertBulkPerRowHookBudget 356 61 7 (0 public) - 0 -
5592-5754 Action registry and activation registerAction … setActionActive 163 24 10 (10 public) - 1 -
5755-6036 Registered resolvers and write-gate probes _heldFileResolver … registerInsertRecomputeListener 282 116 6 (6 public) - 0 -
6037-6661 Readable-ids security and permission/formula resolution readableIdsSecurityReported … resolveDefaultPermissions 625 492 15 (2 public) - 0 -
6662-6768 Authz invalidation and middleware registration writeEpoch … hasObjectMiddleware 107 18 4 (4 public) - 0 -
6769-6946 Execution plumbing (middleware, session, provenance, users) executeWithMiddleware … buildUser 178 45 7 (0 public) - 0 -
6947-7155 Driver options and engine posture buildDriverOptions … resolveEnginePosture 209 150 6 (0 public) - 0 -
7156-7365 Organization probe and system-insert organization probeInstallOrganizations … transactionCoversDriverFor 210 45 4 (0 public) - 0 -
7366-7731 Field defaults resolveNowDefault … defaultFieldEntries 366 4 6 (0 public) - 0 -
7732-8344 Autonumber applyAutonumbers … seedAutonumber 613 80 6 (0 public) - 1 -
8345-9009 Package, app, plugin and driver registration registrationConversionWarned … registerDriver 665 170 6 (3 public) registerApp, registerDriver 0 -
9010-9218 Datasource registry supplyFileColumnsMovedResolver … listUnavailableDatasources 209 1 8 (7 public) - 3 -
9219-9370 Write guard and realtime publish assertWriteAllowed … publishBulkDataEvent 152 18 4 (1 public) - 0 -
9371-9469 Validation-message i18n setI18nService … objectDisplayLabel 99 14 4 (1 public) - 0 -
9470-10178 Referential integrity and master-detail resolution assertReferencesResolve … masterDeclaredFields 709 308 12 (0 public) - 0 -
10179-10294 Dangling-reference and cascade inspection inspectDanglingReferences … inspectCascadeDelete 116 48 2 (2 public) - 0 -
10295-10389 Crypto and tenancy providers setCryptoProvider … setTenancyPostureProvider 95 20 4 (4 public) - 1 -
10390-10809 Secret and internal-field handling encryptSecretFields … resolveInternalField 420 61 9 (3 public) - 0 -
10810-10878 Schema lookup and filter judging getSchema … judgeFilter 69 25 3 (2 public) - 0 -
10879-11144 Datasource routing LIFECYCLE_DATASOURCE … setDatasourceMapping 266 20 8 (3 public) setDatasourceMapping 0 -
11145-11312 Lifecycle init and health init … resolvePrimaryDatasource 168 16 3 (3 public) init 0 -
11313-11386 Media and value-shape presence objectHasMediaField … valueShapeStrictFor 74 13 4 (0 public) - 0 mediaFieldPresence, coveredValueFieldPresence
11387-11976 Migration flags and attestations isFileReferencesMigrationVerified … wasDatastoreCreatedFromEmpty 590 96 17 (8 public) - 0 -
11977-11999 destroy destroy … destroy 23 0 1 (1 public) destroy 0 -
12000-12018 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
12019-12469 Roll-up summaries autonumberCounters … recomputeSummaries 451 0 7 (1 public) - 0 -
12470-12723 Related-record expansion expandRelatedRecords … expandRelatedRecords 254 10 1 (0 public) - 0 -
12724-12921 File-reference hydration resolveFileReferences … readParentDerivedFiles 198 183 5 (0 public) - 0 -
12922-13400 Query lowering resolveWhereTokens … requireFindOnePredicate 479 271 9 (0 public) - 0 -
13401-13957 find / findOne find … staticReadonlyCreateStrip 557 142 4 (2 public) - 0 -
13958-14615 validate validate … validate 658 447 1 (1 public) - 0 -
14616-15962 insert / insertMany readUpdatePriorRow … insertMany 1347 439 3 (2 public) - 1 -
15963-17896 update update … update 1934 275 1 (1 public) - 0 -
17897-19437 Delete cascade and reference walking planCascadeAtomicity … organizationOwnedRowCounts 1541 733 22 (1 public) - 0 storedReferenceIncludes, remainderAfterMemberRemoval, referenceCheckContext, outlivesOrganization, keepsOrganizationReference, organizationCountContext
19438-20007 delete delete … delete 570 47 1 (1 public) - 0 -
20008-20627 count / aggregate count … aggregate 620 192 3 (2 public) - 0 -
20628-20936 execute and transactions execute … enforceTransactionOrigin 309 74 8 (2 public) - 0 -
20937-21229 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-cut16-seam-table.md.

price (net lines) section taken?
-439 priced, -456 landed validate yes (one body: 469 lines at fe53769a8e, 345 of them from the last 30 days; 492 once #26680's and #26778's lines arrived)
-233 Package, app, plugin and driver registration no: overlaps open #26337 (METADATA_ARRAY_KEYS and its check-stack-collection-maps.mjs row), as the order says
-124 Delete cascade and reference walking pays less
-71 or less count / aggregate, Query lowering, the rest pays less
-27 resolveFormulaRelated (Readable-ids security and permission/formula resolution) no: another section, so not one area with validate; a later cut

The shape

  • The class keeps validate with name, modifiers, signature, docblock and position. Its body becomes one host literal of exactly the ValidateHost members it reads, plus a .call, marked [#25522 cut 16], as cuts 8 and 10 to 15 did. Fields go as getters over a readX closure (_registry, _writeWallVerdict, logger, _writeGateProbe); methods go as late-bound closures (resolveObjectName, permissionResolution, resolveDefaultPermissions, initializeSummaryFields, applyFieldDefaults, staticReadonlyCreateStrip, mediaValueShapeStrictFor, valueShapeStrictFor, validationMessageContext, buildEvalUser, findOne, readUpdatePriorRow, resolvePreviewParents, resolvePredicateRelated, resolveOptionPermissions, and the two the body gained while this cut was in flight: getSummaryDescriptors from fix(objectql)!: a child rule reading a parent summary counts the earlier admitted rows of the same bulk create #26680 and assertReferencesResolve from fix(core)!: the import dry run previews create rows in the commit's batches (#26684) #26778), so a spy or double set on the engine is still the one a call reaches. The body assigns no field, so there is no setter (cut 13's setter ruling has nothing to apply to). validate is public with a declared return type (Promise of ValidateDataResponse), so the inferred-return series limit does not hold it; it has no this.FIELD.delete/insert/update( site, no private-static read and no self-call to insert / update / delete.
  • Helpers, under the cut-14 seat ruling. The body reads seven top-level helpers, each also read by bodies that stay: undeclaredWriteFieldErrors and stripComputedWriteFields (insert, update), droppedFieldEvents (insert), insertStaticReadonlySubject (insert, withholdInsertReadonlyFromHooks), updateStaticReadonlySubject (update, staticReadonlyStoredView), shouldSkipStateMachine (insert, update) and servedAsStored (already exported for engine-master-detail-related.ts). The six that were not exported gain export in place, byte for byte otherwise (leading docblocks included); none moves and none is copied. The module imports the seven as values and ObjectQL and WriteWallRowVerdict as types from ./engine.js. It is a new module, so before that it imported nothing from ./engine.js. index.ts and core.ts name their exports singly, so nothing new is published. Exporting insertStaticReadonlySubject is the export that reorders the published chunk (below); that is what ruling 6441032442 now allows.
  • The run-time cycle (measured). The 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 ./engine-update-dispatch.js, which is after every module the body imports (validation/record-validator, validation/rule-validator, engine-update-dispatch, the @objectstack/spec subpaths), so the evaluation order of every other module is unchanged. Measured on the built bundles (index.mjs, core.mjs, index.js, core.js): with the // src/engine.ts region markers set aside (9 at base and at head), the // src/... region order at head equals the base's with one line added, // src/engine-validate.ts, between engine-update-dispatch and integrity/dangling-reference-audit. 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 RAW_FILE_VALUES_CONTEXT_KEY, EngineQueryOptions (type), ExecutionContext (type), ValidateDataIssue and ValidateDataResponse (types), WriteObservabilityOptions (type), validateRecordInScope, normalizeBlankTypedValues, normalizeNumericStringValues, ValidationError, valueShapeStrictEffective, mediaStrictEffective, evaluateValidationRules, hasParentScopedRequiredWhen, stripReadonlyFields, stripRuntimeOwnedFields, createBatchSummaryAccumulator (fix(objectql)!: a child rule reading a parent summary counts the earlier admitted rows of the same bulk create #26680's, which engine.ts keeps for insert) and resolveEngineUpdateDispatch with EngineUpdateDispatchData (type). engine.ts drops the four whose last reader moved: ValidateDataIssue, RAW_FILE_VALUES_CONTEXT_KEY, valueShapeStrictEffective and mediaStrictEffective (each from its existing import line; no line is removed).
  • Fold-in: the two stale {@link READABLE_IDS_MAX_CHAIN_DEPTH}. The docblocks of composeReadableIdsAtBoundary and composeReadableIdsChain stay byte for byte; the link now resolves through a type-only import, import type { READABLE_IDS_MAX_CHAIN_DEPTH } from './engine-readable-ids-permission-formula.js' (a type-only import is erased from the JavaScript; TypeScript counts a {@link} as a use), and the constant gains export in place in that module (one home, unchanged value). Measured first the other way, and dropped: re-wording the two docblocks to name the module changed the published chunk, because those PRIVATE members' docblocks are printed in the .d.ts (private composeReadableIdsAtBoundary; under its docblock). That is a change inside the declare class ObjectQL statement, outside ruling 6441032442's bar, so it was not kept. With the type-only import the head .d.ts is byte-identical to the ruling's measured variant (below).
  • The module header names what moved, how the body reaches the class, and how the helpers were handled.

Net lines (the seat's rule)

engine.ts goes from 22,189 to 21,733 lines, net -456 (44 added, 500 removed). The module is 607 lines. The diff against origin/main is 1,187 changed lines over 5 files (+671 / -516).

Readers of the moved lines

  • content/docs/permissions/system-context.mdx site register. The body's one isSystem read (the caller-write strips' gate) moves with it, so the generated register's anchor packages/objectql/src/engine.ts#validate (1 read, row 21b) becomes packages/objectql/src/engine-validate.ts#validate, rebuilt by node scripts/check-system-context-census.mjs --write (after git add -N of the module), never by hand. Row 21b's Symbol cell names validate by symbol, so it stays. scripts/check-system-context-census.mjs's NON_READ_ANCHORS has no validate row.
  • packages/objectql/src/validate-preview-parent.test.ts (f), the preview/write parity pin. It read engine.ts and attributed each evaluateValidationRules call to its enclosing class method, the one in validate being the preview. The preview's call now lives in the module's top-level function validate, so the reader reads both files and attributes a call to its enclosing method OR function, and names the file in its messages. The writes still come from engine.ts. It still finds exactly one preview call (toHaveLength(1)), so it cannot pass empty.
  • No other census, ADR anchor, docs row or test key names validate by engine.ts path: objectql/src/engine.ts#NAME pointers under packages/spec/**, docs/**, content/** and scripts/** name none of the moved names; federated-injected-column-readers.test.ts keys none; the body calls no canReadObject( (core's row-serving-door-exposure.pin.test.ts is unchanged) and no referenceTargetOf( (core's second-object-read-exposure.pin.test.ts is unchanged); it holds no query-options/no-any-erasure site (8 at base and head), no tenant-audit untyped receiver and no private-static read.
  • No ADR anchor paragraph describes the 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 is identical once line numbers are masked: 48 durability seams and 88 read seams (at base ef7067fdaf), the same entries; validate is named in none.
  • Decision-letter count: check-adr-anchors at base ef7067fdaf (clean detached worktree): 1,898 decision-letter citations on the anchored surface, 59,978 resolving citations across 8,400 files. At head: 1,898, 59,978 across 8,401 (the +1 file is the new module). The count held at the earlier bases too (fe53769a8e and 1685a06dd9: 1,871 = 1,871). The moved body cites ADR-0104 and ADR-0113 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. validate is the area that pays most (the seam table) and the one ruling 6441032442 freed; #26680 and #26778, which edited its body while this cut was in flight, have landed and are carried here (Acceptance notes). Nothing about what the engine does changes.

Tests

No test is added. One test's source reader is migrated with the body (above): the move is proved by identity, not by new behaviour.

Public .d.ts: the declaration-reorder bar of ruling 6441032442 item 1, with a positive control

Built @objectstack/objectql at the base (ef7067fdaf, origin/main after #26778: engine.ts blob 8116d440cd7e, engine-readable-ids-permission-formula.ts blob 099ea596128b, no engine-validate.ts) and at head (engine.ts blob fa37297312da), each from a clean tree (dts-cut16.sh), after a full turbo run build (116 of 116 tasks). The same bar held at the two earlier bases (fe53769a8e and 1685a06dd9, the latter with byte-identical declarations to the former). The builds are the moved code, not a stale cache: validate.call(host occurs 0 times in the base dist/index.mjs and once in the head's.

Normaliser: pm-orders/server/evidence/25522/dtsnorm.mjs, sha256 25226e0677bdad24726e9eff76a7e247bf7319440d2430d28cbaab0db6add94b (the ruling's), run as node dtsnorm.mjs --diff BASE_FILE HEAD_FILE on the two dist/ directories. 6348891160's union-order normaliser has nothing to apply to: the statement-level one closes every diff on its own.

file (base → head) raw sha256 base raw sha256 head raw-diff lines statements base / head normalised sha256 (base = head) declared-surface digest (base = head) import / export specifier sets
core.d.ts 01b7e638addebd16… 642b5a3662c27aabde2dae4bb5877f515cb89f4c1e8075f21c0598b7e6025206 12 14 / 14 40e2fde86719d3ccd1cf492314c426c977ab836a5ab54580800cd1f41352247a 4450aaf478092516… (1 decl) EQUAL 0 / 84, EQUAL
core.d.mts 66eaf00a556923e4… 07f5718a09a2662c2f94bc3d79a8e74b67310efa82071a5d81ed1b716adaa69c 12 14 / 14 171eab8743fd8dfabc014a162c40c3e034be29f262b26a2bcc24cb3e06be0459 4450aaf478092516… EQUAL 0 / 84, EQUAL
index.d.ts f16604a77d499f56… 6dd970eb9c7dc4364e2fe99d22460672edc270302287f812231d823090491122 8 122 / 122 2bb6a21a929b41c8dbb85c72c600f87ed3a3af12ca731d1cace61f5d4aa3d57e 9136189ce4d0aba3… (103 decls) EQUAL 18 / 284, EQUAL
index.d.mts e6a8dff9693367bd… 37153868b48bef0e9ad655d74b549a30f51510977cb704e0d6624db1d8e83781 8 122 / 122 c7d7bab765f3b0fb35241fa03bd0b716a93139c98e5638d209379dad96528f98 9136189ce4d0aba3… EQUAL 18 / 284, EQUAL
util-BcFTQcXa.d.ts → util-DdsJlVTF.d.ts d8cd099b508be5ea… 856d98e0e45ced3ec15bf72a1ac09420a2c4f73b5165ab598c1d7d0000705f02 1,144 185 / 185 a4372dd015163a46e90b82473d806423a5ca982b8bef21e6e8bfed2b0655b9dd 71a464127e0b90b1… (173 decls) EQUAL 55 / 147, EQUAL
util-BcFTQcXa.d.mts → util-DdsJlVTF.d.mts d8cd099b508be5ea… 856d98e0e45ced3ec15bf72a1ac09420a2c4f73b5165ab598c1d7d0000705f02 1,144 185 / 185 a4372dd015163a46e90b82473d806423a5ca982b8bef21e6e8bfed2b0655b9dd 71a464127e0b90b1… EQUAL 55 / 147, EQUAL
  • (a) Every file: statements only in base: 0; only in head: 0, declared-surface digest EQUAL.
  • (b) Every file's normalised sha256 is EQUAL base to head.
  • (c) The import and export specifier sets of every declaration file are 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) The raw diff, by its shape: in the four entries, the chunk name in the first import/export line and the side-effect import '@objectstack/spec/…' lines in another order (12 and 8 lines); in the chunk, the rule-validator declaration block printed earlier (hunks 3598a3599,4164 and 11504,12069d12069), the external import statements in another order and two specifier lists in another order. The chunk is renamed util-BcFTQcXa → util-DdsJlVTF. Nothing else differs; the normalised files are identical.
  • The same reorder as the ruling measured. The chunk diff has the shape of the export function insertStaticReadonlySubject alone variant measured for 6441032442 (pm-orders/server/evidence/25522/dts-reorder-ruling-measure.md): the same rule-validator block printed earlier, the same line counts. At the earlier base 1685a06dd9 the head was that variant byte for byte; main has since added declarations of its own, so the raw hashes are new.
  • (e) After the head build, the dependents' closure build (@objectstack/rest, @objectstack/runtime, @objectstack/client, @objectstack/client-react and @objectstack/core with their dependencies, whose DTS builds type-check against it) and pnpm --filter @objectstack/core typecheck ran on the head dist/: exit 0 each.
  • Premises re-checked at this head: (P1) above; (P2) packages/objectql/package.json exports names ./dist/index.* and ./dist/core.* only, driver-sql's ./dist/index.* only, so no chunk is a published path; (P3) git grep -n -E 'objectql/dist|driver-sql/dist' -- scripts package.json: 0 hits.

Positive control. One sed dropped private from private async readUpdatePriorRow( on the head (a member the moved body reads through the host; anchor count 1 to 0, replacement count 0 to 1; engine.ts blob fa37297312da to 843d3085ea88) and rebuilt (posctl16.sh, build exit 0): the chunk was renamed util-BKP1J1qR, which now publishes readUpdatePriorRow(object: string, id: unknown, context: ExecutionContext | undefined, base?: unknown): Promise… (line 10556); its normalised sha256 is d84c4ef94b1e5c90f96d9e23107fe38c3378bdcf7adb9f341f6357f8108435a8 ≠ a4372dd0…, and its declared-surface digest e4dc581b5cbaa0f8… ≠ 71a464127e0b90b1… (the declare class ObjectQL statement named as the one changed). 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 fa37297312da (git diff HEAD empty), and the head was rebuilt before every later step.

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 moved body is SAME against base ef7067fdaf (parameters, return type, async, body tokens, and every body line equal after one level of indent): validate 2,238 tokens / 492 lines, fix(objectql)!: a child rule reading a parent summary counts the earlier admitted rows of the same bulk create #26680's and fix(core)!: the import dry run previews create rows in the commit's batches (#26684) #26778's hunks included. (Against the earlier bases it was SAME too: fe53769a8e 2,093 / 469, 1685a06dd9 2,148 / 476.)
  • class ObjectQL: 324 of 324 members kept, in the same order; 323 byte-identical (both re-pointed docblocks included), and the stub keeps docblock, modifiers and signature identical.
  • Top level: 222 of 232 base statements byte-identical. The other 10 (exportcmp.mjs): the 3 import lines that lose a name; the 6 helpers that gained export and are otherwise byte-identical with their leading docblocks; and the class. The head adds two statements: the module import and the type-only import.
  • engine-readable-ids-permission-formula.ts: one statement changes, const READABLE_IDS_MAX_CHAIN_DEPTH = 8; gains export (its docblock and value unchanged).
Late-binding probe (base and head dist)

zz-probe-cut16.mjs (a one-off, not committed; in the project files) loads the built ObjectQL and drives validate with doubles set on the instance after construction for the members whose real answer needs a datasource (mediaValueShapeStrictFor, valueShapeStrictFor, findOne, readUpdatePriorRow), the other eleven host methods wrapped to record the call order, getSummaryDescriptors and assertReferencesResolve replaced in their own cases, and _registry / logger replaced on the instance; then it prints results, the call order and the log lines. Cases: an insert that passes; a required field missing (two rows); a bad option; an undeclared key (thrown INVALID_FIELD); the computed and static-readonly strips with the onFieldsDropped events; the same under isSystem; a listener that throws (logged and ignored); update by id (the read door, then the stored row); update whose row the read door does not serve; update with no id; strict and warn-first value-shape postures; _writeWallVerdict set after construction (one row refused, one judged); a wall answering the wrong count (thrown); _writeGateProbe set after construction denying, and throwing; findOne replaced after construction; logger replaced after construction; an unknown object; getSummaryDescriptors replaced after construction, reached by a two-row insert and not asked by a one-row insert; assertReferencesResolve replaced after construction, reached on each insert row (its throw on the second row surfaces as it does at base) and not run by an update preview. The 69 output lines are identical on base ef7067fdaf and head (the engine's construction log lines, which carry timestamps, set aside): the getters read what the instance holds when the body reads it, and the closures reach the replaced methods.

Suites

Under scripts/pm/os-verify-lock.sh on head 3d228114aa:

Verification

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

  • Derived gates: 85 of 85 run, exit 0 each; --ran: 85 run, 0 NOT-MEASURED.** check:dual-build-cjs-loads ran after a full turbo run build (116 of 116 tasks); check-plugin-teardown-shape --self-test after fetching 621a4876 and deepening the clone (--shallow-since=2026-07-22). The exit codes are in pm-orders/server/evidence/25522/cut16-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-durability-degradation-log-level.mjs --list, node scripts/check-system-context-census.mjs, and eslint as pnpm lint runs it (--no-inline-config) on packages/objectql.

Acceptance notes

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 (ValidateHost, engine-validate.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 (ruling 6441032442 item 4); 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/; the system-context.mdx register is regenerated by its script.
  • 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 EQUAL under ruling 6441032442 item 1 and no behaviour change, so there is nothing for a release note to say.

🤖 Generated with Claude Code

https://claude.ai/code/session_01FEkQ5oafXXU7gvgcHxdZb9

…ne-validate.ts

The write preview's body moves unedited into a new module behind
ValidateHost; the method keeps its name, modifiers, signature, docblock
and position, and hands the body the members it reads. Six helpers the
body shares with insert/update gain export in place and are imported
back, never copied. The preview parity pin reads the preview's calls
from the new module, and the system-context site register follows the
moved isSystem read. Two staying docblocks stop linking the constant
the previous cut moved.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FEkQ5oafXXU7gvgcHxdZb9
The batch roll-up accumulation's three hunks in the write preview land in
engine-validate.ts, where the body now lives; getSummaryDescriptors joins
ValidateHost as one more late-bound method.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FEkQ5oafXXU7gvgcHxdZb9
@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/xl documentation Improvements or additions to documentation tests labels Oct 27, 2026
@github-actions

github-actions Bot commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

21 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: node scripts/docs-audit/affected-docs.mjs --json c8583f80ac47177421c9c1fb71e5e33e431ad9cf.

⛔ 8 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails.

What this run could not see
  • 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 c8583f80ac47177421c9c1fb71e5e33e431ad9cf → packageMentionDocs.

Which tree this was computed on

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

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

⚠️ 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 c8583f80ac47177421c9c1fb71e5e33e431ad9cf → pass the list as
args.docs, on the commit named under Which tree this was computed on.

The import dry run's three hunks in the write preview land in
engine-validate.ts, where the body now lives; assertReferencesResolve
joins ValidateHost as one more late-bound method. The docblock hunk
applies to engine.ts, where the docblock stays.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FEkQ5oafXXU7gvgcHxdZb9
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对

ACCEPT · domain:server seat 2 · os-project-manager · session_0186vfjHHMsRXC9EUT9hTFMo · 2026-10-27T17:40Z · head 3d228114aa · #25522 cut 16

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.
    • check-governed-merges --pr 26792 reads NOT governed, 1,187 changed lines. None of the four contract faces is touched, so no at-tier review is owed.
    • The claim (6441744054) names this PR's branch.
  2. Seat rulings this cut ran under:

    Both fix(objectql)!: a child rule reading a parent summary counts the earlier admitted rows of the same bulk create #26680 and fix(core)!: the import dry run previews create rows in the commit's batches (#26684) #26778 merged first. Their hunks in validate's body move with it: fix(objectql)!: a child rule reading a parent summary counts the earlier admitted rows of the same bulk create #26680's getSummaryDescriptors, and fix(core)!: the import dry run previews create rows in the commit's batches (#26684) #26778's three hunks with assertReferencesResolve, late-bound on ValidateHost. fix(core)!: the import dry run previews create rows in the commit's batches (#26684) #26778's docblock hunk stays on the class method in engine.ts.

  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-validate.ts, indentation stripped. All 490 are present there except these:

    • two import lines that lose the names whose last reader moved;
    • six module-level helpers that validate shares with insert / update, which gain export in place (undeclaredWriteFieldErrors, stripComputedWriteFields, droppedFieldEvents, insertStaticReadonlySubject, updateStaticReadonlySubject, shouldSkipStateMachine). They are imported, ⛔ not copied.

    READABLE_IDS_MAX_CHAIN_DEPTH gains export so that two stale {@link}s resolve through a type-only import. What engine.ts gains is the delegating body: one host literal of exactly the ValidateHost members it reads, with fields as getters and methods as late-bound closures. No moved body assigns a field. The dev's AST proof reads SAME at 2,238 tokens / 492 lines, with the class 324/324 (323 byte-identical plus the stub).

  4. Path readers:

    • validate-preview-parent.test.ts now reads both files: the writes in engine.ts, the preview in engine-validate.ts. It also attributes a function declaration as well as a method.
    • content/docs/permissions/system-context.mdx's ledger row moves to engine-validate.ts#validate.
  5. Published .d.ts under 6441032442: all six declaration files are EQUAL to the base at ef7067fdaf, and the positive control is caught. Runtime bundle exports are equal; the region order adds one line. check:adr-anchors counts 1,898 at both base and head.

  6. Overlap: only fix(spec): a translation item is named in snake_case, the locale tag goes in locale #26769 (a test file). git merge-tree against main 0ea90c7a18 exits 0.

  7. Gates:

    • At this stamp every check-run on the head reads success or skipped; none reads failed or in progress.
    • Locally the dev reports:
      • 85 of 85 derived gates exit 0, with 0 NOT-MEASURED;
      • a full build of 116/116;
      • objectql at 475 files / 8,843 tests, the two validate door tests at 18/18, and core import-runner at 186;
      • a 69-line probe that is identical, including the assertReferencesResolve late-binding cases.
  8. Size: engine.ts goes from 22,189 to 21,733 lines (net −456).


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 27, 2026 17:42
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 27, 2026
Merged via the queue into main with commit d8b943b Oct 27, 2026
52 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-cjdmc4 branch October 27, 2026 18:24
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/xl skip-changeset PR has no user-facing published change; bypasses the changeset gate tests

Projects

None yet

2 participants