fix(formula,service-analytics): 类型盲后端把 Date 与线上文本比出错——storage-form 轴指过去当场测出两个真缺陷 (#4191) - #4223
Merged
Merged
Conversation
…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
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 4 package(s): 115 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
#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→ JSDate,wire→ 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 关系运算符排不了这个序:
<一族按 hintnumber强制转换,Date变 epoch、字符串变NaN,每个比较恒 false。实测 16 个共享用例中 10 个丢光全部Date值行。fail-closed 把它变成拒写——#4047 的写侧孪生,正是 D-D2 为裸日期上界记录过的那个失败方向。3. analytics preview evaluator:同样 10/16,且双向错
同一配对(mongo 的
datetimecanon 就是 BSONDate,D-E2),但compare回退到String(value):'Mon Jul 27 2026 …'字典序排在每个'2026-…'comparand 之后,于是窗口既丢行又多收行($gte那格多返回了b_prev/h_leap)。草稿图表因此显示了发布后会变的数字——恰恰是 preview 存在的意义(连续性)被打破。修法
utcInstantMs进spec/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全量service-analytics全量driver-memory全量plugin-security全量(RLS check 真实调用方)core全量speccheck:docs/check:api-surfaceutcInstantMs一行,已 regen 提交)service-analytics的tsc --noEmit有 5 行报错,stash 后逐字相同——改动前既有,不在本 PR 触及的文件里。driver-mongodb:本轮不动,已立项 #4220
#4191 点名最尖的一格。本环境跑不了真实 MongoDB(出站代理对
fastdl.mongodb.org返回 403 policy denial,mongodb-memory-server误报成「版本-平台组合不存在」;本机无 docker 守护进程),而该驱动既无读修复也无 backfill(supports.migrations: false,已逐一 grep 确认无$expr/$convert/repair 路径)——D-E3 有意不做 boot-step backfill,但它指向的「until migrated」那个迁移并不存在。因此遗留 sweep 必然红、修复必须盲写安全相关的驱动改动。按「不做未验证的架构改动、不为未交付能力做假覆盖」立项 #4220,写明预期机理、两条候选修法(
$expr读修复 vs 显式迁移命令,倾向二者并存以与 SQLite 对称)与验收规则。关联
#4191(本体)、#4205(首轮)、cloud#938、#4220(mongo)、#4203(
check: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