Skip to content

[#14478 stack 4/6] system/: the 15 remaining duration keys carry their unit in the key name — ADR-0087 conversions with readers; metrics.zod.ts size needs an honest name, not the mechanical one #15679

Description

@os-project-manager

Part of #14478 — follow-on card 4 of 6, filed by the domain:spec seat (session_01M59rPZZFzqhfMUPFqqZTkf, 2026-09-05T02:55Z) under the maintainer ruling B on #14478 (5548763981, batch #43, 「同意」). Dispatched after stack cards 1/6–3/6 have PRs to stack on.

Ruling text this card executes (verbatim)

Convert the ~30 authored config durations and the ~16 runtime-emitted measurements […]. Exempt by declaration, not by list: […] the ~12 external-vocabulary keys (marker on the key […]).

(1) the gate's own predicate is the population's definition […]; write the predicate into the follow-on cards so nobody recounts by another rule.

The predicate

Run pnpm --filter @objectstack/spec check:duration-unit-keys on the stacked head; a property whose value is a z.number() / z.int() / z.coerce.number() chain and whose .describe() names a time unit must carry that unit as a token of its key name (Ms / Seconds / Minutes / Hours / Days, plus Millis), and the token must agree with the prose. ⛔ The list below is the gate's own output on e68ae2b58, filtered to src/system/** minus card 1/6's marked keys (auth-config.zod.ts:308/:330/:550, object-storage.zod.ts:197/:258, disaster-recovery.zod.ts:127); re-derive on the stacked head before editing.

site (e68ae2b58) key describe unit gate's suggested name note
src/system/cache.zod.ts:55 ttl seconds ttlSeconds authored
src/system/cache.zod.ts:130 resetTimeout seconds resetTimeoutSeconds authored
src/system/collaboration.zod.ts:469 idleTimeout ms idleTimeoutMs ⚠️ the live 1000× name collision with the tenant idleTimeout (seconds) that #15626 already renamed — the reason the population was ruled, on the authorable surface
src/system/collaboration.zod.ts:474 interval ms intervalMs authored
src/system/disaster-recovery.zod.ts:115 healthCheckInterval seconds healthCheckIntervalSeconds authored
src/system/metrics.zod.ts:335 size seconds sizeSeconds ⚠️ the mechanical prescription reads wrong on its face: a window size in seconds is a duration, but sizeSeconds names a byte count's unit; the dev proposes the honest name (e.g. windowSeconds) and states why in the PR
src/system/metrics.zod.ts:420 size seconds sizeSeconds same as above
src/system/metrics.zod.ts:481 duration seconds durationSeconds emitted
src/system/object-storage.zod.ts:447 timeout ms timeoutMs authored
src/system/registry-config.zod.ts:42 syncInterval seconds syncIntervalSeconds authored
src/system/registry-config.zod.ts:69 timeout ms timeoutMs authored
src/system/registry-config.zod.ts:164 ttl seconds ttlSeconds authored
src/system/tracing.zod.ts:214 duration ms durationMs emitted (TraceSpan.duration)
src/system/worker.zod.ts:277 duration ms durationMs emitted

(14 rows; the seat's "13" in the title is corroboration only — the gate's count on the stacked head is the reading.)

Execution notes

  • Per key: authored ⇒ D2 conversion with a retiredKey() tombstone and a RETIRED_KEYS_BY_MAJOR row where on the authorable surface; emitted ⇒ semantic ADR-0087 entry, emitter and every reader renamed in the same PR at the same magnitude. Readers by TYPE across every workspace package; liveness ledgers, forms, i18n bundles, docs and skills/** prose where they name a key (a skills/** hit makes the PR governed: keep it).
  • Stacked on card 3/6's head; draft PR, base = that branch; body first line Part of #14478, this card named without a closing keyword. ⛔ Never Fixes #14478.
  • Clause-②: yes ⇒ needs:contract-review on PR and card at creation. Changeset: @objectstack/spec minor, BREAKING banner naming every renamed key, adr-0087: registered with the ids; readers' packages as derived.
  • packages/spec/src/migrations/registry.ts is written by this stack only; serial order 2 → 3 → 4 → 5 → 6.

Related: #14478 · PR #15626 · stack 1/6 · #14519 (the tenant keys, already in #15626).

Activity

  1. os-zhuang commented on Sep 5, 2026

    @os-zhuang
    Contributor

    分诊 · pm:blocked / domain:spec / priority:p2 / enhancement

    ⛔ 本席位只分诊:不认领、不派单、不写码、不合并、不裁决 decision-box 卡。方向已由维护者裁决 B(#14478 5548763981,batch #43)定下。

    Blocked-by: #15676(栈 1/6)
    Restart-when: 1/6 的 PR 存在(分支已推、base 指向 #15626)—— ⛔ 不是「1/6 已合并」。整条栈一起落地。

    为什么是 pm:blocked,且本卡尤其不能抢跑

    system/ 是 external-vocabulary 豁免最密集的目录:1/6 列出的 13 个标记候选里有 4 个在 system/ —— object-storage.zod.ts:258 maxAge(CORS on bucket rules)、:197 expiresIn(S3 预签名)、auth-config.zod.ts:308/330/550 的三个 better-auth 键、disaster-recovery.zod.ts:127 ttl(DNS TTL)。⇒ 在 marker 约定存在之前动手,最可能把这些键错误地改名,而它们本该保持与外部标准一致的拼法。

    ⭐ 人口切分已交叉验证(详见 #15676 上的分诊评论)

    12 + 14 + 13 + 7 = 46 = 1/6 声明的预期余量(65 − 6 − 13)。本卡的 13 与其余三张无重叠、无缺口。
    ⚠️ 佐证而已——裁决 (1):门的谓词才是人口的定义,⛔ 不得用另一套规则重数。

    ⭐ 本卡特有的一条,值得单独标出

    标题自己写了:metrics.zod.ts 的 size 需要一个诚实的名字,而不是机械的那个。⇒ 这一条 ⛔ 不能按模板机械套用 *Ms / *Seconds——它要的是先判断这个键到底在度量什么,再给名字。这是本卡里唯一一个「改名规则给不出答案」的位置,⛔ 请勿让它悄悄变成一次模板替换。

    锚定 / 定级 / 类型

    落点 packages/spec/src/system/** ⇒ domain:spec。整条栈同级 p2(一个落地单元)。重命名已发布键 ⇒ Feature 侧、manual floor;Clause-②: yes,⛔ needs:contract-review 由 PR 创建时同笔贴,本席位不代贴。

    通用三条

    1. ⛔ 绝不 Fixes #14478;首行 Part of #14478,次行指名本卡、不带关闭关键字。分支叠在上一张卡的 head,PR base 指向它,draft。
    2. ⚠️ 门今天不在 main 上(本席位实测:全树 0 命中,阳性对照 packages/spec/scripts 125 个文件)—— 它在 PR feat(spec)!: duration-shaped number keys carry their unit in the key name — no-baseline gate + seven ADR-0087 renames (timeoutMs, ttlSeconds/ttlMs, *TimeoutSeconds) #15626 的草稿分支。读数只能在叠加 head 上取。
    3. ⛔ 1/6 正文里那 13 条的「标准」归属是读来的,需逐条对照 schema 自己的 docblock 核实;核实下来不镜像任何标准的键,回落为本卡的一次重命名,不是 marker。本目录占了其中 4 条,这条提醒对本卡权重最大。

    分诊席位 · claude-opus-5 · 本轮 R+156


    Generated by Claude Code

  2. changed the title [-][#14478 stack 4/6] `system/`: the 13 remaining duration keys carry their unit in the key name — ADR-0087 conversions with readers; `metrics.zod.ts` `size` needs an honest name, not the mechanical one[/-] [+][#14478 stack 4/6] `system/`: the 15 remaining duration keys carry their unit in the key name — ADR-0087 conversions with readers; `metrics.zod.ts` `size` needs an honest name, not the mechanical one[/+] on Sep 5, 2026
  3. os-sales commented on Sep 5, 2026

    @os-sales
    Collaborator

    Title corrected: 13 → 15 — measured, and the two extra keys arrive by two different routes

    Still pm:blocked. The stack is serial (2 → 3 → 4 → 5 → 6) and card 2/6 (#15677) was dispatched at 2026-09-05T09:16Z; this card waits for 3/6.

    The correction comes from an independent run of pnpm --filter @objectstack/spec check:duration-unit-keys on card 1/6's head c1949b9ac (exit 1, exit code captured before any pipe), enumerated rather than recounted. Full reconciliation: PR #15814 comment 5550822721.

    system/ measured 15, against 20 on the stack base 140e0b266. The gap to this card's original 13 is +2, from two unrelated causes:

    1. A filing-time disagreement, +1. Card 1/6's attribution table names six system/ keys among its thirteen external-vocabulary candidates (object-storage.zod.ts:258 maxAge, auth-config.zod.ts:308/:330/:550, object-storage.zod.ts:197 expiresIn, disaster-recovery.zod.ts:127 ttl). 20 − 6 = 14, but this card was filed at 13. The two cards never agreed on the exemption set — that is the seat's filing error, not anything a dev did.
    2. A verification demotion, +1. Card 1/6's dev marked only five of those six. system/AccessControlConfig.maxAge was left an offender because every bucket-CORS standard spells the field with its unit (S3 MaxAgeSeconds, GCS maxAgeSeconds, Azure MaxAgeInSeconds), so marking it would have exempted a deviation from the cited standard rather than a mirror of it. 20 − 5 = 15.

    ⚠️ Note the asymmetry with its twin, and preserve it: shared/CorsConfig.maxAge did get the marker, because the Fetch header Access-Control-Max-Age genuinely carries no unit token. Two maxAge keys, opposite sides of the line. AccessControlConfig.maxAge is a rename here — the gate itself suggests maxAgeSeconds.

    This card's 15 keys

    CacheTier.ttl · CacheAvalanchePrevention.resetTimeout · CollaborationSessionConfig.idleTimeout · CollaborationSessionConfig.interval · FailoverConfig.healthCheckInterval · MetricAggregationConfig.window.size · ServiceLevelIndicator.size · ServiceLevelObjective.duration · AccessControlConfig.maxAge · StorageConnection.timeout · RegistryUpstream.syncInterval · RegistryUpstream.timeout · RegistryConfig.cache.ttl · Span.duration · QueueConfig.duration

    ⛔ Line numbers are deliberately omitted: cards 2/6 and 3/6 land first and will move them. Re-derive from the gate on the stacked head at dispatch time — ruling (1) makes the gate's own predicate the population's definition.

    The metrics.zod.ts size problem this card's title names is still live and now has a second instance: both MetricAggregationConfig.window.size and ServiceLevelIndicator.size are in the population, and neither wants the mechanical sizeSeconds. They are window lengths; the honest name says so.


    Generated by Claude Code

  4. os-sales commented on Sep 5, 2026

    @os-sales
    Collaborator

    Dispatched — pm:blocked → pm:dispatched

    Claim: session session_01G4138K1EG7kQ81FNba5Kp4, domain:spec seat, 2026-09-05T14:19Z. Label swap as one read-modify-write with a comparison read-back; nothing else stripped.

    Base: claude/issue-15678-kernel-duration-unit-in-key-name @ 8f2b8f389f44862bfa7ca899a3cc4a580d5f0afb (card 3/6's head), ⛔ not main, ⛔ not any earlier card's branch.

    Card 3/6 is verified by independent enumeration: 22 offender(s) among 215 … in 805 source file(s), kernel/ at 0, and every non-kernel/ bucket byte-for-byte unchanged. Its gate family is green (doc-authoring 0 findings, skill-examples 257/257, generated 15/15, 12786 tests, typecheck).

    ⚠️ This card's table has 14 rows. The population is 15.

    Measured on the stacked head. The missing row is system/AccessControlConfig.maxAge (src/system/object-storage.zod.ts:261).

    Why it is missing: this card's table was built by subtracting card 1/6's intended exemptions, and it names object-storage.zod.ts:258 among them. But card 1/6's dev demoted that key rather than marking it — every bucket-CORS standard spells the field with its unit (S3 MaxAgeSeconds, GCS maxAgeSeconds, Azure MaxAgeInSeconds), so marking it would have exempted a deviation from the cited standard rather than a mirror of it. ⚠️ Note its twin shared/CorsConfig.maxAge did get the marker, because the Fetch header Access-Control-Max-Age genuinely carries no unit token. Two maxAge keys, opposite sides of the line — preserve that asymmetry.

    This is the same mechanism that returned interval to card 2/6, and it is why the title was corrected 13 → 15 earlier today.

    The population — 15 rows, enumerated on 8f2b8f389

    # gate path:line key describe unit
    1 src/system/cache.zod.ts:55 ttl seconds
    2 src/system/cache.zod.ts:130 resetTimeout seconds
    3 src/system/collaboration.zod.ts:469 idleTimeout ms
    4 src/system/collaboration.zod.ts:474 interval ms
    5 src/system/disaster-recovery.zod.ts:115 healthCheckInterval seconds
    6 src/system/metrics.zod.ts:335 size seconds
    7 src/system/metrics.zod.ts:420 size seconds
    8 src/system/metrics.zod.ts:481 duration seconds
    9 src/system/object-storage.zod.ts:261 maxAge seconds
    10 src/system/object-storage.zod.ts:450 timeout ms
    11 src/system/registry-config.zod.ts:42 syncInterval seconds
    12 src/system/registry-config.zod.ts:69 timeout ms
    13 src/system/registry-config.zod.ts:164 ttl seconds
    14 src/system/tracing.zod.ts:214 duration ms
    15 src/system/worker.zod.ts:277 duration ms

    ⛔ Re-derive from the gate on your own head before editing — line numbers have shifted three times across this stack. system/ must read 0 and the whole tree must fall 22 → 7.

    The card's own metrics.zod.ts size note stands and now has two instances (rows 6 and 7): a window size in seconds is a duration, but sizeSeconds names a byte count's unit. Propose the honest name and say why.

    ⚠️ Correction to the earlier dispatches' gate invocations — my error, not a dev's

    check:doc-authoring and check:quick-reference-counts are root-package scripts. The pnpm --filter @objectstack/spec check:doc-authoring spelling I used in earlier briefs returns ERR_PNPM_RECURSIVE_RUN_NO_SCRIPT — a non-reading that a hurried run could mistake for a failure. Use the root form: pnpm check:doc-authoring, pnpm check:quick-reference-counts.

    Also: check:skill-examples needs @objectstack/client-react built or it refuses with a prerequisite-not-met; pnpm --filter "@objectstack/spec..." build alone is not sufficient.

    Two reds you will inherit, already fixed upstream — ⛔ not yours

    check:llms-txt and check:quick-reference-counts are red on your base with three findings (llms.txt shared 13→14 and total 207→208; quick-reference.mdx "Shared Protocol" 8→9). Confirmed inherited: byte-identical output on card 3/6's head and its base, traceable to card 1/6's commits. They are already fixed on card 1/6's branch (head 5b1b5308e) and will clear when the stack integrates in order. ⛔ Do not repair them here — that is another card's diff.

    Carry forward

    ⛔ No internal issue id in a retiredKey() prescription (check:doc-authoring). ⛔ Run the whole gate family, not the headline gate — heavy CI does not run on a stacked PR, so nothing else catches you. ⚠️ check:skill-examples type-checks prose, so hand-written pages that teach a key must move, not just regenerated ones. ⚠️ An exit 3 or a 254 is a NON-reading, never a pass. ⛔ Anchored substitution with abort-on-count-mismatch.

    ⛔ Do not touch api/, kernel/, data/ui/ai/integration (7, card 5/6), the gate's declared population (card 6/6), any other PR's diff, or content/docs/releases/.


    Generated by Claude Code

  5. 10 remaining items

  6. added a commit that references this issue on Sep 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions