从 #4191 的 storage-form 轴工作中分出。SQL 系四格与 driver-memory 的 D-E3 收敛格已落地(#4205、本轮 PR),mongo 这一格无法在当前形态下闭合,原因是设计缺口而不是测试缺口。
缺口本身
ADR-0053 给 SQLite 的遗留数据配了两条保险:
backfillCanonicalDatetimes / backfillCanonicalTimes 在 schema sync 时收敛(D-B3、D-C2);
- 永久保留的读修复表达式(
sqliteCanonicalDatetimeSql / sqliteCanonicalTimeSql),因为「query correctness must never be contingent on [a migration] having run」,且它同时覆盖永远不会被 backfill 的外部表(ADR-0015)。
mongo 两条都没有。D-E3 明确不做 boot-step backfill:
MongoDB gets no automatic backfill — rewriting a real collection is a migration, not a boot step — so a pre-existing collection keeps whatever it holds until migrated
但「until migrated」指向的迁移并不存在(supports.migrations: false,代码里没有任何 $expr / $convert / repair 路径——已逐一 grep 确认)。于是一个 #4047 之前建立的 collection 是永久错的,而 D-E1 说明它的失败模式是所有后端里最安静的:
a string comparand never matches a Date value, in either direction, for every operator —— $gte included
预期机理(推理,非实测——见下)
canon 是 datetime → BSON Date、date/time → 文本。遗留 collection 的两个写者群按 writerForm 分裂:native 写入落成 BSON Date(datetime 恰好合规、time/date 不合规),wire 写入落成 ISO 文本(datetime 不合规)。comparand 被 coerceTemporalValue 强制成 canon 形态后,跨类型的那一半整体不匹配。以共享夹具估算,TEMPORAL_CASES 第一格(#3777 默认仪表盘窗口)期望 [b_prev, c_open, d_mid, e_late],实际只会得到 native 那一半 [b_prev, c_open]。
为什么本轮没有直接做
当前开发环境跑不了真实 MongoDB:出站代理对 fastdl.mongodb.org 返回 403(policy denial,mongodb-memory-server 把它误报成「version-platform combination doesn't exist」),本机也没有 docker 守护进程。因此:
- 无法实测行结果,上面的机理只能是推理;
- 修复(无论走哪条)全程无法在本地执行一次,属于盲写安全相关的驱动改动;
- 直接把 sweep 写进去只会得到一个必然红的测试。
按「不做未验证的架构改动 / 不为未交付的能力做假覆盖」的纪律,本轮不动,立项在此。
两条候选修法(需要裁决)
| 方案 |
形状 |
代价 |
| A. 读修复(与 SQLite 对称) |
translateFilter 对未收敛列包 $expr + $convert/$toDate,onError 保留原值 |
正确性不依赖迁移、覆盖外部 collection;但走不了索引,宽查询代价大;$convert 对 zone-naive 空格分隔文本的解析行为需实测 |
| B. 显式迁移命令 |
一条 os migrate 子命令重写 collection(D-E3 说的正是「a migration」) |
符合 D-E3 原文;但迁移跑之前查询仍然是错的,违反 D-B3「correctness must never be contingent」 |
倾向 A + B 并存,与 SQLite 完全对称:A 保证任何时刻的正确性,B 把代价降回可索引。但这需要在能跑真实 mongo 的环境里逐格验证。
验收
在真实 MongoDB 上,mongodb-temporal-conformance.test.ts 增加两条遗留 sweep(getDb().collection(x).insertMany(...) 直塞混合形态、绕过 toStorageForms),跑 TEMPORAL_CASES / TEMPORAL_TIME_CASES,行 id 集合与 canonical sweep 逐格相同——即 #4191 的轴规则。
关联
#4191(本体)、#4205、#4047 / #3777 / #3912 / #3994;ADR-0053 D-B3 / D-C2 / D-E1 / D-E3。
从 #4191 的 storage-form 轴工作中分出。SQL 系四格与
driver-memory的 D-E3 收敛格已落地(#4205、本轮 PR),mongo 这一格无法在当前形态下闭合,原因是设计缺口而不是测试缺口。缺口本身
ADR-0053 给 SQLite 的遗留数据配了两条保险:
backfillCanonicalDatetimes/backfillCanonicalTimes在 schema sync 时收敛(D-B3、D-C2);sqliteCanonicalDatetimeSql/sqliteCanonicalTimeSql),因为「query correctness must never be contingent on [a migration] having run」,且它同时覆盖永远不会被 backfill 的外部表(ADR-0015)。mongo 两条都没有。D-E3 明确不做 boot-step backfill:
但「until migrated」指向的迁移并不存在(
supports.migrations: false,代码里没有任何$expr/$convert/ repair 路径——已逐一 grep 确认)。于是一个 #4047 之前建立的 collection 是永久错的,而 D-E1 说明它的失败模式是所有后端里最安静的:预期机理(推理,非实测——见下)
canon 是
datetime→ BSONDate、date/time→ 文本。遗留 collection 的两个写者群按writerForm分裂:native写入落成 BSONDate(datetime 恰好合规、time/date 不合规),wire写入落成 ISO 文本(datetime 不合规)。comparand 被coerceTemporalValue强制成 canon 形态后,跨类型的那一半整体不匹配。以共享夹具估算,TEMPORAL_CASES第一格(#3777 默认仪表盘窗口)期望[b_prev, c_open, d_mid, e_late],实际只会得到 native 那一半[b_prev, c_open]。为什么本轮没有直接做
当前开发环境跑不了真实 MongoDB:出站代理对
fastdl.mongodb.org返回 403(policy denial,mongodb-memory-server把它误报成「version-platform combination doesn't exist」),本机也没有 docker 守护进程。因此:按「不做未验证的架构改动 / 不为未交付的能力做假覆盖」的纪律,本轮不动,立项在此。
两条候选修法(需要裁决)
translateFilter对未收敛列包$expr+$convert/$toDate,onError保留原值$convert对 zone-naive 空格分隔文本的解析行为需实测os migrate子命令重写 collection(D-E3 说的正是「a migration」)倾向 A + B 并存,与 SQLite 完全对称:A 保证任何时刻的正确性,B 把代价降回可索引。但这需要在能跑真实 mongo 的环境里逐格验证。
验收
在真实 MongoDB 上,
mongodb-temporal-conformance.test.ts增加两条遗留 sweep(getDb().collection(x).insertMany(...)直塞混合形态、绕过toStorageForms),跑TEMPORAL_CASES/TEMPORAL_TIME_CASES,行 id 集合与 canonical sweep 逐格相同——即 #4191 的轴规则。关联
#4191(本体)、#4205、#4047 / #3777 / #3912 / #3994;ADR-0053 D-B3 / D-C2 / D-E1 / D-E3。