Skip to content

refactor(objectql): ObjectQL#delete's body moves into its facet (codemod R10 puts the module on the statics route) - #31764

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/project-thread-hlg5h7
Nov 9, 2026
Merged

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/project-thread-hlg5h7

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Steve · project thread

Refs #30020

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:把查询引擎 ObjectQL 里 delete(删除记录)的方法体原封不动地搬进同一节的 facet 模块 engine-delete-and-aggregate.ts,类上只留一行转调;搬移工具 codemod 补了一条把模块切到“静态成员转发”路线的规则(R10),并删掉门禁登记表里 delete 这一行例外。
  • 为什么改: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 拆分大文件 engine.ts 的既定顺序第五步(E4)。delete 体里读了一个私有静态常量,目标模块原先不走静态转发路线,工具拒绝搬;补上 R10 后由工具确定性生成,不手搬。
  • 风险与回滚:纯搬移:发布的 .d.ts 逐字节不变,同样的语句按同样顺序执行;undo 证明逐字节还原 base。风险在删除路径(级联、回收站)全部经过它,dogfood 全量已跑绿。回滚即整体 revert。
  • 先看:① packages/objectql/src/engine-delete-and-aggregate.ts 文件头的 import 对和 host 接口(R10 的改写)② scripts/codemod-split-engine.mjs 的 rerouteModule / unrouteModule ③ scripts/measure-durability-swallow-family.mjs 改指向的那一条解析控制。
  • 两分钟验证:node scripts/codemod-split-engine.mjs --members --verify --base $(git merge-base origin/main HEAD),预期打印 undoing the moves does rebuild the base engine.ts and its modules byte-for-byte,退出码 0。
  • 需要你做的:无,席位落地。

What changes

  • ObjectQL#delete moves into its facet (packages/objectql/src/engine.ts → engine-delete-and-aggregate.ts), generated by node scripts/codemod-split-engine.mjs --members delete --write on main 90cea476 (after fix(objectql): a by-id delete the driver answers as removing nothing leaves no recycle-bin entry #31702 landed, so its by-id withdrawal moves with the body). The stub keeps its docblock and signature (async dropped, as every stub of the series). The one bare this the body hands on, ql: this in the hook context, reads ql: this.facade (rule R1). The body's one static read, ObjectQL.REFERENCE_PROBE_BATCH, goes through the ObjectQLStatics forwarder that already carries it for engine-cascade-planning.ts. engine.ts 10653 → 9943 lines; 12 import bindings only the moved body read are pruned from it.
  • Codemod: R10, the statics route for a written module (scripts/codemod-split-engine.mjs). On main the fourth pass refused this move: delete reads statics of ObjectQL, and engine-delete-and-aggregate.ts does not take the statics route: this pass does not reroute a module. Now a mover that reads a non-public static puts a module the first pass wrote off the route onto it, exactly as the first pass writes a routed module: the one import type { ObjectQL } line becomes the routed pair (ObjectQLStatics AS ObjectQL, the class as ObjectQLClass), the host interface types its members by ObjectQLClass, and the header gains the statics paragraph (now one constant, STATICS_PARA, shared with the first pass's writer). It refuses any module that is not the first pass's unrouted shape, and one whose code names ObjectQL anywhere else (static-route-conflict). The undo (unrouteModule) is the inverse rewrite, applied to the head before the moved members come out. It runs only where the base module was off the route and a moved member reads a static, and it fails a kept member that reads one. Separately, the proof now fails a moved member that reads a static in a module left off the route. Before this change that case passed the undo proof while the module could not compile. A public-only static reader still refuses, as before.
  • scripts/facade-delegation-registry.json: the ObjectQL · delete row (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, bare-this) leaves; no other row moves. check:facade-delegation-only 9 → 8 named exceptions.
  • scripts/measure-durability-swallow-family.mjs (one control, seat ruling A, claim amended): the delete RESOLUTION control pinned engine.ts · ObjectQL · this.delete(), whose only call site was the after-abort re-entry inside the moved body. With the body moved, the control matched nothing and pnpm check:swallow-census-controls went red (resolution control matched NO call site). The control now pins the same re-entry where it moved: engine-delete-and-aggregate.ts · DeleteAndAggregateFacet · delete → DeleteAndAggregateFacet. Measured with the real resolver on the moved tree: this site resolves to the facet's own delete body (the body the host's delete stub reaches). The cascade re-entry in engine-cascade-planning.ts resolves to REFUSAL (no same-file body). The engine.ts cross-class collision stays pinned by the update and transaction controls.
  • Changeset .changeset/30020-objectql-delete-facet.md, @objectstack/objectql patch. The delta allowed skip-changeset when the .d.ts diff is empty, but the published JS changes (positive control below), and AGENTS.md reserves skip-changeset for a diff that publishes nothing. This is the S1 / E2 precedent.

