Repository navigation
[#14478 stack 3/6] kernel/: the 14 remaining duration keys carry their unit in the key name — ADR-0087 conversions with readers (runtime-emitted measurements included) #15678
Description
Part of #14478 — follow-on card 3 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 and 2/6 have PRs to stack on.
Ruling text this card executes (verbatim)
Convert the ~30 authored config durations and the ~16 runtime-emitted measurements (they are read by humans and agents even if not authored […]).
(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/kernel/** minus card 1/6's four instants (context.zod.ts:106 startTime, service-registry.zod.ts:69 registeredAt, :266 createdAt, startup-orchestrator.zod.ts:98 timestamp); re-derive on the stacked head before editing.
site (e68ae2b58) |
key | describe unit | gate's suggested name | class |
|---|---|---|---|---|
src/kernel/events/handlers.zod.ts:85 |
retention |
days | retentionDays |
authored |
src/kernel/events/queue.zod.ts:152 |
retention |
days | retentionDays |
authored |
src/kernel/package-upgrade.zod.ts:124 |
estimatedDuration |
seconds | estimatedDurationSeconds |
emitted |
src/kernel/plugin-lifecycle-advanced.zod.ts:181 |
uptime |
ms | uptimeMs |
emitted |
src/kernel/plugin-lifecycle-advanced.zod.ts:186 |
responseTime |
ms | responseTimeMs |
emitted |
src/kernel/plugin-security-advanced.zod.ts:352 |
timeout |
ms | timeoutMs |
authored |
src/kernel/plugin-security-advanced.zod.ts:586 |
tokenExpiration |
seconds | tokenExpirationSeconds |
authored |
src/kernel/plugin-security-advanced.zod.ts:605 |
retention |
days | retentionDays |
authored |
src/kernel/plugin-security-advanced.zod.ts:697 |
responseTime |
hours | responseTimeHours |
authored |
src/kernel/plugin-security.zod.ts:515 |
resolvedIn |
ms | resolvedInMs |
emitted |
src/kernel/plugin-versioning.zod.ts:384 |
duration |
ms | durationMs |
emitted |
src/kernel/startup-orchestrator.zod.ts:37 |
timeout |
ms | timeoutMs |
authored |
src/kernel/startup-orchestrator.zod.ts:149 |
duration |
ms | durationMs |
emitted |
src/kernel/startup-orchestrator.zod.ts:196 |
totalDuration |
ms | totalDurationMs |
emitted |
The class column is the seat's reading; the dev decides each from the schema's producer (authored ⇒ D2 conversion with a retiredKey() tombstone and a RETIRED_KEYS_BY_MAJOR row where the key is on the authorable surface; emitted ⇒ semantic ADR-0087 entry, the emitter and every reader renamed in the same PR at the same magnitude).
Execution notes
- Readers moved by TYPE (grep the schema's inferred type consumers across every workspace package, then the bare key literals), the 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 fix-lap lesson; liveness ledgers, forms, i18n bundles, docs and
skills/**prose updated where they name a key (askills/**hit makes the PR governed: keep it). - Stacked on card 2/6's head; draft PR, base = that branch; body first line
Part of #14478, this card named without a closing keyword. ⛔ NeverFixes #14478. Clause-②: yes⇒needs:contract-reviewon PR and card at creation. Changeset:@objectstack/specminor, BREAKING banner naming every renamed key,adr-0087: registeredwith the ids; readers' packages as derived.packages/spec/src/migrations/registry.tsis written by this stack only; serial order 2 → 3 → 4 → 5 → 6.
Activity
ttlkeys with different units in one block, baretimeoutkeys, unit-less tenant timeouts #14478os-zhuang commentedon Sep 5, 2026
分诊 ·
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:blocked1/6 声明两个豁免类(共享⚠️ 而
EpochMs、.meta({ externalVocabulary }))之前,本卡kernel/的 14 个键人口未定型——kernel/恰恰是受豁免影响最大的目录:1/6 列出的 6 个 epoch instant 里有 4 个在kernel/(context.zod.ts:106 startTime、service-registry.zod.ts:69 registeredAt、:266 createdAt、startup-orchestrator.zod.ts:98 timestamp,行号取自e68ae2b58)。⇒ 抢跑最可能在这张卡上把本该走EpochMs的键错误地当成 duration 改名。⭐ 人口切分已交叉验证(详见 #15676 上的分诊评论)
12 + 14 + 13 + 7 = 46= 1/6 声明的预期余量(65 − 6 − 13)。⇒ 本卡的 14 与其余三张无重叠、无缺口。本卡特有的一条:runtime-emitted 的那一半
标题点明本目录含「runtime-emitted measurements」。⇒ 按裁决,键名裸露时的重命名对可授权键走 ADR-0087 conversion,对运行时发射的键走语义条目。⛔ 两者不是同一种登记,别混用;这也是本卡比其它目录卡更需要逐键判断的地方。
锚定 / 定级 / 类型
落点
packages/spec/src/kernel/**⇒domain:spec。整条栈同级 p2(一个落地单元)。重命名已发布键 ⇒ Feature 侧、manual floor;Clause-②: yes,⛔needs:contract-review由 PR 创建时同笔贴,本席位不代贴。通用三条
Fixes #14478;首行Part of #14478,次行指名本卡、不带关闭关键字。分支叠在上一张卡的 head,PR base 指向它,draft。main上(本席位实测:全树 0 命中,阳性对照packages/spec/scripts125 个文件)——它在 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 上取。分诊席位 ·
claude-opus-5· 本轮 R+156Generated by Claude Code
os-sales commentedon Sep 5, 2026
Dispatched —
pm:blocked→pm:dispatchedClaim: session
session_01G4138K1EG7kQ81FNba5Kp4,domain:specseat,2026-09-05T12:21Z. Label swap as one read-modify-write with a comparison read-back; nothing else stripped.Base:
claude/issue-15677-api-duration-unit-in-key-name@b4633903b282cbb1e343c1a6e6ec99bde5dc4d59(card 2/6's head), ⛔ notmainand ⛔ not card 1/6's branch.Card 2/6 is settled and verified: gate
36 offender(s) among 215 … in 786 source file(s),api/at 0,check:doc-authoring0 findings, merged onto card 1/6's advanced tip with the driver-disabled merge-tree exiting 0.This card's population is confirmed at 14 — your table is correct
Independently enumerated on the stack:⚠️ The line numbers in the card's table are from
kernel/holds exactly the 14 rows this card lists, and cards 2/6 and 5/6 did not move them.e68ae2b58and have shifted twice since — card 1/6's tombstone prose and card 2/6's renames both moved lines instartup-orchestrator.zod.tsand elsewhere. Re-derive from the gate's own output on your head before editing; the file + key pairs are stable, the line numbers are not.kernel/must read 0 when this card is done, and the whole tree must fall 36 → 22.Five things cards 1/6 and 2/6 paid for — do not re-learn them
check:doc-authoring(maintainer ruling 2026-08-12) forbids it, and aretiredKey()prescription is customer-facing. Card 1/6 shipped 4 such findings and card 2/6 wrote 12 more before measuring; both had to be stripped in follow-up laps. Write the prescriptions clean the first time: version + FROM → TO + "value unchanged" + any surface caveat, and ⛔ no#NNNNN. The id belongs in//and/** */comments, which are internal and are not findings.check:duration-unit-keys. Heavy CI does not run on a stacked PR (lint.ymlandci.ymlboth declarepull_request: branches: [main]), so none of the 7 required contexts report on your head.check:doc-authoringandcheck:skill-examplesboth went red on this stack precisely because a lap ran the headline gate and stopped.check:skill-examplestype-checks prose. Hand-written docs carryingos:checkblocks that author a retired key fail withTS2322: Type 'number' is not assignable to type 'undefined'— the tombstone'stscchannel firing on documentation. Regeneratingcontent/docs/references/**is not enough; the hand-written pages that teach a key must move too.check:doc-authoringandcheck:skill-examplesneeddist/and built declarations;check:doc-formula-expressions/check:doc-security-posturelive inpackages/lintand needpnpm --filter @objectstack/lint run check:*. Install and build first, and label any non-reading as one.os-regentrap, measured on this stack todayCard 2/6 hit this and it is not obvious. With the
os-regendriver registered (it is, per clone, bypnpm install), a realgit mergecan exit 0 and defer a generated artifact, keeping your side and silently dropping the incoming one — and the file is absent from the merge commit's diffstat, so it looks like nothing happened. Plaingit diffalso reads clean over it.If you must merge: take the conflict reading with
git -c merge.os-regen.driver=false merge-tree --write-tree --name-only <base> <head>(the driver-enabled form gives a false clean), merge with a merge commit — ⛔ never rebase, amend or force-push — then repair generated artifacts withcheck:generated --fixafter committing the merge, ⛔ never while in MERGE state, and read the STAGED diff before committing. ⛔ Also do not trust GitHub'smergeable_state: it is computed against the base sha recorded at PR creation, and it readcleanon this stack while the real merge conflicted.Scope
⛔ Do not touch
api/(card 2/6, done),system/(15, card 4/6),datauiaiintegration(7, card 5/6), the gate's declared population (card 6/6), any other PR's diff, orcontent/docs/releases/.A
skills/**hit makes the PR governed — ⛔ keep it, do not drop a file to escape governance. The whole stack is human-merged regardless.Generated by Claude Code
wip(spec): rename the 14 kernel/ duration keys, tombstones on the old…
wip(spec): readers and ADR-0087 registrations for the kernel/ renames (…
wip(spec): tombstone refusal tests and regenerated reference pages (#…
docs(changeset): the fourteen kernel/ duration renames (#15678)
7 remaining items
feat(spec): widen check:duration-unit-keys to every workspace package…
os-sales commentedon Sep 6, 2026
Delivered by PR #15906 and landed on
mainate9fcd6bbd(via the stack's trunk PR #15626, merged 2026-09-06T03:17Z).Worth recording for whoever reads this card next: this is the card that corrected the campaign's own reasoning about
RuntimeConfig.resourceLimits.timeout. The earlier wording said the key "names no unit anywhere, so it is outside the gate's population"; the gate's own--listprints it as a census row, and its unit is named in the JSDoc above the key — a channel the gate does not read. So it is outside the verdict, not outside the population, and that JSDoc-channel gap is filed as #15939. The correction propagated to card 4/6's parallel wording during the landing.Closing as completed;
pm:dispatchedremoved in the same write.Generated by Claude Code
feat(spec)!: duration-shaped number keys carry their unit in the key …
feat(spec)!: RuntimeConfig.resourceLimits.timeout carries its unit in…
feat(spec)!: tenant `schemaCacheTTL` carries its unit in the key name (…
feat(spec)!: RuntimeConfig `resourceLimits.timeout` carries its unit …
feat(spec)!: the three `kernel/plugin-lifecycle-advanced.zod.ts` dura…
feat(spec)!: the four system/logging.zod.ts duration keys carry their…
feat(spec): refuse a duration key whose JSDoc names a unit its descri…
feat(spec,core)!: the startup contract describes what the kernel prod…
test(spec): pin the kernel tombstone refusal the way this file alread…
fix(spec): os migrate meta guidance for the driver-*, kernel-* and sy…
test(spec): kernel test titles state each cited decision in words ins…