Repository navigation
refactor(objectql): move the predicate-write hook dispatch bodies out of engine.ts into engine-hook-dispatch.ts (#25522 cut 6) - #26102
Conversation
… of engine.ts into engine-hook-dispatch.ts Move-only: buildPerRowAfterContexts, dispatchPerRowBeforeHooks, assertBulkPerRowHookBudget and inAfterAbortUnit keep their methods, signatures and docblocks on ObjectQL; each body now lives unedited in engine-hook-dispatch.ts and is reached through HookDispatchHost (_registry as a getter, triggerHooks and transaction as late-bound closures). Imports whose last reader moved are narrowed. Claude-Session: https://claude.ai/code/session_01VbCrrAuovPD2joHGR1XXRZ Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): 22 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 6 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 19 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 52db9e713a576408699f4c35a12863b56c9ffb48 && git checkout 52db9e713a576408699f4c35a12863b56c9ffb48
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 0a6948210cc96a95cc568b30193895e157968c01 69d8f91a606f91e09c252e40861a91da9b205000 && git checkout -B drift-repro 0a6948210cc96a95cc568b30193895e157968c01 && git merge --no-ff 69d8f91a606f91e09c252e40861a91da9b205000
node scripts/docs-audit/affected-docs.mjs --json 0a6948210cc96a95cc568b30193895e157968c01
|
席位核对ACCEPT · The maintainer's standing authorisation for landing reviewed PRs, verbatim: 「按照 pm-dispatch 规则复核过的都授权」.
Generated by Claude Code |
Requested by Leehom · project thread
Part of #25522
Clause-②: no
Metadata-protocol: none
Ruling-ref: 6348891160
Cut 6 of the
packages/objectql/src/engine.tssplit (#24586 ruling A, recorded in6270097840). The claim is6414562831. Four bodies of the "Hook dispatch internals" section move unedited into a new module,packages/objectql/src/engine-hook-dispatch.ts:buildPerRowAfterContexts,dispatchPerRowBeforeHooks,assertBulkPerRowHookBudgetandinAfterAbortUnit. No hook behaviour changes. #25522 stays open for the later cuts.Branch
claude/project-thread-2f5vpq(the card thread's harness-pinned branch), cut fromorigin/mainat712770bee(after cut 5,4bea75589f), withmainmerged in twice (be7897799, thenaa2f8e31f, becausedispatch-gatesreported a stale gate script). Neither merge touchedengine.ts.Why this area (the fresh seam table)
Measured on
origin/main712770bee;engine.tsis byte-identical ataa2f8e31f.engine.tsis 23,351 lines, 7,317 of them (31.3%) from the last 30 days (git blame -s --since=2026-09-26T11:15Z).scripts/pm/git-history.mjs count(window covered, exit 0): 123 commits in 30 days, 31 in 7.class ObjectQLholds 318 members, 256 methods, 148 private or protected. Sections are cut 1's contiguous member ranges, re-located by first member name, docblocks included. "inferred public" stays; "U" countsthis.FIELD.delete/insert/update(sites (AST, casts stripped); "private statics" stay.txStore…constructorgetStatus…registryregisterHook…triggerHookshasHooksFor…assertBulkPerRowHookBudgetregisterAction…setActionActive_heldFileResolver…registerInsertRecomputeListenerreadableIdsSecurityReported…resolveDefaultPermissionswriteEpoch…hasObjectMiddlewareexecuteWithMiddleware…buildUserbuildDriverOptions…resolveEnginePostureprobeInstallOrganizations…transactionCoversDriverForresolveNowDefault…defaultFieldEntriesapplyAutonumbers…seedAutonumberregistrationConversionWarned…registerDriversupplyFileColumnsMovedResolver…listUnavailableDatasourcesassertWriteAllowed…publishBulkDataEventsetI18nService…objectDisplayLabelassertReferencesResolve…masterDeclaredFieldsinspectDanglingReferences…inspectCascadeDeletesetCryptoProvider…setTenancyPostureProviderencryptSecretFields…resolveInternalFieldgetSchema…judgeFilterLIFECYCLE_DATASOURCE…setDatasourceMappinginit…resolvePrimaryDatasourceobjectHasMediaField…valueShapeStrictForisFileReferencesMigrationVerified…wasDatastoreCreatedFromEmptydestroy…destroyMAX_EXPAND_DEPTH…MAX_BULK_PER_ROW_HOOK_ROWSautonumberCounters…recomputeSummariesexpandRelatedRecords…expandRelatedRecordsresolveFileReferences…readParentDerivedFilesresolveWhereTokens…requireFindOnePredicatefind…staticReadonlyCreateStripvalidate…validatereadUpdatePriorRow…insertManyupdate…updateplanCascadeAtomicity…organizationOwnedRowCountsdelete…deletecount…aggregateexecute…enforceTransactionOriginregisterObject…createThe per-method move screen for every section is in the project file
pm-orders/server/25522-cut6-seam-table.md. A body stays if any of these holds: an inferred-return public method, a U site, a private-static read, anengine.ts#NAMEpointer underpackages/spec/**ordocs/adr/**, or athis.update(/this.insert(/this.delete(self-call. The pick:buildSummaryIndexis cited frompackages/spec(functional-completeness.ts);recomputeSummariesself-callsthis.update(;ensureSummaryIndexeswrites three fields. Does not payunregisterDriverandclearDatasourceUnavailableare U sites (U = 3);registerDatasourceDefis cited fromliveness/datasource.json. Does not payexpandRelatedRecordsreads the private staticMAX_EXPAND_DEPTHand is cited fromliveness/query.jsonandliveness/field.jsonrecordObservedDeviationandretractCreationAttestationself-callthis.update(; the rest write five fields, so the host needs setters, a shape no cut has usedpackages/specordocs/adrpointerThree bodies of the section stay in the class:
hasHooksFor,opensAfterAbortUnitanddispatchUnscopedMultiWriteHooks. Each callshookMatchesObject, a functionengine.tsdeclares and exports at module level. Reaching it from the new module would be a value import ofengine.ts, which would be the series' first run-time module cycle (everyengine-*.tsmodule imports the engine as a type only).The shape (#23050 shape B, as cuts 1 to 5 apply it)
fn.call(host, …), marked[#25522 cut 6].HookDispatchHostis new:_registry,triggerHooks,transaction, each typed as the class member (ObjectQL['triggerHooks']). Each moved function types itsthisas thePickof exactly the members its body reads, and each stub annotates its literal asThisParameterTypeof that function. There is no cast, and no member changes visibility._registrygoes as a GETTER.triggerHooksandtransactiongo as late-bound closures over the instance, so a double or spy set on the engine still intercepts.inAfterAbortUnitis generic, so its stub instantiates the function with the method's own type parameterTbefore.call(host, write). A bare.callwould eraseTtounknown. The emitted JS is a plain.call.assertBulkPerRowHookBudgetreads no member and is called directly.ObjectQLas a TYPE only and nothing re-exports it.engine.tsdrops the names whose last code reader moved:resolveBulkPerRowHookBudget,divergingHookPayloadKeysandMultiUpdateHookKeyDivergenceError. The last one is still named by a{@link}in thedispatchPerRowBeforeHooksdocblock that stays, and that docblock goes to no published.d.ts, because private members print without JSDoc.SummaryRecomputeError,HookTargetRebindError,coerceBooleanFields,recordHookPayloadWritesandMAX_BULK_PER_ROW_HOOK_ROWSstill have readers inengine.tsand stay.Net lines (the seat's rule)
engine.tsgoes from 23,351 to 23,234 lines, net -117. The module is 240 lines: the header and host interface (about 66), four one-line docblocks, and the 141 moved body lines with their signatures. The diff is 415 changed lines over 2 files (+269 / -146), far under the 3,000-line human-merge threshold.The published
.d.tsdoes not change (ruling6348891160)Base:
@objectstack/objectqlbuilt withengine.tsrestored frombe7897799and the new module removed (tree only; restored withgit checkout HEAD --, thengit statuswas clean). Head: built at1b00a88a9. The second merge brought no package change. The headdist/index.mjscarriesinAfterAbortUnit.call(hostonce and the base build has it zero times, so each leg built the source it names. All six declaration files are byte-identical:core.d.ts1d20303a455a909e4b55fd0802fb0b0bcb59ec51784daf27d5ef4c0659410953index.d.ts0454d5e70b156d8effbf27b8915587c7cbee241dcd7d698013c0a4c369a02aa5util-CVzOGxAn.d.ts85ef1cbec2b016d5f7afabaa21689c86c6dffc573076fdfdeb8ea0f1a1b1ad53core.d.mtsef178bf0725eccc2f358f082ce13dc5122a6d5a53c19ef630017256f67de03b4index.d.mtsccf900240aa137abe72f82b8abfc872d42c5645467400134c580285f2a3b3090util-CVzOGxAn.d.mts85ef1cbec2b016d5f7afabaa21689c86c6dffc573076fdfdeb8ea0f1a1b1ad53Positive controls: (1) making
dispatchPerRowBeforeHookspublicin the source and rebuilding (an earlier base,712770bee) changed all six hashes, and the method printed with its full signature atutil-35eaTeZh.d.ts:5034. (2) Removingprivatefrom line 5063 of the headutil-CVzOGxAn.d.ts(private dispatchPerRowBeforeHooks;) gives47f9b025059291111682c59ff64f77688fd34cb0077bf8a753443bb6ce3a18d0. Export lists are the same for ESM and CJS at both ends:.182 names (sorted sha256881616cc…0adaa) and./core54 (393e8069…90f49). No moved name is exported. The same six-file comparison at712770beewas also byte-identical.Proof of move-only
A TypeScript-AST leaf-token compare (recursive
getChildren, JSDoc skipped) of baseengine.tsagainst the head module:this), return type, type parameters and body tokens are SAME,asyncmatches, and every body line equals the base line less two leading spaces, comments included:dispatchPerRowBeforeHooks454 tokens / 103 lines,buildPerRowAfterContexts165 / 17,inAfterAbortUnit73 / 12 (type parameterTon both),assertBulkPerRowHookBudget73 / 9.===to!==in the movedassertBulkPerRowHookBudgetreports DIFFER. Editing only the// D4.comment in the moveddispatchPerRowBeforeHooksfails the line rule. Both exit 1.@objectstack/spec/dataimport and the removedmulti-update-hook-key-divergence.jsimport. Head adds the new module import with its comment.Readers of the moved lines
engine.ts#NAMEpointer anywhere in the repository names one. Noscripts/adr-anchors/**paragraph describes them: the engine entry's paragraph is the transaction seam. No gate or source-reading test matches their text (screened: everyscripts/gate,docs/qa,content/docs, and the 14 test files that readengine.tsfrom disk).packages/spec/liveness: 40 entries citeengine.ts, and every one of their keys is still mentioned inengine.tsoutside the moved range, so the key-mention check loses nothing.ADR-NNNNcitation (theADR-0058andADR-0119 D1citations sit in the docblocks, which stay).check-adr-anchorscounts the same at base and head: 88 anchored files and 1,864 decision-letter citations at712770bee, and 88 and 1,878 atbe7897799against head1b00a88a9. No anchor entry is needed.check-tenant-audit-censusis green: 243 write call sites, and the moved bodies hold nothis.FIELD.delete/insert/update(site.check:logger-receiver-detachis green (the moved bodies read no logger), and so ischeck-system-context-census.Acceptance notes
engine.tsbeside a moved method still resolves by method name, because every method stays on the class. Examples areservice-storage'sattachment-lifecycle.tsandplugin-audit'saudit-writers.tscomments, andspec'sbulk-write-hook-conformance.tsnamingObjectQL.assertBulkPerRowHookBudget. They are left as they are.dispatchPerRowBeforeHooksbody still says "see the class doc above" next to the D3 accumulate step. The doc it means is the method's docblock inengine.ts. The comment is unedited because this is a move.pr_create: 38 PRs are open. One touchesengine.ts: fix(objectql,types,rest)!: a database NOT NULL refusal on a declared field is the record validator's ValidationError at every door #26026 (seat 1's A database NOT NULL refusal answers differently per door: the dispatcher /data twin answers 500 INTERNAL_ERROR, and REST batch/import/form/action rows name the column key (RestServer POST/PATCH /data answer 400 with the label after #24715) #24768,claude/project-thread-otcr21at50c3929ba), with hunks at lines 209, 8621-8674, 17122, 17310 and 18743-19275. This PR's range is 5343-5819.git merge-tree --write-treeof the two heads is clean (exit 0). Whichever lands second mergesmain.Verification
Tests at
1b00a88a9, which has the same package sources as the final head69d8f91a6(the second merge touched only.github/,scripts/andui/):pnpm --filter @objectstack/objectql typecheck(tsc --noEmit+check:test-typecheck, 39 files / 233 errors / 64 pinned signatures held): exit 0.eslint --no-inline-config packages/objectql: exit 0.pnpm --filter @objectstack/objectql exec vitest run --project local --maxWorkers=2: 470 files, 8,748 tests, 8,748 passed, 0 failed, 0 skipped. The same suite was also green before the first merge (50dae828a: 470 files, 8,742 tests). The run includesbulk-write-per-row-hooks.test.ts,multi-update-hook-key-divergence.test.ts,engine-after-hook-abort-unit.test.tsandhook-exclude-objects.test.ts.packages/corerow-serving-door-exposure.pin.test.tsandsecond-object-read-exposure.pin.test.ts(both scan the repository for read doors): 2 files, 7 tests passed..d.tsfiles are byte-identical, so no dependant can see a difference. CI'sTypeScript Type Checkcovers them.69d8f91a6:dispatch-gates --commandsderived 56 families on the real diff. Every one was run, each exit code captured before any pipe, then reconciled with--ran: 56 accounted for, 55 run, 1 NOT MEASURED. NOT MEASURED (CI runs it):check:dual-build-cjs-loads, exit 3, PREREQUISITE NOT MET. It needs every package'sdist/; the export lists above answer it for objectql. Named readings:check-system-context-censusOK (151 read sites, 144 of 144 symbols cited),check:logger-receiver-detachOK,check:durability-log-levelOK,check-engine-split-ratio --days 9097.6%.69d8f91a6:check-adr-anchorsat baseaa2f8e31fand at head (88 files and 1,878 letters at both ends),check-adr-symbol-anchors(2,658 anchors resolve),check-tenant-audit-census(243 write call sites),check-nul-bytes,check-platform-checklist --changed-from origin/main(13 anchors judged, 0 lost) andcheck-commit-card-trailers --range origin/main..HEAD.test:repowas not run locally; CI'sTest Coreruns it.skip-changeset: move-only. The published.d.tsfiles and export lists are byte-identical,liveness/and every other published path are untouched, and the module's runtime is the same code under a different file name.Generated by Claude Code