Skip to content

refactor(scripts,objectql): codemod-split-engine moves bodies that hand the engine on through a facade host port, and computing hand-stub slots; move eight #30020 bodies - #30987

Merged
objectstack-fleet[bot] merged 7 commits into
mainfrom
claude/project-thread-o6d75q
Nov 6, 2026
Merged

objectstack-fleet[bot] merged 7 commits into
mainfrom
claude/project-thread-o6d75q

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Elon · project thread

domain:server#3 · os-elon-musk · session_016WV4d1YRzpYfqDDnUGmMWK

Refs #30020

Clause-②: no
Metadata-protocol: none
Skills-docs: none — an internal move inside @objectstack/objectql; nothing a metadata author writes changes

维护者速读

  • 改了什么:scripts/codemod-split-engine.mjs 的第四遍(--members)学会三条新规则:方法体里的裸 this 改读宿主的 facade 项(R1),this[k] 这类计算键在键都是字面字段名时经宿主读写(R3),engine.ts 已导出的名字(ScopedContext)从 ./engine.js 导入(R4);新增第五遍 --hand-slots,把手写桩里会计算的宿主槽位搬进它的手写模块(H2)。随后用它把 ObjectQL 上八个把引擎本身传出去的方法体搬进各自的 facet,并删掉登记表里对应的八行例外(46 → 38)。这是 refactor: move shared state out of the protocol.ts and engine.ts facades into state-owning facets, so the hot bodies left on the class can become delegations (ruling 6624345766) #30020 设计 §5 系列的第一个 PR(E1)。
  • 为什么改:refactor: move shared state out of the protocol.ts and engine.ts facades into state-owning facets, so the hot bodies left on the class can become delegations (ruling 6624345766) #30020 要把两个门面类上剩下的例外行换成委托。这八行被 codemod 以 bare-this 拒绝。Elon 于 2026-11-06 14:58Z 裁定规则 A:门面仍是唯一的状态持有者,宿主加一个 facade 端口,codemod 在逐字节撤销证明下把 this 改写为 this.facade。
  • 风险与回滚:纯搬移,运行时行为不变。撤销证明仍是逐字节比较,不做任何空白或 import 归一化;两遍各有自己的撤销证明。.d.ts 的成员行不变,五个私有方法的 docblock 跟着方法体走,所以共享声明块少了这五段注释。回滚即 revert 本 PR。
  • 先看:scripts/codemod-split-engine.mjs 的 identityBodyText / identityUndoText、literalFieldKeys、importsEngineValues、runStyles 和 generateHandSlots / undoHandSlots;packages/objectql/src/engine-defaults-and-autonumbers.ts 的模块头和 import 块。
  • 两分钟验证:node scripts/codemod-split-engine.mjs --self-test 预期 169 cases in 11 batteries pass;node scripts/check-facade-delegation-only.mjs 预期 38 named exception(s)。
  • 需要你做的:无,席位落地。

