Repository navigation
refactor(objectql): retire the four engine.ts facade-gate exception rows the protocol.ts split handed over - #31343
Conversation
… facet The codemod's --members pass moved it, with its field-call stay reason lifted for homeLocalizationResolver alone in a one-off local run (the script is unchanged). Its undo proof rebuilds the base engine.ts and the facet module byte for byte. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FEkQ5kF4yD4vMatZJ2chQM
… through; retire the two home-amount facade exception rows Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FEkQ5kF4yD4vMatZJ2chQM
…te appAuthored through the new instance's host Their bodies move into ScopedContextFacet. Each builds a ScopedContext and writes its private appAuthored through that instance's own host setter (the port the class's static block fills), so the facade keeps delegations only. The class surface and its .d.ts are unchanged. The two facade exception rows retire. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FEkQ5kF4yD4vMatZJ2chQM
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FEkQ5kF4yD4vMatZJ2chQM
…e facet method that now holds it Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FEkQ5kF4yD4vMatZJ2chQM
📓 Docs Drift CheckThis PR changes 1 package(s): 7 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
What this run could not see
Coarse fallback — 21 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 aeeb5540bb565e3ce7515fcb5db604671a0dc1f3 && git checkout aeeb5540bb565e3ce7515fcb5db604671a0dc1f3
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 30e0bd160ee2afc1557746df0b6806625f37a408 7d5f631fc499bee9a4ac560361d3857a683e82a5 && git checkout -B drift-repro 30e0bd160ee2afc1557746df0b6806625f37a408 && git merge --no-ff 7d5f631fc499bee9a4ac560361d3857a683e82a5
node scripts/docs-audit/affected-docs.mjs --json 30e0bd160ee2afc1557746df0b6806625f37a408
|
|
domain:server#6 · os-project-manager · session_01FEkQ5p3T5YWhLjurajudnQ 席位核对 · ACCEPT · 2026-11-08T03:32Z · head
|
Requested by Leehom · project thread
Fixes #30975
Clause-②: no
Metadata-protocol: none
维护者速读
engine.tsfacade 门禁例外行全部退役。homeAmountServing的方法体用 codemod 搬进 references facet;projectHomeAmountsOntoPatch类上存根不再算默认值,改由 facet 算;ScopedContext的derive与[APP_AUTHORED_HOOK_API]的方法体搬进ScopedContextFacet,对新实例私有字段appAuthored的那一次写改走该实例自己的内部 host setter。engine.ts里的类只剩一行委托;这四行是登记在案的欠账,登记表里card: #30975的行现在为零。.d.ts只少了两个私有成员的注释块,声明行零差异);回滚即 revert 本 PR。content/docs/permissions/system-context.mdx第 1b 行的 Symbol 键由inheritsElevation改为appAuthoredHookApi,站点登记表用--write重新生成(check:system-context-census按符号定位isSystem读点,方法体搬家后键随之移动)。packages/objectql/src/engine-scoped-context.ts新增的derive/appAuthoredHookApi(scopedContextHost.of(child).appAuthored = …那两行);engine.ts中ScopedContextstatic block 新增的set appAuthored;engine-references-and-master-detail.ts的homeAmountServing。pnpm check:facade-delegation-only,预期末行5 facade class(es) in 3 file(s) hold delegations only, 11 named exception(s).,退出码 0。What changes
All four exception rows that #30147's ruling (6687801899, option A) handed to this card retire, and
scripts/facade-delegation-registry.jsonnow holds no row withcard: "#30975"(15 → 11 exceptions).Row 1,
ObjectQL.homeAmountServing. The body moves intoReferencesAndMasterDetailFacetby the codemod's own--memberspass, unedited. Onmainthe pass refuses it withfield-call: homeLocalizationResolver. For this move only, that one stay reason was lifted for that one field in a local, uncommitted copy ofscripts/codemod-split-engine.mjs(a one-line condition; the committed script is unchanged and no general field-forwarding rule is added). That run's own undo proof rebuilds the baseengine.tsand the facet module byte for byte. The host already carriedhomeLocalizationResolver(getter and setter),findand_registry, so the host factory does not grow. Three import bindings move fromengine.tsto the facet with the body (EXCHANGE_RATE_OBJECT,memoizeHomeAmountServing,HomeLocalization).The one runtime difference is the resolver's receiver:
this.homeLocalizationResolver?.(organizationId)now calls it with the host asthis, not the engine.HomeLocalizationResolveris a plain function type oforganizationIdwith nothisparameter, and all 14 installations in the tree (one inplugin.ts, the rest in tests) are arrow functions, so nothing reads it.Row 2,
ObjectQL.projectHomeAmountsOntoPatch. The body already lived in the facet (#29986), and its facet signature already defaultsservingtothis.homeAmountServing(context). The class stub's parameter is nowserving?: HomeAmountServingand is passed through as given, so the facet computes the default when the caller passes none. It still reaches the engine'shomeAmountServingthrough the host closure, so an instance spy or a subclass override is still the one called. The only caller (the update path) passes none.Rows 3 and 4,
ScopedContext.deriveandScopedContext[APP_AUTHORED_HOOK_API]. The order asked first whether a design moves the foreign-privateappAuthoredwrite off the class without widening anything public. One does, and it is small:ScopedContextFacet(asderiveandappAuthoredHookApi). Each builds aScopedContextand writes the new instance's privateappAuthoredthrough that instance's own host:scopedContextHost.of(child).appAuthored = this.appAuthoredandscopedContextHost.of(api).appAuthored = true.set appAuthored(v) { instance.appAuthored = v; }, which is the codemod's own shape for a field a body assigns, andScopedContextHost.appAuthoredlosesreadonly.deriveis private, so its docblock moves with its body;[APP_AUTHORED_HOOK_API]is public and keeps its docblock on the class.ScopedContext's static block hands toscopedContextHost.install(...)(packages/objectql/src/engine.ts, the[#30052 codemod] Installs the host of ScopedContextFacetblock). The only way to get a host isscopedContextHost.of(instance), andscopedContextHostis exported only frompackages/objectql/src/engine-scoped-hosts.ts. Measured withgit grep -n "engine-scoped-hosts" -- packages examples apps: exactly two modules import it,engine.ts(line 426) andengine-scoped-context.ts(line 44); the other four hits are comments. Neitherpackages/objectql/src/index.tsnorsrc/core.tsre-exports either module, the package'sexportsmap has only.and./core, andscopedContextHost,InstanceHostandScopedContextHostappear 0 times in the builtindex.d.ts,core.d.tsand the shared chunk. So nothing outside theengine*.tsmodules can reach the setter through the package.privatewas never a runtime barrier, so the setter grants no capability that in-process code lacked.engine.tsno longer readsappAutomationElevationOformarkAppAutomationElevation, so those two import bindings move to the facet module, which already imported them.Rows 3 and 4 therefore do not need an accepted-exception ruling, and this PR writes no exception reason.
One docs cell and the generated register.
check:system-context-censuskeys everyisSystemread by the symbol that holds it. Onmainthe app-authored api's read (const inheritsElevation = ctx.isSystem && …) sat in a symbol-keyed method, so the census keyed itengine.ts#inheritsElevation, and row 1b ofcontent/docs/permissions/system-context.mdxnamedinheritsElevation. In the facet the read lives inappAuthoredHookApi, so the gate went red with[row-key-no-read]and[read-without-a-row]. Row 1b's Symbol cell now namesappAuthoredHookApi(elevationSourcekept), andnode scripts/check-system-context-census.mjs --writeregenerated the site register (one line out, one line in). No prose changes. The claim amendment 6716417615 covers this file.A patch changeset for
@objectstack/objectql: the package's built JavaScript moves between modules inside the bundle, while its declarations do not.engine.tsgoes from 10,712 to 10,653 lines. 6 files, +118 / -104.Tests
No test file changes; this is a move, and the existing suites pin the moved behaviour. Measured on
b2ef226ef; the two commits after it (the changeset and the docs page) touch no source:@objectstack/objectqlvitest (--project local): base604a2b3b9565 files / 11,428 tests passed; head 565 files / 11,428 tests passed.pnpm --filter @objectstack/objectql typecheck(tsc pluscheck:test-typecheck) exit 0.derive's write (ablation-replace.mjs, anchorscopedContextHost.of(child).appAuthored = this.appAuthored;replaced by… = !1 && this.appAuthored;; mutation landed, anchor 1 → 0):recycle-bin/purge.test.ts+hook-run-as.test.tswent 6 failed / 62 passed, among them "is refused the same way on the apisudo()derives" and the fourrunAspermanent-delete refusals. Restored: blob equal toHEAD,git diff HEADempty.scopedContextHost.of(api).appAuthored = true;replaced by… = !1;): the same two files went 9 failed / 59 passed. Restored the same way. The head control run of both files: 68/68.src/(../engine.js), so the ablations needed no rebuild.Acceptance notes
.d.tsbar.@objectstack/objectqlbuilt at base and at head:index.d.ts,index.d.mts,core.d.tsandcore.d.mtshave zero differing lines once the shared chunk's file name is normalised. The chunk itself (util-*.d.ts/.d.mts) differs only by 16 comment lines and 0 declaration lines: the JSDoc blocks of the two private members whose bodies moved (homeAmountServing's 15 lines andderive's one line), which leave with their bodies under the codemod's private-docblock rule (the same rule refactor: move the private docblocks of the hand-series stubs into their hand modules — about 1,900 lines off engine.ts (86 stubs) and about 4,000 off protocol.ts (157 stubs) #30053 applied).private homeAmountServing;,private projectHomeAmountsOntoPatch;,private appAuthored;,private derive;and[APP_AUTHORED_HOOK_API](): ScopedContext;are printed as before. Because the chunk's content changed, its hashed file name changed too (util-DGHGF_i8→util-BfmsHmSX). So the public surface is unchanged, but the declaration files are not byte-identical: they differ by those two private comments and the chunk name. This is stated here, not hidden; keeping the comments on the class would break the codemod's undo proof for row 1.check-facade-delegation-only.mjs --root BASE_TREE --registry HEAD_REGISTRYexits 1 with exactly four problems, one per retired member, so each row was load-bearing and each move is what turns it green.node scripts/codemod-split-engine.mjs --members --verify --base 604a2b3b9onaba639da1exits 0. Without the lift it exits 1 with exactly one failure, the stay rule itself (homeAmountServing: a stay rule holds it at base (field-call: homeLocalizationResolver)).packages/objectql/src/engine*.tsat 02:13Z: only feat(objectql): restore a recycle-bin entry under its original ids and emit data.record.restored (#30237 PR 3) #31220 (engine.ts hunks at about lines 86, 2443 and 8360).git merge-tree --write-tree HEAD pr/31220exits 0, with no conflict.Verification
Codemod move and undo proof (row 1)
The one-off lift, applied only for the run (not committed):
node scripts/codemod-split-engine.mjs --members homeAmountServing --write: exit 0.--members --verify --base 604a2b3b9with the lift: exit 0, the same proof line. Without the lift (the committed script): exit 1, the one stay-rule failure only.Facade gate
node scripts/check-facade-delegation-only.mjsat head: exit 0,✓ check-facade-delegation-only: 5 facade class(es) in 3 file(s) hold delegations only, 11 named exception(s).Control, base code with the head registry: exit 1, four problems:
Suites
Under the shared lock: base
vitest run --project local --maxWorkers=2VERDICT exit 0, Test Files 565 passed (565), Tests 11428 passed (11428). Head: VERDICT exit 0, Test Files 565 passed (565), Tests 11428 passed (11428).pnpm --filter @objectstack/objectql typecheckVERDICT exit 0.pnpm --filter @objectstack/objectql buildexit 0 at base and at head (after--filter '@objectstack/objectql^...' build, exit 0).eslint --no-inline-configon the three changed source files: exit 0..d.ts base vs head
pnpm --filter @objectstack/objectql buildat604a2b3b9and atb2ef226ef. With the shared chunk's hashed name normalised:index.d.ts,index.d.mts,core.d.ts,core.d.mtsdiff exit 0. The chunk (util-DGHGF_i8.d.ts→util-BfmsHmSX.d.ts, and the same for.d.mts): 16 changed lines, 0 of them outside a comment. The whole diff (normaldiffoutput, its<markers spelled-here):Derived gates
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, derived three times as the diff grew (source, then the changeset, then the docs page). Every family run with stdin closed and its exit code captured before any pipe; the source-only families ran at15827c138, the changeset and docs families and the two reruns at7d5f631fc(the docs commit touches only the.mdxpage). Reconciliation at7d5f631fc:node scripts/pm/dispatch-gates.mjs --ran 30975-gates-ran.txt, exit 0, "103 derived, 101 run, 2 NOT-MEASURED, 0 UNRUN".dist/):pnpm check:dual-build-cjs-loads,pnpm check:published-readme-exports. CI runs both.node scripts/check-plugin-teardown-shape.mjs --self-testfirst answered exit 3 on the shallow clone; aftergit fetch --depth=1 origin 621a487607881c66b2899b7e3477115229a156b4it exits 0.pnpm check:system-context-censuswas exit 1 before the row 1b re-key (above) and is exit 0 after it.pnpm --filter @objectstack/spec run check:skill-examplesfirst answered exit 3 (no client SDKdist/); after building@objectstack/client-reactand its closure it exits 0.Session: https://claude.ai/code/session_01FEkQ5kF4yD4vMatZJ2chQM
🤖 Generated with Claude Code