⏱️ 本卡所有读数取自同一动作:2026-09-17T13:30Z。MEASURED / RELAYED 逐条标注 —— 标 MEASURED 的由本席在 origin/main 上第一手重测,标 RELAYED 的来自 #18124 的 dev 报告且未被本席复测。
Governing text: packages/spec/scripts/check-duration-unit-keys.ts 顶部 docblock(三条采纳通道)· docs/adr/0087-metadata-protocol-upgrade-contract.md · #18115 ruling A(决裁批 #134 item 1)· packages/spec/src/migrations/registry.ts 的两条同形先例。
事情
#18115 ruling A 的步骤③(卡 #18124)把十行「真时长」里的八行交付了(PR #18657,已入队)。余下两行被 dev 正确地退回,因为两者都撞上同一堵墙:现有的闭合类型都表达不了它们的单位。
| 行 |
实测单位 |
为什么闭合类型不覆盖 |
data/field-value.zod.ts FileValue.duration(MEASURED::484 duration: z.number().optional()) |
小数秒(RELAYED:外部惯例 HTMLMediaElement.duration / ffprobe;本仓库内无单位成文) |
DurationSeconds 带 .int() ⇒ 拒 12.34;DurationMs 与惯例差 1000× |
kernel/plugin-versioning.zod.ts CompatibilityMatrixEntry.estimatedMigrationTime(MEASURED::188-191 JSDoc 逐字「Estimated migration time in hours」+ z.number().optional()) |
小时 |
词汇表里没有小时类型;卡面 fence「⛔ 无新类型」 |
⭐ 关键读数(MEASURED),它把这道题从「加类型还是收窄」变成了「改不改名」:门禁的采纳通道有三条,不是两条 —— 它自己的 docblock 逐字写:「carries it in its NAME or in its TYPE, never only in its .describe() prose」,第三条是 .meta({ externalVocabulary })。⇒ 键名自带单位就被采纳,值类型不必是闭合类型。
⇒ 于是两行都有一条不加新类型、不收窄已存储数据的出路:duration → durationSeconds(值仍 z.number(),小数秒合法);estimatedMigrationTime → estimatedMigrationTimeHours。
⭐ 而且这条路本仓库已经走过两次(MEASURED,migrations/registry.ts):window.size → window.durationSeconds;ServiceLevelObjective.period.duration → durationSeconds,其条目原文逐字「Rename the key to durationSeconds; the value (seconds) …」。⇒ 不是新发明,是既有做法。
选项 × 真实代价
|
做什么 |
客户可感知的后果 |
代价 |
| A |
改名:durationSeconds + estimatedMigrationTimeHours,各一条 ADR-0087 转换层条目 |
老数据照常读(转换层重放);新写的元数据里键名自己说单位 |
两条 ADR-0087 条目落 packages/spec/src/migrations/registry.ts —— 该文件被 #17534 持有(MEASURED:它仍 open + pm:dispatched)⇒ 必须排在它之后 |
| B |
上闭合类型:FileValue.duration 取 DurationSeconds |
一个写 12.34 秒的生产者会在写的那一刻被拒;而 FileValueSchema 是 ADR-0104 双模已存储内联 blob ⇒ 对可能已存在的行收窄 |
且 estimatedMigrationTime 仍无解,还得回到 A 或 C |
| C |
给闭合词汇表加新类型:小数秒 + 小时 |
与 A 对客户等价 |
#18124 卡面 fence「⛔ 无新类型」⇒ 要维护者亲自解 fence;词汇表从 2 个长到 4 个 |
| D |
退役两键 |
两键在本仓库零生产者、零消费者(MEASURED:文件字段水合写出的形状是 { id, name, size, mimeType, url },packages/objectql/src/engine.ts 自己的注释逐字如此,不含 duration;RELAYED:objectui 侧也无消费者) |
退役同样欠 ADR-0087 条目、同样落被持有的那个文件 ⇒ 省不下排期,只省下键 |
业务直译:A = 「把单位写进名字,像我们前两次那样」。B = 「为了把单位写进类型,接受它开始拒绝小数,并赌没有已存的小数行」。C = 「为这两个单位各造一个新词,并请维护者收回自己设的禁令」。D = 「这两个字段没人用,删掉」。
① 项目长远合理性:A 用的是仓库已经在用的采纳通道与已经走过两次的改名先例 ⇒ 特例不增;C 把闭合词汇表从 2 扩到 4,而扩它的理由是两个零拉动的字段;B 制造一个「声明了单位但会拒绝该单位的自然取值」的字段,这是特例。
② 实际业务拉动:今天没有人撞上 —— 两键在本仓库零生产者零消费者(MEASURED)。⇒ 按模板「零拉动默认 defer 或 remove」,方向定了也应排在有拉动的活后面。
③ 防 AI 犯错:一个叫 duration 的裸数字,正是 AI 照着邻居抄一个数、静默差 1000× 的形状;改名后错的那一刻在键名上就读得出来,且门禁会响亮拒绝矛盾声明(#18657 的消融腿 A/B 已证该门禁真能红)。B 的拒绝也响亮,但它拒的是正确的小数值。
④ 创业阶段不扩散:D(退役)本是 ④ 最偏好的,但它省不下代价 —— 退役同样欠 ADR-0087 条目、同样落被持有的文件;而 FileValue 是发布契约,删一个已发布可 authoring 键的下游面比改名大。⇒ ④ 在这里不推翻 A。
Prior rulings read: duration,filevalue,estimatedmigrationtime,adr-0087 → 4 hits; ADR-0087 D8, ADR-0120 D2, ADR-0120 D7, ADR-0131 D13
⚠️ 逐条读过,没有一条已经答了本题(MEASURED):ADR-0087 D8 管的是 breaking changeset 的 no-migration-prescription 处置类别 —— 即「改名之后台账怎么记」,⛔ 不是「改不改名」;ADR-0120 与 ADR-0131 对 duration / FileValue / estimatedMigrationTime 三个词零命中(亮控:同一把 grep 在 ADR-0087 上读回内容,所以仪器是响的),它们是被共现词 adr-0087 带出来的。⇒ 本卡是决策,⛔ 不是已被裁决过的执行。
推荐 A。自检行:只看①选 A;②③④ 是否翻转:否 —— ② 把它推后(排在 #17534 之后,且零拉动不插队),③ 加强它,④ 本想选 D 但 D 省不下代价。⛔ 字母不改。
回退项:D(退役)。若维护者判「零拉动的已发布键不值得一条转换层条目」,退役比改名更彻底,代价同级。
置信缺口(本席看不见的):① FileValue.duration 的外部消费者 —— 本仓库与 objectui 内为零是测过的,但这是发布契约,仓外用它的人本席看不到;② 是否真有已存储的小数 duration 行 —— 本席没有生产库可查,B 的收窄风险因此只能定性、⛔ 不能定量。
裁后执行段
裁 A ⇒ 本席在 #17534 释放 migrations/registry.ts 后立一张执行卡(⛔ 不重开 #18124):两条改名 + 两条 ADR-0087 转换层条目 + 两处 .zod.ts 改名 + 生成物重生成,Part of #18124;#18124 在那一笔之后才收口。
裁 D ⇒ 同样排在 #17534 之后,执行卡改为两条退役条目(retiredKey() 墓碑,按 #17502 ⛔ 不加 title)。
裁 B 或 C ⇒ 本席回到 #18124 补派,并在派发令里写明所解的 fence 原话。
维护者速读
事情。 规格里有两个「时长」字段,单位只写在注释里或哪里都没写:一个是文件的时长(网上通行的写法是小数秒),一个是「预计迁移工时」(单位是小时)。我们这批把其它八个字段的单位写进了合约,这两个卡住了 —— 现有的两种「带单位的类型」是整数毫秒和整数秒,装不下小数秒,也装不下小时。
发现。 规则其实还有第三条路:键名自己带单位就算数。把 duration 改叫 durationSeconds、把 estimatedMigrationTime 改叫 estimatedMigrationTimeHours,不用造新类型,也不用让字段开始拒绝小数。这条路我们已经走过两次,台账里有记录。
代价。 改名要一条「老数据怎么继续读」的台账条目,而那个台账文件现在被另一张在做的卡占着,所以这件事得排在它后面。另外:这两个字段今天没有任何代码在写、也没有任何代码在读,所以不着急。
你要做的。 选一个字母:A 改名(推荐)· B 上整数类型(会拒绝小数,且对可能已存在的数据收窄)· C 给词汇表加新类型(需要你收回「不加新类型」那条禁令)· D 退役这两个字段。
Part of #18124 · 执行 #18115 ruling A 时长出的残留问题,按巡检 H52 的处方另立新卡而 ⛔ 不在 #18124 上重挂 needs-user-decision(那会让收件箱说不清哪个问题还开着,且 #18124 收口时会把标签连同问题的唯一可见性一起带走)。
Generated by Claude Code
⏱️ 本卡所有读数取自同一动作:2026-09-17T13:30Z。MEASURED / RELAYED 逐条标注 —— 标 MEASURED 的由本席在
origin/main上第一手重测,标 RELAYED 的来自 #18124 的 dev 报告且未被本席复测。Governing text:packages/spec/scripts/check-duration-unit-keys.ts顶部 docblock(三条采纳通道)·docs/adr/0087-metadata-protocol-upgrade-contract.md· #18115 ruling A(决裁批 #134 item 1)·packages/spec/src/migrations/registry.ts的两条同形先例。事情
#18115 ruling A 的步骤③(卡 #18124)把十行「真时长」里的八行交付了(PR #18657,已入队)。余下两行被 dev 正确地退回,因为两者都撞上同一堵墙:现有的闭合类型都表达不了它们的单位。
data/field-value.zod.tsFileValue.duration(MEASURED::484duration: z.number().optional())HTMLMediaElement.duration/ffprobe;本仓库内无单位成文)DurationSeconds带.int()⇒ 拒12.34;DurationMs与惯例差 1000×kernel/plugin-versioning.zod.tsCompatibilityMatrixEntry.estimatedMigrationTime(MEASURED::188-191JSDoc 逐字「Estimated migration time in hours」+z.number().optional())⭐ 关键读数(MEASURED),它把这道题从「加类型还是收窄」变成了「改不改名」:门禁的采纳通道有三条,不是两条 —— 它自己的 docblock 逐字写:「carries it in its NAME or in its TYPE, never only in its
.describe()prose」,第三条是.meta({ externalVocabulary })。⇒ 键名自带单位就被采纳,值类型不必是闭合类型。⇒ 于是两行都有一条不加新类型、不收窄已存储数据的出路:
duration→durationSeconds(值仍z.number(),小数秒合法);estimatedMigrationTime→estimatedMigrationTimeHours。⭐ 而且这条路本仓库已经走过两次(MEASURED,
migrations/registry.ts):window.size→window.durationSeconds;ServiceLevelObjective.period.duration→durationSeconds,其条目原文逐字「Rename the key todurationSeconds; the value (seconds) …」。⇒ 不是新发明,是既有做法。选项 × 真实代价
durationSeconds+estimatedMigrationTimeHours,各一条 ADR-0087 转换层条目packages/spec/src/migrations/registry.ts—— 该文件被 #17534 持有(MEASURED:它仍 open +pm:dispatched)⇒ 必须排在它之后FileValue.duration取DurationSeconds12.34秒的生产者会在写的那一刻被拒;而FileValueSchema是 ADR-0104 双模已存储内联 blob ⇒ 对可能已存在的行收窄estimatedMigrationTime仍无解,还得回到 A 或 C{ id, name, size, mimeType, url },packages/objectql/src/engine.ts自己的注释逐字如此,不含 duration;RELAYED:objectui 侧也无消费者)业务直译:A = 「把单位写进名字,像我们前两次那样」。B = 「为了把单位写进类型,接受它开始拒绝小数,并赌没有已存的小数行」。C = 「为这两个单位各造一个新词,并请维护者收回自己设的禁令」。D = 「这两个字段没人用,删掉」。
① 项目长远合理性:A 用的是仓库已经在用的采纳通道与已经走过两次的改名先例 ⇒ 特例不增;C 把闭合词汇表从 2 扩到 4,而扩它的理由是两个零拉动的字段;B 制造一个「声明了单位但会拒绝该单位的自然取值」的字段,这是特例。
② 实际业务拉动:今天没有人撞上 —— 两键在本仓库零生产者零消费者(MEASURED)。⇒ 按模板「零拉动默认 defer 或 remove」,方向定了也应排在有拉动的活后面。
③ 防 AI 犯错:一个叫
duration的裸数字,正是 AI 照着邻居抄一个数、静默差 1000× 的形状;改名后错的那一刻在键名上就读得出来,且门禁会响亮拒绝矛盾声明(#18657 的消融腿 A/B 已证该门禁真能红)。B 的拒绝也响亮,但它拒的是正确的小数值。④ 创业阶段不扩散:D(退役)本是 ④ 最偏好的,但它省不下代价 —— 退役同样欠 ADR-0087 条目、同样落被持有的文件;而
FileValue是发布契约,删一个已发布可 authoring 键的下游面比改名大。⇒ ④ 在这里不推翻 A。Prior rulings read: duration,filevalue,estimatedmigrationtime,adr-0087 → 4 hits; ADR-0087 D8, ADR-0120 D2, ADR-0120 D7, ADR-0131 D13no-migration-prescription处置类别 —— 即「改名之后台账怎么记」,⛔ 不是「改不改名」;ADR-0120 与 ADR-0131 对duration/FileValue/estimatedMigrationTime三个词零命中(亮控:同一把 grep 在 ADR-0087 上读回内容,所以仪器是响的),它们是被共现词adr-0087带出来的。⇒ 本卡是决策,⛔ 不是已被裁决过的执行。推荐 A。自检行:只看①选 A;②③④ 是否翻转:否 —— ② 把它推后(排在 #17534 之后,且零拉动不插队),③ 加强它,④ 本想选 D 但 D 省不下代价。⛔ 字母不改。
回退项:D(退役)。若维护者判「零拉动的已发布键不值得一条转换层条目」,退役比改名更彻底,代价同级。
置信缺口(本席看不见的):①
FileValue.duration的外部消费者 —— 本仓库与 objectui 内为零是测过的,但这是发布契约,仓外用它的人本席看不到;② 是否真有已存储的小数duration行 —— 本席没有生产库可查,B 的收窄风险因此只能定性、⛔ 不能定量。裁后执行段
裁 A ⇒ 本席在 #17534 释放
migrations/registry.ts后立一张执行卡(⛔ 不重开 #18124):两条改名 + 两条 ADR-0087 转换层条目 + 两处.zod.ts改名 + 生成物重生成,Part of #18124;#18124 在那一笔之后才收口。裁 D ⇒ 同样排在 #17534 之后,执行卡改为两条退役条目(
retiredKey()墓碑,按 #17502 ⛔ 不加 title)。裁 B 或 C ⇒ 本席回到 #18124 补派,并在派发令里写明所解的 fence 原话。
维护者速读
事情。 规格里有两个「时长」字段,单位只写在注释里或哪里都没写:一个是文件的时长(网上通行的写法是小数秒),一个是「预计迁移工时」(单位是小时)。我们这批把其它八个字段的单位写进了合约,这两个卡住了 —— 现有的两种「带单位的类型」是整数毫秒和整数秒,装不下小数秒,也装不下小时。
发现。 规则其实还有第三条路:键名自己带单位就算数。把
duration改叫durationSeconds、把estimatedMigrationTime改叫estimatedMigrationTimeHours,不用造新类型,也不用让字段开始拒绝小数。这条路我们已经走过两次,台账里有记录。代价。 改名要一条「老数据怎么继续读」的台账条目,而那个台账文件现在被另一张在做的卡占着,所以这件事得排在它后面。另外:这两个字段今天没有任何代码在写、也没有任何代码在读,所以不着急。
你要做的。 选一个字母:A 改名(推荐)· B 上整数类型(会拒绝小数,且对可能已存在的数据收窄)· C 给词汇表加新类型(需要你收回「不加新类型」那条禁令)· D 退役这两个字段。
Part of #18124 · 执行 #18115 ruling A 时长出的残留问题,按巡检 H52 的处方另立新卡而 ⛔ 不在 #18124 上重挂
needs-user-decision(那会让收件箱说不清哪个问题还开着,且 #18124 收口时会把标签连同问题的唯一可见性一起带走)。Generated by Claude Code