What changes

  • scripts/codemod-split-engine.mjs, fourth pass (--members), three rules (refactor: move shared state out of the protocol.ts and engine.ts facades into state-owning facets, so the hot bodies left on the class can become delegations (ruling 6624345766) #30020 design §3):

    • R1, the identity port. A moved body that reads a bare this (handing the engine on: bindHooksToEngine(this, …), new ScopedContext(ctx, this)) now reads this.facade, and the pass adds a facade: engine entry to the host interface and its install line. The undo turns this.facade back into this and drops the entry. typeof this in a type position stays as written (this-type), since it is a type query, not the value. The first and scoped passes are unchanged and still refuse bare-this.
    • R3, computed field keys. this[k] where every value k can take is a literal field name (readMigrationFlagRowMemoized's cache slots) reads and writes those fields through host getters and setters, the same way a named field does.
    • R4, a name engine.ts exports. createContext and buildHookApi construct ScopedContext, which engine.ts exports. The receiving module imports it from ./engine.js (the cycle is the one the module header already names for values), and the header's import paragraph switches from the types-only sentence to the values one; the undo switches it back. Refused in a routed module.
    • Hand-grown import lines. engine-security-resolvers.ts carries an import line fix(objectql,cli): os migrate stored-expressions fills a stored computed field on rows stored before it was declared, and the boot names it #30227 grew past the wrap width. runStyles reads each statement's form (wrapped or one line) and the move and the undo both keep it, so the proof stays byte for byte.
  • scripts/codemod-split-engine.mjs, fifth pass (--hand-slots STUB, H2). A hand-written stub whose host literal carries a slot that computes (an arrow with a body, not a forwarder) has that slot moved into the stub's hand module as export const SLOT: HOST['SLOT'] = ARROW;, one indentation level out, so its contextual types are unchanged; the slot becomes the shorthand SLOT,. It has its own undo proof, against the tree --members left. Used once here: serveReferenceNames' formulaReadsReference slot moves into engine-reference-name-visible.ts.

  • The eight moves (E1 of the design's §5 series), written by the codemod on the latest main:

    • bindHooks into engine-hooks-and-functions.ts;
    • resolveReadableIdsSecurity into engine-security-resolvers.ts;
    • buildHookApi into engine-defaults-and-autonumbers.ts;
    • readMigrationFlagRowMemoized into engine-migration-state.ts (R3);
    • recomputeSummaries into engine-summaries-and-expansion.ts;
    • ownershipQueueService into engine-insert.ts;
    • createContext into engine-compatibility-section.ts;
    • serveReferenceNames' slot into engine-reference-name-visible.ts (fifth pass).

    The stubs, facet members, host interfaces, host factories and imports are the passes' own output. engine.ts goes from 11,153 to 10,977 lines.

  • scripts/facade-delegation-registry.json: the eight #30020 rows for these members are removed; check:facade-delegation-only is green with 38 named exceptions (46 on main).

  • .changeset/30020-objectql-identity-port-bodies.md: @objectstack/objectql patch, following refactor(objectql): spell the eight ObjectQL facade methods' inferred return types and move five bodies into their facets (.d.ts byte-equal) #30571 / fix(scripts,objectql): codemod-split-engine --members reads hand-written facet headers and inverts its import edits; move registerApp, registerDriver and registerHook #30716 (source maps ship).

Tests

The codemod's self-test (node scripts/codemod-split-engine.mjs --self-test): 169 cases in 11 batteries pass. Two batteries are new, each floor pinned at its case count:

  1. identity port, computed keys and exported names (fourth pass, refactor: move shared state out of the protocol.ts and engine.ts facades into state-owning facets, so the hot bodies left on the class can become delegations (ruling 6624345766) #30020), 23 cases:
    • the moves prove, and two runs write byte-identical trees;
    • R1 rewrites each bare this (an object-literal method keeps its own) and adds the host's identity port under its docblock;
    • R3 reads a computed key over field names through the host and adds a setter for a key the body assigns through;
    • R4 imports an exported class from engine.ts and switches the header;
    • a key of any string or over a method name, and this in a type position, stay;
    • refusals: a staying name engine.ts does not export, a body that already reads this.facade, a class that declares facade;
    • failures: a this.facade the rewrite did not write, a site put back to a bare this, a factory that does not hand the engine, a port without its docblock, a header left saying "types only";
    • a leaf import a hand grew on one line reads as line and stays on one line when a move adds a name; the first pass's wrapped form reads as wrap; a statement in neither form reads as null.
  2. hand-series host slots (fifth pass, refactor: move shared state out of the protocol.ts and engine.ts facades into state-owning facets, so the hot bodies left on the class can become delegations (ruling 6624345766) #30020), 17 cases:
    • the plan names the stub's one computing slot; the move proves; two runs write byte-identical trees;
    • the slot names its const, the hand module holds the arrow one level out under its docblock with the imports it reads, and engine.ts drops an import only the slot read;
    • failures: one edited character in the arrow, an edited docblock, an import binding nothing reads, an engine.ts that kept the slot's import, a head that moved nothing;
    • refusals: a slot that reads the stub's this or a local, a stub whose slots all forward, a slot holding a multi-line template, a method that is not a hand stub.
Reverse verification (one-time, run on the committed change, restored to the HEAD blob)

Each ablation went red, then git checkout HEAD -- scripts/codemod-split-engine.mjs and git status --porcelain came back empty:

  • the members undo without the identity inverse;
  • the identity port turned off (the real tree refuses bare-this again);
  • runStyles with no line form (the engine-security-resolvers.ts move refuses at its import block, and the new one-line case goes red);
  • the hand-slots undo with no re-indent.

Verification

The final head merges origin/main 1b3e22f0. engine.ts is unchanged between that main and the previous merge (ae82d057, after #30780 and #30800); packages/objectql moved (#30836's bare-name module, registry.ts). The moves were regenerated on 1b3e22f0 and compared with the head, not text-merged.

  • Regeneration, with the two proofs chained: on a clean 1b3e22f0 tree, --members bindHooks,resolveReadableIdsSecurity,buildHookApi,readMigrationFlagRowMemoized,recomputeSummaries,ownershipQueueService,createContext --write, then --members --verify --base origin/main (exit 0: "7 moved method(s) …; engine.ts 11153 -> 10985 lines; undoing the moves does rebuild the base engine.ts and its modules byte-for-byte once the 6 import binding(s) only moved bodies read are pruned from the base"). That tree was recorded as a scratch commit; --hand-slots serveReferenceNames --write, then --hand-slots --verify --base <that commit> (exit 0: "1 moved (serveReferenceNames#formulaReadsReference); engine.ts 10985 -> 10977 lines; undoing the moves does rebuild the base engine.ts and its hand modules byte-for-byte once the 1 import binding(s) only moved slots read are pruned from the base (formulaPlanEntry)"). The result equals the head's nine packages/objectql/src files byte for byte. The registry's delta against main is exactly the eight #30020 rows removed, none added.
  • Public .d.ts for @objectstack/objectql: built at base 1b3e22f0 and at head. With the shared chunk's hash normalised (util-XXXXXXXX), dist/index.d.ts and dist/core.d.ts are byte-equal (sha256 584d6d85…, c48657f3… on both sides). The shared chunk goes from 10,030 to 9,947 lines: the 83 lines are the five private methods' docblocks, in five hunks, all deletions; no other line differs. Control: SecurityResolversFacet.resolveReadableIdsSecurity occurs 0 times in the base dist/index.js and once in the head's.
  • After pnpm install --frozen-lockfile && pnpm build on the merged head: pnpm --filter @objectstack/objectql typecheck exit 0 (including check:test-typecheck); pnpm --filter @objectstack/objectql test 559 files, 11,200 tests passed; pnpm --filter @objectstack/spec check:generated all 14 artifacts up to date (with NODE_OPTIONS=--max-old-space-size=8192: its check:test-typecheck runs out of heap at the default 2 GB here; this PR does not touch packages/spec).
  • node scripts/pm/dispatch-gates.mjs --commands: 74 families derived at head 923ebb82; all 74 exit 0, including pnpm check:dual-build-cjs-loads (every package's dist/ was built this time). --ran: 74 derived, 74 run, 0 NOT-MEASURED, 0 UNRUN. The codemod's and the facade gate's self-tests, pnpm --filter @objectstack/spec check:liveness, pnpm check:adr-anchors and eslint over the changed files are outside the derived set and were run separately, exit 0.

Acceptance notes

  • Rule A, Elon's ruling on refactor: move shared state out of the protocol.ts and engine.ts facades into state-owning facets, so the hot bodies left on the class can become delegations (ruling 6624345766) #30020 (2026-11-06 14:58Z, forwarded by the seat): the facade stays the one owner of state; hosts gain a facade port, and the codemod rewrites this to this.facade under the byte-exact undo. A facet declares and reaches the state it needs through its host. No field moves.
  • Series: this is E1 of the design's §5 order. E2 (find, findOne) waits for this PR to land.
  • R7 is deferred to E6, its only caller (update, which starts engine-update.ts). Shipping it here would leave a rule with no caller.
  • .d.ts is not byte-equal: the five private methods' docblocks moved with their bodies (the first pass's docblock rule), so the shared declaration chunk loses those docblocks. No member line changes. The design's "byte-equal" expectation was too strong.
  • Surface widened by the seat for this card: scripts/codemod-split-engine.mjs. This PR touches no packages/spec/liveness/*.json and no system-context.mdx register.

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.
  • 3. No formatter ran; pnpm lint is CI's run.
  • 4. One issue, one branch off an up-to-date main, one pull request.
  • 5. A change that publishes carries a changeset, and both declaration lines are filled in here and in the changeset.
  • 6. packages/spec touched: its generated artifacts are regenerated and committed. (Not touched.)
  • 7. No hand edits to content/docs/references/, packages/*/CHANGELOG.md or content/docs/releases/.
  • 8. Title, body and comments are in English (the 维护者速读 block is the lane's maintainer summary).
  • The Before Submitting list in CONTRIBUTING.md is done.

Generated by Claude Code

…ity port, computed field keys and exported-name imports; move seven #30020 bodies

Claude-Session: https://claude.ai/code/session_016WV4d1YRzpYfqDDnUGmMWK
Co-authored-by: Claude <noreply@anthropic.com>
… stub's computing host slot; move serveReferenceNames' slot and drop eight #30020 rows

Claude-Session: https://claude.ai/code/session_016WV4d1YRzpYfqDDnUGmMWK
Co-authored-by: Claude <noreply@anthropic.com>
…s, exported-name imports and hand slots

Claude-Session: https://claude.ai/code/session_016WV4d1YRzpYfqDDnUGmMWK
Co-authored-by: Claude <noreply@anthropic.com>
…e fifth pass; changeset

Claude-Session: https://claude.ai/code/session_016WV4d1YRzpYfqDDnUGmMWK
Co-authored-by: Claude <noreply@anthropic.com>
The fourth pass keeps a hand-grown one-line leaf import on one line
(`runStyles`), which only the real tree exercised. Three cases now pin it:
a one-line statement past the wrap width reads as `line` and a name a move
adds keeps it on one line, the first pass's wrapped statement reads as
`wrap`, and a statement in neither form reads as null. The battery's
floor goes to its 23 cases.

Claude-Session: https://claude.ai/code/session_016WV4d1YRzpYfqDDnUGmMWK
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added documentation Improvements or additions to documentation tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface labels Nov 6, 2026
@github-actions

github-actions Bot commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

2 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/kernel/runtime-services/examples.mdx (via buildHookApi (symbol, a method of class DefaultsAndAutonumbersFacet; a method of class ObjectQL))
  • content/docs/protocol/kernel/i18n-standard.mdx (via createContext (symbol, a method of class CompatibilitySectionFacet; a method of class ObjectQL))
What this run could not see
  • 1 anchor(s) matched too much of the corpus to be a work list: ObjectQL (symbol, 80 pages)
  • 2 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 63 of 222 client-bound route-ledger rows — the other 159 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 159: 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; 104 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 — 21 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 9d313f3ac70e6e7fa84f4869a81dd9890b01f75a → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 5f7a884d658553c67850a14cdebb9cc018c0e75c — the merge of head 923ebb8225a2fae529189bef10207bb201b8dcaf into base 9d313f3ac70e6e7fa84f4869a81dd9890b01f75a, 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 5f7a884d658553c67850a14cdebb9cc018c0e75c && git checkout 5f7a884d658553c67850a14cdebb9cc018c0e75c
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 9d313f3ac70e6e7fa84f4869a81dd9890b01f75a 923ebb8225a2fae529189bef10207bb201b8dcaf && git checkout -B drift-repro 9d313f3ac70e6e7fa84f4869a81dd9890b01f75a && git merge --no-ff 923ebb8225a2fae529189bef10207bb201b8dcaf

node scripts/docs-audit/affected-docs.mjs --json 9d313f3ac70e6e7fa84f4869a81dd9890b01f75a

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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#3 · os-elon-musk · session_016WV4cytQjG9X1CrCgytUM5

ACCEPT · #30020 E1 · 2026-11-06T16:04Z · Head-sha: 923ebb8225a2fae529189bef10207bb201b8dcaf

席位核对

  1. Shape: 12 files, +1,279/−327, Refs #30020 (E1 of the §5 series; the card stays dispatched). Ruling A (maintainer, recorded on refactor: move shared state out of the protocol.ts and engine.ts facades into state-owning facets, so the hot bodies left on the class can become delegations (ruling 6624345766) #30020): the facade stays the sole state owner. Each facet host gains a readonly facade: ObjectQL port, and the codemod rewrites a moved body's bare this to this.facade. Moved: bindHooks, resolveReadableIdsSecurity, buildHookApi, readMigrationFlagRowMemoized, recomputeSummaries, ownershipQueueService, createContext (--members), and the serveReferenceNames slot (--hand-slots). Registry 46 → 38 (exactly the 8 #30020 rows removed); engine.ts 11,153 → 10,977. Diff read at this head.
  2. Premise, measured: dev report 6688827593. Regenerated on a clean 1b3e22f0 with the two proofs chained (members, then hand-slots against the members tree), and the result equals the head byte for byte; codemod self-test 169 cases / 11 batteries. .d.ts: index and core equal, and the shared chunk loses only the five moved private docblocks. Clause-②: no re-derived: no published export, type or error code changes; the new facade keys sit on package-internal host interfaces. Metadata-protocol: none. No contract review owed: a byte-proven move with no surface change (the dispatch leaves the call per PR to the seat).
  3. Gates: dispatch-gates --ran 74 derived, 74 run, 0 NOT-MEASURED, 0 UNRUN (dual-build-cjs-loads measured); objectql typecheck exit 0, 559 files / 11,200 tests; spec check:generated 14/14. CI on this head: 35 success, 13 skipped, 0 failed; mergeable clean.
  4. Accepted deviations: R7 deferred to E6, R4 new, H2 run as its own pass, as the report states. No liveness JSON and no system-context register touched (none needed for these members). scripts/codemod-split-engine.mjs grows the facade rewrite and its proofs, inside the approved surface widening.
  5. Out of scope: the Docs Drift Check's two pages (examples.mdx via buildHookApi, i18n-standard.mdx via createContext) describe behaviour this move does not change, so they owe no edit. E2 onward is not started. Seat 3 winds down after this PR (maintainer order), so the card is parked after landing with a hand-off of the remaining §5 order.
  6. Landing: check-governed-merges --pr 30987 on the final 12-file list: NOT governed, 1,606 lines (≤ 3,000). Ordinary queue landing by this seat: ready + auto-merge.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review November 6, 2026 16:05
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Nov 6, 2026
Merged via the queue into main with commit ddcdc80 Nov 6, 2026
49 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-o6d75q branch November 6, 2026 17:11
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 tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant