Skip to content

fix(objectql,spec): filter folds to where on EVERY engine method; top/limit joins the #3795 slot table (#4346) - #4356

Merged
os-zhuang merged 1 commit into
mainfrom
claude/filter-folds-engine-find-d26aa0
Jul 31, 2026
Merged

fix(objectql,spec): filter folds to where on EVERY engine method; top/limit joins the #3795 slot table (#4346)#4356
os-zhuang merged 1 commit into
mainfrom
claude/filter-folds-engine-find-d26aa0

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Closes #4346. Refs #3795, #4181, #3713, ADR-0057.

问题

filterwhere 的别名折叠在引擎层只存在于六个方法中的一个。ObjectQL.find() 折叠(注释还写明了后果),findOne/count/update/delete/aggregate 原样透传 —— ast.where === undefined,每个 driver 都读作"无谓词":

调用 修复前 修复后
find({filter: {status:'done'}}) [b, c] ✅(唯一折叠的) 不变
findOne({filter: {status:'done'}}) 表首行(任意行) 匹配行
count({filter}) 整表计数 匹配计数
update(data, {filter, multi:true}) 全表改写 匹配行
delete({filter, multi:true}) 清空整表 匹配行
aggregate({filter, …}) 聚合全表 匹配行

可达且真实:废弃的 DataEngine{Query,Update,Delete,Count,Aggregate}OptionsSchema 都声明 filter 为合法选项,ScopedContext/ObjectRepository(交给 L2 hook 体的跨对象 API)原样转发,spec 自己的 hook 文档教的就是这个坏调用。仓内也有真实受害者:auth-manager.tsstampIdentitySourcefindOne({filter: {id}})(拿到的是表首行)和 count({filter: …})(数的是整表)—— 本修复使这两处回归声明语义。

修复

一次折叠,三个读者。 每个引擎入口经由 #3795 的既有机制(RPC_QUERY_ALIAS_SLOTS + foldQueryAliasSlots)折叠,替换 find 手写的守卫,不再新增开写副本:别名单独出现即折入规范键、相同值冗余坍缩、不同值抛错("Send exactly one",#4181 规则)、显式 null 别名视为撤回。写路径在 withResolvedWhere 之前折叠,folded filter 里的 {filter-placeholder} token 同样解析(#3810 语义对齐)。

