Repository navigation
refactor(objectql): move five delete-cascade bodies out of engine.ts into engine-delete-cascade.ts (#25522 cut 19) - #27391
Conversation
…into engine-delete-cascade.ts Part of the engine.ts seam split: collectCascadeDeleteSet, refuseRestrictsInsideOwnCascade, deleteRestrictedError, referenceProbeFilter and recordReferenceCheckElevation move unedited behind DeleteCascadeHost; cascadeReachOf moves with its only reader; four shared helpers and CascadeDeleteSet are exported in place. Claude-Session: https://claude.ai/code/session_01L6bhgXPvYEKGGY6sms31q2 Co-authored-by: Claude <noreply@anthropic.com>
…-delete-cascade.ts The `required` local it names moved with deleteRestrictedError. Claude-Session: https://claude.ai/code/session_01L6bhgXPvYEKGGY6sms31q2 Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L6bhgXPvYEKGGY6sms31q2 Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): 8 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 1 release-owned page(s) also name something this change touched. These are read-only:
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 816dd7f1022cb0c2b7d462f467023bfa8c6c24cc && git checkout 816dd7f1022cb0c2b7d462f467023bfa8c6c24cc
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 7e200b4a68c242afc9f7caf0289e985eff3fcac6 4a55c08e2ddd816e83b6e6acb6e4299d943ea354 && git checkout -B drift-repro 7e200b4a68c242afc9f7caf0289e985eff3fcac6 && git merge --no-ff 4a55c08e2ddd816e83b6e6acb6e4299d943ea354
node scripts/docs-audit/affected-docs.mjs --json 7e200b4a68c242afc9f7caf0289e985eff3fcac6
|
|
domain:server#1 · os-sales · session_01L6bhgXcN9HaZYSEUdJWEjw 维护者速读(席位终稿)
席位核对 — ACCEPT ·
|
|
domain:server#1 · os-sales · session_01L6bhgXcN9HaZYSEUdJWEjw Landed ·
|
| Reading | Value |
|---|---|
| PR #27391 | merged at 2026-10-29T05:42Z, merge commit 5c0e4fd0c6, head 4a55c08e (the ACCEPTed head) |
| Card #25522 | open (Part of), released to pm:queue with a delivered/remaining note |
State change: #25522 pm:dispatched → pm:queue, assignee cleared. No card was closed by this PR's body.
Generated by Claude Code
Requested by Sales · project thread
Part of #25522
Clause-②: no
Metadata-protocol: none
Ruling-ref: 6348891160 + 6441032442
维护者速读
engine.ts「删除级联与引用遍历」区里 5 个方法体(级联删除集合的收集遍历collectCascadeDeleteSet、级联内restrict判定refuseRestrictsInsideOwnCascade、409 DELETE_RESTRICTED拒绝的构造deleteRestrictedError、引用探测过滤referenceProbeFilter、引用检查提权记录recordReferenceCheckElevation)原样搬进新文件engine-delete-cascade.ts;类上的方法签名、注释、位置不变,方法体换成转交。只被搬走代码读的顶层 helpercascadeReachOf随之搬走;与留下代码共用的 4 个 helper 和CascadeDeleteSet类型原地加export、由新文件导入(不复制)。engine.ts22,110 → 21,874 行。src/engine.ts(23,903 lines, +7,501 in 30 days) along itsObjectQLseams, one move-only slice per PR #25522 按缝拆分engine.ts的第 19 刀。此区此前因 fix(objectql)!: a predicate delete honours deleteBehavior for every matched row: restrict refuses, master_detail cascades, set_null clears (#26871) #26930 在改而被排除,现已空出。重新测价:区内 24 个成员里,读私有静态成员的 10 个方法(含walkReferencingRelations、cascadeDeleteRelations、planCascadeAtomicity)和 7 个私有静态本身按系列限制必须留下,cascadeRelationBehavior因federated-injected-column-readers.test.ts按同文件读viahelper 也留下,所以实际可搬的是 5 个付得起转交成本的方法体,净 −236 行(不是派单估计的 18 个 / 约 1,069 行)。.d.ts六个文件与基线逐字节相同(含阳性对照);运行期导出名单与函数名相同;构建产物只多出新文件一个区段,外部包首次加载顺序不变;一次性晚绑定探针在新旧两版构建产物上逐行一致。回滚 = revert 本 PR。packages/objectql/src/engine-delete-cascade.ts(5 个函数与DeleteCascadeHost)、packages/objectql/src/engine.ts里 5 个转交桩、新导入行与 5 处原地export、content/docs/permissions/system-context.mdx第 29 行登记的锚点改指新文件。pnpm --filter @objectstack/objectql exec vitest run --project local --maxWorkers=2 src/engine-cascade-delete.test.ts src/engine-cascade-delete-sibling-restrict.test.ts src/engine-cascade-delete-tree.test.ts,应见三个文件全部通过(级联收集、同级 restrict 判定与拒绝信封各自走到的测试)。Cut 19 of the
packages/objectql/src/engine.tssplit (#24586 ruling A, recorded in6270097840), under the claim6487687348. Five bodies of the "Delete cascade and reference walking" section move unedited into a new module,packages/objectql/src/engine-delete-cascade.ts, behindDeleteCascadeHost. No behaviour changes. Every published declaration file is byte-identical to the base. #25522 stays open for the later cuts.Branch
claude/project-thread-f9p63p(the card thread's harness-pinned branch), cut fromorigin/mainatc4c515ad3c, withorigin/main7e200b4a68merged in before this PR was opened (that merge touched none of this diff's paths).What changes
Why this area, and why only five bodies
The claim names this section: it was excluded from cuts 17 and 18 while #26930 was open in it, and #26930 and #27073 have since merged. Its old price (−248) was stale, so it was re-priced member by member on
c4c515ad3c(engine.ts22,110 lines, blob50a504332d70). The section runs fromplanCascadeAtomicitytoorganizationOwnedRowCounts(base lines 17,647 to 19,591) and holds 27 members.planCascadeAtomicityMAX_CASCADE_DEPTH; also a liveness / reader-test sitewarnCascadeNotAtomicreferenceProbeFilterstoredReferenceIncludes,remainderAfterMemberRemoval,referenceCheckContextrecordReferenceCheckElevationdependentCountIsDisclosablestoredReferenceIncludescascadeDeleteRelationsMAX_CASCADE_DEPTH;packages/spec/liveness/field.jsonnamesengine.ts#cascadeDeleteRelationsREFERENCE_PROBE_BATCHcollectCascadeDeleteSetcascadeRelationBehaviorfederated-injected-column-readers.test.tschecks theorganizationOwnedRowCountsrow'svia: 'cascadeRelationBehavior'by reading that helper in the SAME file as the site; moving it would mean changing the test's logic, not re-keying a rowfileReferenceCheckElevationprobeReferencingRowsreferenceCheckContext,storedReferenceIncludesreferenceProbeFilterOfSetprobeReferencingRowsOfSetREFERENCE_PROBE_BATCHwalkReferencingRelationskeepsOrganizationReference,remainderAfterMemberRemovalrefuseRestrictsInsideOwnCascadedeleteRestrictedErrorisTenantColumnSetNulloutlivesOrganization,keepsOrganizationReference,organizationCountContextcountReferencingRowsOutsideorganizationCountContextorganizationCountIsDisclosablerefuseOrganizationDeleteWhileOwnedoutlivesOrganizationorganizationOwnedRowCountsoutlivesOrganization,organizationCountContext; publicSo the order's hypothesis ("~18 movable bodies, ~1,069 lines") does not hold: the series limit on private statics keeps 17 members, the largest body (
walkReferencingRelations, 318 lines) among them, and the five that move pay −225 in bodies, −236 in all. One PR, under 3,000 changed lines (753), so no further split was needed.The shape
DeleteCascadeHostmembers it reads, plus a.call, marked[#25522 cut 19], as cuts 8 and 10 to 18 did.referenceProbeFilterreads no member, so its stub calls the function directly. The one field read (logger, byrecordReferenceCheckElevation) goes as a getter over areadLoggerclosure; the methods go as late-bound closures (cascadeRelationBehavior,fileReferenceCheckElevation,probeReferencingRowsOfSet,deleteRestrictedError,referenceProbeFilter,validationMessageContext,dependentCountIsDisclosable,objectDisplayLabel), so a double set on the engine is still the one a call reaches. No moved body assigns a field, so there is no setter (6430872749not needed)..d.tsline staysprivate NAME;. No moved body reads a private static, has athis.FIELD.delete/insert/update(site, callsthis.insert/this.update/this.delete, or carries anengine.ts#NAMEliveness pointer.6432376812. The bodies readcascadeMemberKey,cascadeSetAdd,cascadeSetHas,declaredMultiValuedand theCascadeDeleteSetinterface, all also read by bodies that stay (cascadeDeleteRelations,walkReferencingRelations,countReferencingRowsOutside,resolveOptionDefaultandprobeReferencingRowsfordeclaredMultiValued). Each gainsexportin place, byte for byte otherwise, and the module imports them from./engine.js(values; the interface type-only).cascadeReachOfis read only byrefuseRestrictsInsideOwnCascade, so it moves with it, byte-identical. Nothing re-exports any of them from the package entry.renderOperationMessage(@objectstack/spec/system) andresolveFieldLabel(./validation/record-validator.js) were read inengine.tsonly bydeleteRestrictedError, so they leaveengine.ts's import lists (one specifier each; both modules are still imported there for other names) and the module imports them. The module also importsServiceObjectandExecutionContextas types.engine.tsimports the module right after cut 16's type import, after every module the new one imports, so no module is evaluated earlier than before. On the head bundles, with the// src/engine.tsmarkers set aside, the// src/...region order equals the base's with one line added,// src/engine-delete-cascade.ts(index.mjs101 to 102 regions,core.mjs90 to 91,index.js102 to 103,core.js91 to 92). The external modules load in the same first-occurrence order (index.mjsimport specifiers, deduplicated in order: md5853c7898…both). The ESM bundle has one moreimportstatement (150 to 151 inindex.mjs, 132 to 133 incore.mjs; CJSrequire(150 to 151 / 132 to 133): the module's ownrenderOperationMessageimport from@objectstack/spec/system, a packageengine.tsalready loads first, so no load moves.Net lines
engine.tsgoes from 22,110 to 21,874 lines, net −236 (42 added, 278 removed). The module is 429 lines. The diff againstorigin/mainis 753 changed lines over 4 files (+473 / −280).Readers of the moved lines
content/docs/permissions/system-context.mdx:recordReferenceCheckElevationholds row 29's elevation read (context?.isSystem). The register row is re-keyed fromengine.ts#recordReferenceCheckElevationtoengine-delete-cascade.ts#recordReferenceCheckElevation(moved to its sorted place); the row's own Symbol cell names the symbol only and is unchanged.node scripts/check-system-context-census.mjs: OK (154 read sites, register matches the tree).docs/qa/platform-checklist/areas/records-forms.json: one source citation,packages/objectql/src/engine.ts#required, named therequiredlocal ofdeleteRestrictedError; re-pointed toengine-delete-cascade.ts#required, its text otherwise unchanged.check-platform-checklist --changed-from origin/main: 13 judged, 0 lost; the full checklist gate: OK.federated-injected-column-readers.test.tskeys namecascadeRelationBehavior,planCascadeAtomicity,organizationOwnedRowCountsandisTenantColumnSetNull, all of which stay; no moved body holds a seam use, and the test passes.packages/spec/liveness/field.jsonnamesengine.ts#cascadeDeleteRelations, which stays: nothing underpackages/specchanges.packages/specCHANGELOG/comments,engine-cascade-delete.test.tsanddelete-reference-cleanup-system-identity.test.tscomments, a runtime integration test docblock namingObjectQL.refuseRestrictsInsideOwnCascade) name the method by behaviour, and the method still exists on the class.check-durability-degradation-log-level --listat base and head: identical once line numbers are masked (165 lines). No moved body guards a storage read.engine.tsentry is the transaction-seam paragraph), so the module needs no anchor entry of its own.check-adr-anchorsat basec4c515ad3c(clean checkout): 1,900 decision-letter citations on the anchored surface, 60,466 resolving citations across 8,527 files. At head: 1,900, 60,466 across 8,528. The moved bodies carry no[ADR-NNNN Dn]letter.Why
#25522 splits
engine.tsone seam at a time so that a change to one area reads, reviews and conflicts as that area alone. The delete-cascade section is the largest section left and was free once #26930 merged. Nothing about what the engine does changes.Tests
No test is added and none is edited: the move is proved by identity, not by new behaviour.
Public
.d.ts: byte-identical (ruling6348891160item 1), with a positive controlBuilt
@objectstack/objectqlat the base (c4c515ad3c,engine.tsblob50a504332d70, no module;turbo run build --filter=@objectstack/objectql..., 14 of 14 tasks) and at head (engine.tsbloba5687d70a2f0;pnpm --filter @objectstack/objectql build). The builds are the moved code, not a stale cache:collectCascadeDeleteSet.call(hostand// src/engine-delete-cascade.tseach occur 0 times in the basedist/index.mjsand once in the head's.core.d.tsbdc32f3004ee12903aba75d2f07929f8e8abdacc135f7ca65e6b66e625a16876core.d.mtsae601360109a2117cbcac69f9a5d28c5334cdff8abed80b35057fe0dd41de73bindex.d.tsbcb31057f6f7e50f34792c621026e92bc95764160843924aa25c72c476073505index.d.mts943aad8e39db6806e5585acd220b33be7219804cb27a225052b1feea873fb64eutil-6U6fCMd3.d.ts146d109c8c793af1dc26780171712da9570d87eef2e4fcadebfe32e62fb7fa37util-6U6fCMd3.d.mts146d109c8c793af1dc26780171712da9570d87eef2e4fcadebfe32e62fb7fa37util-6U6fCMd3at both), so6348891160item 1 holds with no normalisation and the6441032442reorder bar is not reached. The6441032442normaliser (dtsnorm.mjs) lives in another project's files and was not available in this thread's container; with a 0-line raw diff on all six files there is nothing for it to normalise.index182 names andcore54, ESM and CJS, and a sha256 over every export's name and function.nameis the same at base and head (2aaa13a11288fd84…/70d1bf1f346b2793…), so no bundler same-name rename happened.turbo run build(115 of 115 tasks, the dependents' DTS builds included) andpnpm --filter @objectstack/core typecheckran: exit 0 each. (P2)packages/objectql/package.jsonexportsnames./dist/index.*and./dist/core.*only; (P3)git grep -n -E 'objectql/dist|driver-sql/dist' -- scripts package.json: 0 hits.Positive control. On the head,
node scripts/ablation-replace.mjsdroppedprivatefromprivate async deleteRestrictedError(args: {(a moved member's stub; anchor 1 to 0, bloba5687d70a2f0to1850f07b29d3) and rebuilt (exit 0). The chunk is renamedutil-Dd_AzMXVand now publishesdeleteRestrictedError(args: { object: string; … context: ExecutionContext | undefined; }): PromiseofError(line 11117, where the head printsprivate deleteRestrictedError;); every declaration file's sha256 changes (core.d.ts68f57645…,index.d.tsb9136807…, chunk519be62f…). The restore leg put the blob back toa5687d70a2f0(git diff HEADempty), and the rebuild that followed produced six declaration files and four bundles that compare equal to the head build.AST compare (move-only)
A one-off script (TypeScript compiler API; leaf tokens by recursive
getChildren) on baseengine.tsagainst headengine.tsplus the module:async, body tokens, and every body line equal after one level of indent):referenceProbeFilter97 tokens / 13 lines,recordReferenceCheckElevation107 / 24,collectCascadeDeleteSet582 / 64,refuseRestrictsInsideOwnCascade810 / 74,deleteRestrictedError323 / 81. Negative control: one changed token in the module copy (slice(1, -1)toslice(1, -2)) reads DIFF.class ObjectQL: 330 of 330 members kept, in the same order; 325 byte-identical including leading trivia, and the 5 stubs keep docblock, modifiers and signature identical.declaredMultiValued,CascadeDeleteSet,cascadeMemberKey,cascadeSetHasandcascadeSetAdd, each withexportadded and otherwise byte-identical;cascadeReachOf, moved into the module byte-identical; and the class. The head adds one statement: the module import.Late-binding probe (base and head
dist)zz-probe-cut19.mjs(a one-off, not committed) loads the builtObjectQLfrom eachdist/and drives the five bodies with doubles set on the instance after construction:referenceProbeFilteron a single-valued lookup, a multi-valued lookup, an id needing JSON escaping (the$orarm) and a multiselect;recordReferenceCheckElevationwithuserId,actor,isSystemand no context, theloggerreplaced twice after construction;collectCascadeDeleteSetover a three-level master-detail fixture with every host member replaced and each call recorded in order, with and withoutfileElevations;refuseRestrictsInsideOwnCascadeon both sets after a second generation of doubles is wired (it reaches the second generation, and refuses through the replaceddeleteRestrictedErrorwith the replacedreferenceProbeFilter's filter); and the class's owndeleteRestrictedErrorwith its three host members replaced, for arestrictand arequiredrelation, with count disclosure off and on. The 16 output lines are identical on basec4c515ad3cand head (the 5 construction log lines, which carry timestamps, set aside); stderr is empty on both.Suites
Under
scripts/pm/os-verify-lock.sh:turbo run build: 115 of 115 tasks, exit 0.typecheckof@objectstack/objectql(tsc +check:test-typecheck) and@objectstack/core: exit 0 each.@objectstack/objectqlvitest--project local: 479 files, 8,942 tests passed, exit 0 (theengine-cascade-delete*files,engine-cascade-reference-carrier.test.ts,engine-dangling-reference-audit.test.tsandfederated-injected-column-readers.test.tsamong them).@objectstack/runtimecascade-delete-multivalue-lookup-real-driver,cascade-delete-restrict-same-parent-real-driverandsandbox/referential-cascade-delete-signalintegration tests (3 files, 15 passed, 1 skipped), and@objectstack/plugin-securitydelete-reference-cleanup-system-identity.test.ts(18 passed).test:repoprojects were not run locally (the published surface is byte-identical);Test Coreruns them in CI.Verification
The gate list is
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackon this diff (merge basec4c515ad3), each run with its exit code captured before any pipe, then reconciled with--ran(each line carrying itsexit N).--ran: 80 derived, 80 run, 0 NOT-MEASURED, 0 UNRUN ("a DERIVED zero — all 80 recorded an exit code and none of them is 3").check:dual-build-cjs-loadsandcheck:dts-closureran after the full build;check-plugin-teardown-shapeandcheck-engine-split-ratio --self-testafter deepening the clone to 2026-07-22 and fetching621a4876.node scripts/check-adr-anchors.mjs(counts above),node scripts/check-tenant-audit-census.mjs(245 write call sites),node scripts/check-adr-symbol-anchors.mjs(2,798 anchors),node scripts/check-platform-checklist.mjs --changed-from origin/mainand the fullcheck-platform-checklist.mjs,node scripts/check-commit-card-trailers.mjs --range origin/main..HEAD,node scripts/check-system-context-census.mjs,node scripts/check-nul-bytes.mjs,node scripts/check-durability-degradation-log-level.mjs --list(base and head identical, masked), and eslint aspnpm lintruns it (--no-inline-config) onpackages/objectql.origin/main7e200b4a68was merged (15 files underui/and the Studio, none of this diff's paths), the change set againstorigin/mainis the same four files, so the derived list is unchanged.Acceptance notes
6487687348named this branch.packages/objectql/**or this diff's docs, listed at start and again just before opening this (each head fetched,git diff --name-only origin/main...pr/N): draft feat(objectql): record-currency guards — blank code refuses the write; ungrouped sum/avg/min/max over a record-mode amount refused; summary roll-up over one refused at registration #27339 editsengine.tsat its import block (right after cut 18's import),insert,updateandaggregate, none of this section, the five exported helpers or the two edited import lists;git merge-treeof this head with feat(objectql): record-currency guards — blank code refuses the write; ungrouped sum/avg/min/max over a record-mode amount refused; summary roll-up over one refused at registration #27339's head is clean. feat(service-automation, runtime, spec): connector credential store and open-tier credentialRef chain (#27237) #27344 adds rows tosystem-context.mdxin other places and auto-merges with this head. The branch objectql: virtual-object correctness floor —applyDriverQueryInMemoryhelper, per-datasource row cap with a loud refusal,supportshonoured for plugin drivers, refusal-envelope mapping for driver failures (epic #27261 P1) #27277's claim named,claude/project-thread-mlrxgx, does not exist on the remote (git ls-remote: 0 refs), so there was nothing to read there. Whichever lands second mergesmain.walkReferencingRelations(318 body lines), cannot move under the current series limits.cascadeRelationBehavior(60) stays for the reader test's same-fileviarule.engine.ts'sCascadeDeleteSetdocblock andrefuseRestrictsInsideOwnCascade's docblock still say{@link cascadeReachOf}, which now lives in the module; they are left byte-identical, as cut 14 left a{@link}to a helper that moved. The moved bodies keep their indentation minus one level, as moved, not re-formatted.Human Minimum
packages/specholds no runtime logic. (Not touched.)DeleteCascadeHost,engine-delete-cascade.ts).pnpm lintpasses on the touched package.main, one pull request.skip-changeset; both declaration lines are filled in above.packages/specnot touched.content/docs/references/,packages/*/CHANGELOG.mdorcontent/docs/releases/.skip-changeset: move-only inside@objectstack/objectql, with every published declaration file byte-identical and no behaviour change, so there is nothing for a release note to say.🤖 Generated with Claude Code
https://claude.ai/code/session_01L6bhgXPvYEKGGY6sms31q2
Generated by Claude Code