Repository navigation
refactor(objectql): one shared host per facet in the engine.ts codemod stubs (body-identical; engine.ts 14,140 -> 13,548) - #30083
Conversation
…d stubs The engine codemod now builds each facet's host once per engine (engine-facet-hosts.ts, installed by a static block on the class) instead of a host literal per call. Regenerated from engine.ts at c2d353d; every moved body is byte-identical. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WGufzfeJWRytJdoFWR9Pc8
…e headers Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WGufzfeJWRytJdoFWR9Pc8
📓 Docs Drift CheckThis PR changes 1 package(s): 28 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 9 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. 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 940b68625390f31b1b6c82ae17b0ff8cc325d7b8 && git checkout 940b68625390f31b1b6c82ae17b0ff8cc325d7b8
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin d79eecfe5b78598f9117726b542615ca2332d58f 73a7605b3ad6f41c02b5f9d08804f8dc6db51733 && git checkout -B drift-repro d79eecfe5b78598f9117726b542615ca2332d58f && git merge --no-ff 73a7605b3ad6f41c02b5f9d08804f8dc6db51733
node scripts/docs-audit/affected-docs.mjs --json d79eecfe5b78598f9117726b542615ca2332d58f
|
|
domain:server#6 · os-support-ai · session_01WGufzibcN9oHa8kDbTTnL5 席位核对Verdict: ACCEPT at head
|
Requested by Yibo Wang · project thread
Fixes #30051
维护者速读
scripts/codemod-split-engine.mjs改为每个 facet 只建一个共享 host(每个引擎实例一份,首次调用时由类的 static 块安装的工厂建成,存在新模块engine-facet-hosts.ts),类上的 stub 从「每次调用现建一个 host 字面量」变成一行Facet.x.call(xHost.of(this), …)。从 base 重新生成,生成物未手改。engine.ts14,140 → 13,548 行(−592)。protocol.ts不动:它的 codemod 存根本来就走共享的protocolHost(this),没有可压缩的。engine.ts(「我觉得文件还是太长,还有进一步的拆分优化空间吗」「可以后续立卡处理」),本卡是其中的 stub 压缩一项。engine.ts;打包后 6 个.d.ts与 main 逐字节相同;objectql 测试 base 547 文件/10,833 = head;回滚就是 revert。packages/objectql/src/engine-facet-hosts.ts(FacetHost缓存,78 行);②engine.ts类末尾第二个 static 块(16 个 host 工厂);③ codemod 里的hostsTexts/stubBody和verify新增的 stub 与 host 校验。pnpm install --frozen-lockfile后运行node scripts/codemod-split-engine.mjs --verify --base c2d353d2,应看到161/161 moved bodies identical、23245 -> 13548 lines,退出码 0;--self-test应通过 68 例。Clause-②: no
Metadata-protocol: none
What changes
scripts/codemod-split-engine.mjs. Each facet's host is now built ONCE per engine instead of once per call.hostsTextswrites a new module,engine-facet-hosts.ts. It holdsFacetHost, aWeakMapcache keyed by engine with a factory slot, and one exported instance per facet module (actionsHost,cascadePlanningHost, …, 16 in all). The module imports types only, so it forms no evaluation cycle and is evaluated before the class whichever module loads first. This matters because 4 facet modules importObjectQLStaticsfromengine.tsby value, so a cache inside a facet module could be in its temporal dead zone when the class's static block runs.ObjectQLinstalls each facet's factory:xHost.install((engine) => ({ … })). The factory forwards each member as the per-call literal did. A method is a closure called on the engine at call time (m: (...a) => engine.m(...a)), so a spy or subclass override installed later is still reached. A field or accessor is a getter, and a field a body assigns also gets a setter. The host is the facet's whole host interface (ActionsHostand so on), which satisfies every body'sPick. A static block adds no class member, so the.d.tsdoes not move.stubBodywritesreturn Facet.x.call(xHost.of(this), …args);(a generic method still calls its body instantiated with its own type parameters). A stub whose body reads no member is unchanged.thisbare already stays on the class (bare-this).verifynow also checks that (1) every stub equals the generated one-call stub for its method, facet and host, (2)engine-facet-hosts.tsequals the generated text for the host interfaces on disk, and (3) the install block equals the generated block. Each member is forwarded by its BASE class kind: a method as a closure, anything else as a getter, plus a setter when the interface does not mark itreadonly. The undo drops the block and the import.engine.tsalready binds a host name, or when a moving method has a parameter named after its facet's host.--self-test: 68 cases in 6 batteries. "facets, statics and docblocks" goes from 11 to 17 cases, and its floor is raised to 17. The new refusals: a stub handing another facet's host, an edited host module, and a written field's setter left out.--writeonengine.tsatc2d353d2(main's last unsplitengine.ts; nothing has touchedengine*.tssince refactor(objectql): generate the engine.ts section split with a deterministic codemod #30012 merged as237f5137), unedited. The diff:engine.ts(145 stubs lose their literal, plus the install block and one import), the newengine-facet-hosts.ts, and the header prose of the 16 facet modules (the host paragraph). No body moved.protocol.ts/scripts/codemod-split-protocol.mjs: no change. The card's premise does not hold for this file. Its 85 codemod stubs already reach their bodies through one shared host per instance,ProtocolClassHeadFacet.x(protocolHost(this), …)(protocol-host.ts, aWeakMapcache installed by the class's static block). That is the shape this PR givesengine.ts.grep -c 'const host: ThisParameterType' scripts/codemod-split-protocol.mjsreads 0. The per-call literals left inprotocol.ts(164 stubs, 774 lines) are all hand-series stubs that the codemod did not write (see Acceptance notes).Line counts
c23501b2)packages/objectql/src/engine.tspackages/objectql/src/engine-facet-hosts.tspackages/metadata-protocol/src/protocol.tsThe card estimated about 1,000 lines off
engine.ts. The measured figure is 592. The 145 per-call literals were 869 lines, and the install block adds back one line per host member (207 across 16 facets, plus 31 setters and 2 lines per facet). PR size: 847 additions + 1,149 deletions = 1,996 changed lines.Tests
Proofs (all at head 73a7605 unless stated)
node scripts/codemod-split-engine.mjs --verify --base c2d353d2: exit 0. 161/161 moved bodies identical (body, parameters, return type, async), 146/146 top-level declarations identical, every stub equals the generated one, the host module and install block are the generated ones, and the undo rebuilds baseengine.tsbyte for byte once 107 import bindings are pruned.engine.ts 23245 -> 13548 lines.--writeruns from baseengine.ts(sha256dc5677f3…), one in a separate worktree, print the same output tree sha25609c80319211a965b89820ab7bb650d74cf6aaaec0afc0afb9cd75f2b867e3554. The second worktree's regenerated tree equals the committed head (git statusempty)..d.ts:packages/objectql/distbuilt at main (c23501b2) ×2 and at head ×2. All 6 declaration files (index,core,util-BWl1U61F, each.d.ts+.d.mts) are byte-identical base vs head. The public-surface reading (pubsurface.mjsfrom Codemod split: generate the objectql engine.ts split with a deterministic script, verified byte-identical #29986) passes base1=base2, head1=head2, base1=head1 and base2=head2, and its controls behave (a private member made public is caught, a public type change is caught, a private docblock edit is not).test: base (mainc23501b2) 547 files / 10,833 passed = head 547 / 10,833.test:repo: 1 file / 5 = 5.typecheck(tsc + check:test-typecheck): exit 0.index.mjsand CJSindex.js): two engines keep separate hosts (residualOwnerSourceset on one readsundefinedon the other), a host setter writes through (setDefaultBodyRunnerfirst-winstrue, thenfalse).node scripts/check-facade-delegation-only.mjs: exit 0, registry unchanged (2 facades, 60 exceptions). Theengine.tsdelegation kinds move from 217 host-literal stubs to 144 host delegations plus 73 host-literal stubs (the hand series). The new install block reads as a secondstatic host factory.check-durability-degradation-log-level.mjs: base (main) and head output byte-identical (51 seams, 9 propagating; 101 read seams, 13 discriminated).--self-testexit 0.Ablations (each red, then restored; file hash back to its HEAD blob)
Done through
scripts/ablation-replace.mjs.engine.tsstub pointed at another facet's host:--verify --base c2d353d2exits 1 withrecordResidualOwner: its stub is not the generated one-call stub.Acceptance notes
engine.tsthat is 73 stubs (454 lines; the [maintainer] refactor(objectql): splitsrc/engine.ts(23,903 lines, +7,501 in 30 days) along itsObjectQLseams, one move-only slice per PR #25522 hand modules). Inprotocol.tsit is 164 stubs (774 lines; the [maintainer] refactor(metadata-protocol): splitsrc/protocol.ts(33,065 lines, +10,169 in 30 days) along itsObjectStackProtocolImplementationseams, one move-only slice per PR #25467 hand modules). The codemods do not write those stubs, and this card's ruling is codemod-only. The same mechanism would apply (route them through the facet hosts orprotocolHost, after wideningProtocolHost). By arithmetic, not a generated run, that is about −400 lines inengine.tsand about −700 inprotocol.ts. Carrier: the seat, if it wants a card.skip-changeset): the behaviour and the published declaration files do not change.Verification
Local, at head
73a7605b:npx turbo run build --concurrency=2: 120/120 tasks, exit 0.pnpm lint: exit 0. objectqltypecheck: exit 0.node scripts/pm/dispatch-gates.mjs --commands: 64 families derived, each run, all exit 0. Three exited 3 on the first sweep because a prerequisite was missing (check-plugin-teardown-shape --self-testneeds the pinned fixture commit fetched;check:dual-build-cjs-loadsandcheck:lean-entry-closureneed the full build). Each passed when rerun after its prerequisite was in place.--ran: 64 derived, 64 run, 0 NOT-MEASURED, 0 UNRUN.check-facade-delegation-only.mjs(exit 0, registry unchanged),check-durability-degradation-log-level.mjs(base = head output),codemod-split-engine.mjs --self-test(68 cases).test:repo, and dogfood (the title has no!and there is no narrowing arm).Human Minimum
packages/specholds no runtime logic. (not touched)pnpm lintpasses.main, one pull request.skip-changeset, see above)packages/spectouched: its generated artifacts are regenerated and committed. (not touched)content/docs/references/,packages/*/CHANGELOG.mdorcontent/docs/releases/.Generated by Claude Code