Skip to content

fix(formula,service-analytics): 类型盲后端把 Date 与线上文本比出错——storage-form 轴指过去当场测出两个真缺陷 (#4191) - #4223

Merged
os-zhuang merged 2 commits into
mainfrom
claude/storage-form-time-locking-xkcdgc
Jul 30, 2026
Merged

fix(formula,service-analytics): 类型盲后端把 Date 与线上文本比出错——storage-form 轴指过去当场测出两个真缺陷 (#4191)#4223
os-zhuang merged 2 commits into
mainfrom
claude/storage-form-time-locking-xkcdgc

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

#4191 storage-form 轴的第二轮(首轮 #4205 已合,封了 SQL 系四格 + cloud#938 的 turso 四格)。本轮按优先级往下走,在两个类型盲后端上把轴测成了真缺陷

1. driver-memory:D-E3 的 schema-到达收敛,此前零覆盖

D-E3 承诺该驱动在 schema 到达时收敛表里已有的行initialData 夹具与持久化恢复,两者都发生在 schema 声明之前)。此前每条 sweep 都经 create() 播种——唯一不可能产出 pre-convention 形态的路径,所以这个承诺从未被断言过。

新增两条 describe:经 initialData 塞入原始 pre-#4047 形态(native → JS Datewire → zone-naive 文本;time 孪生 → epoch 日 Date 与全量时间戳),让 syncSchema 跑追溯 pass,再跑共享表;各带一条收敛后取值的前提断言,杜绝空转绿。结论:D-E3 的承诺成立,现在有锁了。

2. formula 的 RLS 写侧 check:10/16 发散,方向是拒写

post-image 是调用方原始写入载荷plugin-security{ ...opCtx.data },早于任何驱动 formatInput),所以 SDK 侧 create({ signed_on: new Date() }) 直接把 Date 交给 matchesFilterCondition;反向配对同样成立——CEL 的 today() 按 D1 lower 成 Date,撞上持有规范文本的记录。

JS 关系运算符排不了这个序:< 一族按 hint number 强制转换,Date 变 epoch、字符串变 NaN每个比较恒 false。实测 16 个共享用例中 10 个丢光全部 Date 值行。fail-closed 把它变成拒写——#4047 的写侧孪生,正是 D-D2 为裸日期上界记录过的那个失败方向。

3. analytics preview evaluator:同样 10/16,且双向

同一配对(mongo 的 datetime canon 就是 BSON Date,D-E2),但 compare 回退到 String(value)'Mon Jul 27 2026 …' 字典序排在每个 '2026-…' comparand 之后,于是窗口既丢行又多收行$gte 那格多返回了 b_prev/h_leap)。草稿图表因此显示了发布后会变的数字——恰恰是 preview 存在的意义(连续性)被打破。

修法

utcInstantMsspec/data/calendar-day.ts,与 nextUtcCalendarDay 并列(core 再导出)——一处定义供两个后端用,即 D-D2 对同一问题采用的纪律,而不是各留一份私有副本。它只接受无歧义拼写(Date / epoch ms / 裸 YYYY-MM-DD / 带或不带显式时区的 ISO 时间戳,后者按 D-B2 视为 UTC),因此答案不依赖进程时区——这一点对跑在 America/New_York 下的 Temporal Conformance job 是硬要求。