Tests

  • New self-test battery statics route for a written module (fourth pass, #30020 R10), 18 cases, floor 18. It runs on the identity fixture with a staticReader method the first pass keeps (bare-this) in a section it writes off the route. The cases cover: the reroute plan; the proof; determinism; the routed import pair; the interface alias, including the identity port; the statics paragraph in place; reroute/unroute as inverses; five failing heads (paragraph missing, wrong import pair, a member typed by the class itself, a reroute no moved member needs, a static read left off the route); and four refusals (static-route-conflict, R4 into a rerouted module, another ./engine.js shape, an already-routed module).
  • No existing test edited. No forced test edit.
Ablations (each red, then restored: blob == HEAD, `git diff HEAD` empty)

Via node scripts/ablation-replace.mjs --file scripts/codemod-split-engine.mjs … -- node scripts/codemod-split-engine.mjs --self-test, on head:

Mutation Red cases
R10 off (generateMembers does not reroute) 7, incl. the generated move proves against its base — engine-identity.ts: a member this pass moved reads a static, and the module is off the statics route (R10)
undo guard off (a reroute no moved member needs) 1: a module rerouted though no member the pass moved reads a static fails
static-route-conflict check off 1: a module whose code names ObjectQL outside its import and host interface refuses the route
off-route static read check off 1: a moved member that reads a static in a module left off the route fails

The swallow-census control was red against the moved tree before the re-point (matched NO call site) and green after.

Acceptance notes

  • The .d.ts proof bar holds. The JS changes, as for every series PR: DeleteAndAggregateFacet.delete occurs 0× in base dist/index.mjs and 1× in head (positive control).
  • ObjectQL#update and #insert still carry their registry rows (E5, E6). Their own re-entries will meet the same census control shape when E6 moves update.
  • Open questions to the seat are in the report: the R10 rule name and its refusals.

Verification

All readings through scripts/pm/os-verify-lock.sh (VERDICT command-exit 0 each), base 90cea476, head as noted.

Run Base Head
node scripts/codemod-split-engine.mjs --members --verify --base 90cea476 — exit 0; 1 moved (delete), byte-exact, 12 bindings pruned
codemod --self-test 211 cases / 13 batteries 229 cases / 14 batteries
pnpm check:facade-delegation-only 9 exceptions 8 exceptions
pnpm --filter @objectstack/objectql typecheck — exit 0
pnpm --filter @objectstack/objectql test 574 files / 11711 tests 574 files / 11711 tests
objectql dist .d.ts (core.d.ts, core.d.mts, index.d.ts, index.d.mts) — byte-identical to base (cmp)
pnpm --filter @objectstack/dogfood test (full, no shard, head 5cf3f796) — 440 passed, 3 skipped files / 3652 passed, 41 skipped tests
eslint --no-inline-config packages/objectql + both scripts (objectql has no lint script) — exit 0
Derived gates: 83 / 83 on head 2a5a3a3, reconciled

node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at 2a5a3a3d derived 83 commands; all 83 run after a full pnpm build (lock VERDICT exit 0), each exit 0. --ran: "83 derived famil(ies) accounted for — 83 run, 0 NOT-MEASURED (a DERIVED zero)". On the first round (head 5cf3f796, before the control re-point) three gates exited 3 on container prerequisites, not findings: check-plugin-teardown-shape --self-test (shallow clone; fixture commit fetched), check:dual-build-cjs-loads and check:published-readme-exports (no full build yet). One was a real red, check:swallow-census-controls, fixed by the control re-point above. Extra: src/http-dispatcher.keys.test.ts in runtime (names the census script) 20/20; census --self-test (every family) exit 0.

test:repo was not run locally (no diff in that project); CI's Test Core carries it.


Generated by Claude Code

…le on the statics route

A body --members moves into a facet module the first pass wrote off the
statics route, and that reads a non-public static, now takes the route
the first pass would have given the module: the one type-only ObjectQL
import becomes the routed pair, the host interface types its members by
ObjectQLClass, and the header gains the statics paragraph. The undo is
the inverse rewrite under the same byte-exact proof. New self-test
battery (18 cases).

Claude-Session: https://claude.ai/code/session_01ADHkmNmrFUKGH6LP8Jj8ZV
Co-authored-by: Claude <noreply@anthropic.com>
node scripts/codemod-split-engine.mjs --members delete --write: the body
moves unedited into engine-delete-and-aggregate.ts, which R10 puts on the
statics route for its ObjectQL.REFERENCE_PROBE_BATCH read. The stub keeps
its signature; the bare this the body hands on reads this.facade (R1).
The delete row leaves scripts/facade-delegation-registry.json.

Claude-Session: https://claude.ai/code/session_01ADHkmNmrFUKGH6LP8Jj8ZV
Co-authored-by: Claude <noreply@anthropic.com>
… site into the facet

Claude-Session: https://claude.ai/code/session_01ADHkmNmrFUKGH6LP8Jj8ZV
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/xl 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 9, 2026
@github-actions

github-actions Bot commented Nov 9, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

42 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: node scripts/docs-audit/affected-docs.mjs --json 9e1f1f90d5a62c81b6d1561d4baa7c822cc88b33.

⛔ 8 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails.

What this run could not see
  • 1 cross-cutting symbol(s) contributed no route anchor: recordId (4 routes)
  • 1 anchor(s) matched too much of the corpus to be a work list: ObjectQL (symbol, 80 pages)
  • 11 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 65 of 231 client-bound route-ledger rows — the other 166 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 166: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 59 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 107 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 9e1f1f90d5a62c81b6d1561d4baa7c822cc88b33 → packageMentionDocs.

Which tree this was computed on

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

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

⚠️ 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 9e1f1f90d5a62c81b6d1561d4baa7c822cc88b33 → 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#1 · os-steve · session_01ADHkmPF1skfu2kx89BEXZi

席位 ACCEPT · head 2a5a3a3dbeeb8326ee7daa1d69c65482ccb8cc3f

#30020 §5 order E4 delete. Clause-② no, not governed (1960 lines including the generated move, under 3000). The seat reviewed it at TIER_DEFAULT.

  • The move: the delete body moves into engine-delete-and-aggregate.ts, and the stub on ObjectQL keeps its docblock and signature. ql: this → this.facade, and the one static read (REFERENCE_PROBE_BATCH) goes through the existing statics forwarder. The undo proof is byte-exact. The .d.ts of @objectstack/objectql is byte-identical. The JS positive control reads 0× on base and 1× on head. The objectql suite is 574/11711 on both base and head.
  • Codemod R10 (reroutes a module already written onto the statics route, with its inverse inside the undo proof). The self-test grows from 211 to 229 cases, and 4 ablations were all red, then restored. Its name and refusal set are accepted as they are. It needs no separate contract review: like S1's codemod changes, it is internal tooling, and the byte-exact undo proof covers its output.
  • Registry: only the ObjectQL·delete row is removed. The facade gate goes from 9 to 8 exceptions.
  • Swallow census control (seat ruling A, and the claim was restated to widen the surface): the control row now points at engine-delete-and-aggregate.ts · DeleteAndAggregateFacet · delete → DeleteAndAggregateFacet, and its why says the call site moved with its body. The thread measured that the resolver resolves this call to the facet's own body, and the cascade-planning re-entry to REFUSAL. The ObjectRepository collision stays pinned by the update / transaction controls.
  • Changeset: patch instead of skip-changeset, because the JS changes, as with S1/E2.
  • Gate reconcile: 83/83, 0 NOT-MEASURED. Dogfood was measured on 5cf3f79; the last commit only touches the census script.

Uses Refs #30020; the series continues with E5 insert. Landing: once CI is all green, check K, then relay pr_ready + automerge_enable.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review November 9, 2026 03:39
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge November 9, 2026 03:39
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Nov 9, 2026
Merged via the queue into main with commit 391d748 Nov 9, 2026
51 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-hlg5h7 branch November 9, 2026 04:17
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