第六对别名。 toplimit 加入 RPC_QUERY_ALIAS_SLOTS(#3795 的 scope note 把它排除在外)。协议层原来是反向折叠(options.limit = Number(options.top) —— 别名覆盖规范键),引擎层是规范键优先,{top: 1, limit: 3} 经 HTTP 得 1、进程内得 3。现在 wire 归一化、RPC parse、引擎三个读者语义一致:top 单独出现仍限行,冲突拒绝。

文档。 hook.zod.ts 的 hook 示例与 engine.tscreateContext 示例改教 where;check:docs 通过(无生成物漂移)。

针脚(pin)

  • engine-filter-alias.test.ts(新,真实引擎 + 内存 driver,issue 的三行数据集):六方法 {filter} 单独生效、六方法 {filter, where} 冲突拒绝且写路径确认未落盘、相同值坍缩、null 撤回、{top} 限行、{top, limit} 冲突拒绝/相同坍缩、findOne({top}) 仍单行、{filter: {id}} 走 by-id 快径、ScopedContext/ObjectRepository 钩子门面路径(文档教的那个调用)。
  • protocol-data.test.ts:SLOTS 表加 ['top','limit',5,3,5] 行,自动获得 alias-alone / conflict / identical 三组针脚。
  • data-engine.test.ts(spec):RPC parse 折叠 toplimit 并丢弃别名;冲突用例加 {top:1, limit:3}

验证

  • spec 7156 ✅ / objectql 1418(88 文件)✅ / metadata-protocol 136 ✅ / rest 523 ✅ / runtime 951 ✅,受影响包 typecheck ✅,check:docs ✅,全仓 pnpm test@objectstack/dogfood 的 2FA lockout 用例 5s 超时外全绿 —— 该用例与本改动无交集(better-auth 2FA 路径全程 where),单文件复跑 5/5 绿,符合本机 turbo 并行资源噪音的已知模式。

行为变化说明

之前各层各自"选赢家"的冲突拼写({where: X, filter: Y}{top: 1, limit: 3})现在在每条路径上一致地大声拒绝,不再按层静默取不同值。

🤖 Generated with Claude Code

…/limit joins the #3795 slot table (#4346)

The filter → where fold that #3795 settled at the protocol layer existed at
the engine layer in exactly one of six methods. find() folded it (with a
comment saying why); findOne/count/update/delete/aggregate passed the option
bag through with ast.where === undefined, which every driver reads as "no
predicate" — so a caller filtering with { filter } silently matched EVERY
row: an over-grant on the reads, an unbounded write on update/delete
(multi: true rewrote or emptied the whole table). The deprecated
DataEngine*OptionsSchema contracts declare filter on all of them, the
ScopedContext/ObjectRepository facade forwards hook-body arguments verbatim,
and the spec's own hook docs taught the broken call — auth-manager's
stampIdentitySource was a live in-repo victim (findOne({filter}) read the
table's first row; count({filter}) counted the whole table).

Every entry point now folds through the spec's own machinery
(RPC_QUERY_ALIAS_SLOTS + foldQueryAliasSlots) instead of find's hand-rolled
copy, under the #4181 rule: alias alone folds, identical duplicates
collapse, different values throw, an explicit null alias is a withdrawal.
Write paths fold BEFORE withResolvedWhere, so filter-carried placeholder
tokens resolve too (#3810 parity).

top → limit — the pair the #3795 scope note excluded — joins the slot
table. The protocol normalizer folded it BACKWARDS (options.limit =
Number(options.top): alias overwrote canonical) while engine.find folded it
canonical-wins, so {top: 1, limit: 3} answered 1 over HTTP and 3 in-process.
All three readers (wire, RPC parse, engine) now agree: top alone still
limits, a conflicting pair is refused. The hook.zod.ts / createContext doc
examples teach where now.

Pinned per method with a real engine + in-memory driver, write paths
included (nothing lands before the refusal); the protocol SLOTS table and
the RPC parse pins gained the sixth pair.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 31, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Jul 31, 2026 8:38am

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 3 package(s): @objectstack/metadata-protocol, @objectstack/objectql, @objectstack/spec.

110 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/agents.mdx (via @objectstack/spec)
  • content/docs/ai/skills-reference.mdx (via @objectstack/spec)
  • content/docs/ai/skills.mdx (via @objectstack/spec)
  • content/docs/api/client-sdk.mdx (via @objectstack/spec)
  • content/docs/api/environment-routing.mdx (via @objectstack/spec)
  • content/docs/api/error-catalog.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-client.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx (via @objectstack/spec)
  • content/docs/api/index.mdx (via @objectstack/spec)
  • content/docs/automation/approvals.mdx (via @objectstack/spec)
  • content/docs/automation/connectors.mdx (via @objectstack/spec)
  • content/docs/automation/flows.mdx (via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx (via packages/spec)
  • content/docs/automation/hooks.mdx (via @objectstack/spec)
  • content/docs/automation/index.mdx (via @objectstack/spec)
  • content/docs/automation/webhooks.mdx (via @objectstack/spec)
  • content/docs/automation/workflows.mdx (via @objectstack/spec)
  • content/docs/concepts/architecture.mdx (via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx (via packages/spec)
  • content/docs/concepts/index.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx (via @objectstack/metadata-protocol, @objectstack/objectql, packages/spec)
  • content/docs/concepts/north-star.mdx (via packages/spec)
  • content/docs/data-modeling/analytics.mdx (via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx (via @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx (via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx (via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx (via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx (via packages/objectql, @objectstack/spec)
  • content/docs/data-modeling/index.mdx (via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx (via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx (via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx (via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx (via @objectstack/spec)
  • content/docs/deployment/cli.mdx (via @objectstack/spec)
  • content/docs/deployment/migration-from-objectql.mdx (via @objectstack/objectql)
  • content/docs/deployment/troubleshooting.mdx (via @objectstack/spec)
  • content/docs/deployment/validating-metadata.mdx (via @objectstack/spec)
  • content/docs/deployment/vercel.mdx (via @objectstack/objectql)
  • content/docs/getting-started/build-with-claude-code.mdx (via @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx (via @objectstack/spec)
  • content/docs/getting-started/examples.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-reference.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx (via @objectstack/spec)
  • content/docs/getting-started/your-first-project.mdx (via @objectstack/spec)
  • content/docs/kernel/cluster.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx (via packages/spec)
  • content/docs/kernel/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/email-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sharing-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx (via packages/spec)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/metadata-protocol, @objectstack/objectql, @objectstack/spec)
  • content/docs/kernel/services.mdx (via @objectstack/objectql, @objectstack/spec)
  • content/docs/permissions/authentication.mdx (via @objectstack/objectql)
  • content/docs/permissions/authorization.mdx (via @objectstack/spec)
  • content/docs/permissions/permission-sets.mdx (via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx (via @objectstack/spec)
  • content/docs/permissions/positions.mdx (via @objectstack/spec)
  • content/docs/permissions/rls.mdx (via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx (via @objectstack/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx (via @objectstack/spec)
  • content/docs/plugins/development.mdx (via @objectstack/spec)
  • content/docs/plugins/index.mdx (via @objectstack/objectql, @objectstack/spec)
  • content/docs/plugins/packages.mdx (via @objectstack/objectql, @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx (via @objectstack/spec)
  • content/docs/protocol/diagram.mdx (via packages/spec)
  • content/docs/protocol/kernel/config-resolution.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/i18n-standard.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/objectql, @objectstack/spec)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/plugin-spec.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/runtime-capabilities.mdx (via @objectstack/spec)
  • content/docs/protocol/knowledge.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/query-syntax.mdx (via packages/objectql, @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx (via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx (via @objectstack/objectql, @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/record-alert.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx (via @objectstack/spec)
  • content/docs/releases/implementation-status.mdx (via @objectstack/objectql, @objectstack/spec)
  • content/docs/releases/index.mdx (via @objectstack/spec)
  • content/docs/releases/v12.mdx (via @objectstack/spec)
  • content/docs/releases/v13.mdx (via @objectstack/spec)
  • content/docs/releases/v16.mdx (via @objectstack/spec)
  • content/docs/releases/v17.mdx (via @objectstack/spec)
  • content/docs/releases/v9.mdx (via @objectstack/metadata-protocol, @objectstack/spec)
  • content/docs/ui/actions.mdx (via @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx (via @objectstack/spec)
  • content/docs/ui/dashboards.mdx (via @objectstack/spec)
  • content/docs/ui/forms.mdx (via @objectstack/spec)
  • content/docs/ui/index.mdx (via @objectstack/spec)
  • content/docs/ui/public-data-collection.mdx (via @objectstack/spec)
  • content/docs/ui/setup-app.mdx (via @objectstack/spec)
  • content/docs/ui/translations.mdx (via @objectstack/spec)
  • content/docs/ui/views.mdx (via @objectstack/spec)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

@github-actions github-actions Bot added documentation Improvements or additions to documentation protocol:data tests tooling labels Jul 31, 2026
@os-zhuang
os-zhuang merged commit ed77493 into main Jul 31, 2026
18 checks passed
@os-zhuang
os-zhuang deleted the claude/filter-folds-engine-find-d26aa0 branch July 31, 2026 08:52
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 protocol:data size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[P1] filter folds to where in engine.find only — findOne/count/update/delete silently match EVERY row, and the hook docs teach the broken call

1 participant