刻意收窄:提升只在「一侧是 Date 且两侧都能读成 instant」时触发,所以此前能工作的比较逐字节不变(string-vs-string 保持 ISO 字典序,number 保持数值),安全姿态不会比操作数本身所能证成的更宽。裸墙钟返回 null——Field.time 不是 instant(#2004),类型盲面必须原样放过而不是替它编一个日历日。

覆盖与验证

两个消费者各加一条 native-writer sweep(外加 formula 的镜像配对断言)。spec 模块文档新增「What the axis means on a TYPE-BLIND backend」一节,写明为何 date 列与整张 Field.time 表按既有 scope rule($gt-on-datetime 那条)留在轴外。

套件 结果
formula 全量 315 passed
service-analytics 全量 476 passed
driver-memory 全量 260 passed
plugin-security 全量(RLS check 真实调用方) 677 passed
core 全量 437 passed
spec check:docs / check:api-surface 绿(api-surface diff 仅 utcInstantMs 一行,已 regen 提交)

service-analyticstsc --noEmit 有 5 行报错,stash 后逐字相同——改动前既有,不在本 PR 触及的文件里。

driver-mongodb:本轮不动,已立项 #4220

#4191 点名最尖的一格。本环境跑不了真实 MongoDB(出站代理对 fastdl.mongodb.org 返回 403 policy denial,mongodb-memory-server 误报成「版本-平台组合不存在」;本机无 docker 守护进程),而该驱动既无读修复也无 backfillsupports.migrations: false,已逐一 grep 确认无 $expr/$convert/repair 路径)——D-E3 有意不做 boot-step backfill,但它指向的「until migrated」那个迁移并不存在。

因此遗留 sweep 必然红、修复必须盲写安全相关的驱动改动。按「不做未验证的架构改动、不为未交付能力做假覆盖」立项 #4220,写明预期机理、两条候选修法($expr 读修复 vs 显式迁移命令,倾向二者并存以与 SQLite 对称)与验收规则。

关联

#4191(本体)、#4205(首轮)、cloud#938、#4220(mongo)、#4203check:generated 台账,main 上即红);ADR-0053 D-A3 / D-D2 / D-E2 / D-E3。

🤖 Generated with Claude Code

https://claude.ai/code/session_01SWHvwFgUU2U8M9nbV1g1iQ


Generated by Claude Code

claude added 2 commits July 30, 2026 16:13
…rage-form axis (#4191)

D-E3 promises this driver converges the rows ALREADY in a table when the
schema arrives — the population initialData fixtures and persistence
restores create, both landing before any schema is declared. Nothing
asserted it: every existing sweep seeds through create(), which is the
one path that cannot produce a pre-convention form.

Two describes seed the raw pre-#4047 forms via initialData (native → a
JS Date, wire → zone-naive text; the time twin → an epoch-day Date and a
full timestamp), let syncSchema run its retroactive pass, then run the
shared tables. Each carries a premise assertion on the converged values
so the sweep cannot pass vacuously.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SWHvwFgUU2U8M9nbV1g1iQ
…ext on the type-blind filter backends (#4191)

Pointing the storage-form axis at the two backends that have no storage
found the same defect in both, and neither needed a legacy fixture to
reach it — the cross-type pairing arrives from real callers:

- the RLS `check` post-image is the caller's RAW write payload
  (`{ ...opCtx.data }`, before any driver formatInput), so an SDK write
  of `new Date()` lands in matchesFilterCondition as a Date against
  wire-text comparands; the mirror pairing arrives too, since a CEL
  `today()` lowers to a Date;
- a preview row from a mongo-backed dataset arrives as a BSON Date
  (D-E2), against the same comparands.

JS relational operators cannot order that pair: they coerce with hint
number, so the Date becomes its epoch and the string becomes NaN. 10 of
the 16 shared cases diverged on each backend. Fail-closed made formula's
half a DENIED write — the write-side twin of #4047, the direction D-D2
recorded for the bare-day upper bound. The preview's half was worse:
falling back to String(value) puts 'Mon Jul 27 2026 …' after every
'2026-…' comparand, so windows both lost rows and gained them, and a
drafted chart's numbers changed at publish.

utcInstantMs joins nextUtcCalendarDay in spec/data/calendar-day.ts (core
re-exports it) — one definition for both, the D-D2 discipline. It accepts
only unambiguous spellings, so the answer never depends on the process
zone, and returns null for a bare wall clock, which denotes no instant:
the Field.time table and the date column stay out of the sweep by the
same scope rule this matrix already applies to $gt-on-datetime.

Deliberately narrow: the lift runs only when one operand is a Date and
both read as instants, so every comparison that worked before is
byte-identical and the security posture never loosens beyond what the
operands justify.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SWHvwFgUU2U8M9nbV1g1iQ
@vercel

vercel Bot commented Jul 30, 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 30, 2026 4:28pm

Request Review

@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation protocol:data tests tooling and removed size/m labels Jul 30, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 4 package(s): @objectstack/core, @objectstack/formula, @objectstack/service-analytics, @objectstack/spec.

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

  • content/docs/ai/actions-as-tools.mdx (via @objectstack/core)
  • content/docs/ai/agents.mdx (via @objectstack/spec)
  • content/docs/ai/knowledge-rag.mdx (via @objectstack/core)
  • content/docs/ai/natural-language-queries.mdx (via @objectstack/core)
  • 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/data-api.mdx (via @objectstack/service-analytics)
  • 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/service-analytics, @objectstack/spec)
  • content/docs/automation/approvals.mdx (via packages/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/core, @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 packages/spec)
  • content/docs/concepts/north-star.mdx (via packages/core, 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 @objectstack/formula, @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/formula, @objectstack/spec)
  • content/docs/deployment/cli.mdx (via @objectstack/spec)
  • content/docs/deployment/migration-from-objectql.mdx (via @objectstack/core)
  • content/docs/deployment/troubleshooting.mdx (via @objectstack/spec)
  • content/docs/deployment/validating-metadata.mdx (via @objectstack/spec)
  • 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/core, @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/examples.mdx (via @objectstack/core)
  • 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/core, @objectstack/service-analytics, @objectstack/spec)
  • content/docs/kernel/services.mdx (via @objectstack/core, @objectstack/spec)
  • content/docs/permissions/authentication.mdx (via @objectstack/core)
  • content/docs/permissions/authorization.mdx (via packages/core, @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/service-analytics, @objectstack/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx (via @objectstack/spec)
  • content/docs/plugins/anatomy.mdx (via @objectstack/core)
  • content/docs/plugins/development.mdx (via @objectstack/core, @objectstack/spec)
  • content/docs/plugins/index.mdx (via @objectstack/core, @objectstack/spec)
  • content/docs/plugins/packages.mdx (via @objectstack/core, @objectstack/formula, @objectstack/service-analytics, @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/core, @objectstack/spec)
  • content/docs/protocol/kernel/i18n-standard.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/core, @objectstack/spec)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/core, @objectstack/spec)
  • content/docs/protocol/kernel/plugin-spec.mdx (via @objectstack/core, @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 @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/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/formula, @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx (via @objectstack/spec)
  • content/docs/releases/implementation-status.mdx (via @objectstack/core, @objectstack/service-analytics, @objectstack/spec)
  • content/docs/releases/index.mdx (via @objectstack/spec)
  • content/docs/releases/v12.mdx (via @objectstack/core, @objectstack/spec)
  • content/docs/releases/v13.mdx (via @objectstack/spec)
  • content/docs/releases/v15.mdx (via @objectstack/core, @objectstack/formula)
  • content/docs/releases/v16.mdx (via @objectstack/formula, @objectstack/spec)
  • content/docs/releases/v17.mdx (via @objectstack/spec)
  • content/docs/releases/v9.mdx (via @objectstack/service-analytics, @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.

@os-zhuang
os-zhuang marked this pull request as ready for review July 30, 2026 16:38
@os-zhuang
os-zhuang merged commit e4c2dc8 into main Jul 30, 2026
18 checks passed
@os-zhuang
os-zhuang deleted the claude/storage-form-time-locking-xkcdgc branch July 30, 2026 16:41
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 tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants