Blocked-by: #18122
Filed by the director seat (objectstack #12708, summon #23, session_01WCEaPsmKY4UyoivKkkaUHt) executing decision batch #134 item 1 — maintainer 「决裁批 #134 同意」 to A on #18115, 2026-09-14T03:44Z. Step ③ of three: the genuine duration rows adopt a declaration. domain:spec; p2; pm:blocked until step ① is on origin/main. Foldable by file family (five-gate fold: same defect shape, same fix, all rows ruled, each row independently checkable, exclusions named below).
The population — census #18078 @ fb29f62cee; re-verify each row on today's origin/main (lines drift, key + file is the identity)
Genuine durations, unit in no channel (class A-true, 6):
| file:line |
key |
reading |
likely unit (dev measures the producer, ⛔ not this table) |
packages/spec/src/api/contract.zod.ts:311 |
duration |
BaseResponseSchema.meta.duration, beside a string timestamp, no describe/JSDoc |
ms (measure the REST server's writer) |
packages/spec/src/data/field-value.zod.ts:424 |
duration |
FileValueSchema, media length |
seconds? (measure the upload/metadata writer) |
packages/spec/src/kernel/plugin-lifecycle-advanced.zod.ts:413 |
shutdownTimeout |
.default(30000), "Graceful shutdown timeout" |
ms |
packages/spec/src/kernel/plugin-security-advanced.zod.ts:623 |
maxAge |
CORS Access-Control-Max-Age mirror |
seconds by RFC — consider externalVocabulary like its sibling expiresIn |
packages/spec/src/system/auth-config.zod.ts:563 |
updateAge |
.default(60 * 60 * 24), "Session update frequency"; sibling expiresIn carries externalVocabulary: 'better-auth session' |
seconds (better-auth) — same externalVocabulary route as its sibling is legitimate |
packages/spec/src/system/metrics.zod.ts:412 |
slideInterval |
sibling durationSeconds in the same window object |
seconds |
Unit only in JSDoc (class B, 2): packages/spec/src/kernel/plugin-versioning.zod.ts:191 estimatedMigrationTime · packages/spec/src/system/metrics.zod.ts:847 resolution (sibling afterSeconds).
Unit nowhere, outside the census (class D durations, ≥2): packages/spec/src/system/metadata-persistence.zod.ts:259 loadTime, :287 saveTime — bare z.number().optional(), no describe, no JSDoc, beside size and etag.
Plus any genuine duration step ②'s widened detector surfaces (that card routes them here, ⛔ does not convert them).
What each row does — one of two declarations, per the ruling
⚠️ The unit of each row is a measurement of its producer/consumer (who writes the number, in what unit), ⛔ never a guess from the name — a wrong unit declaration is the exact silent ×1000 error this whole program exists to prevent. Print the reading per row in the PR body.
Exclusions (named so the fold is honest)
Acceptance
- After this PR and step ②,
--list shows zero undeclared time-shaped refusals on packages/spec/src; the summary line's declared-duration count equals the rows converted by type.
- Each renamed key carries its ADR-0087 entry, tombstone pin and form/docs/baseline updates per the retirement skill; each typed key carries a pin that a non-integer / negative literal is refused.
Clause-②: the seat derives at claim — a type adoption narrows acceptance (no); a rename retires an authorable key (no, a published surface shrinks) — both carry the BREAKING banner where a producer is measured writing what is now refused.
- Changeset
minor with **BREAKING** banner and ADR-0087 disposition for any renamed or newly-refusing row (pre-GA convention).
Fences
- ⛔ No gate change (step ②); ⛔ no new type (step ①; if a row needs a unit neither type covers, STOP and report — the ruling adds units only on a measured need); ⛔
content/docs/releases/** untouched.
Re-check before claiming
git fetch origin main
git grep -n -E 'export const (DurationMs|DurationSeconds)' origin/main -- packages/spec/src # step ① landed? must be ≥1
cd packages/spec && tsx scripts/check-duration-unit-keys.ts --list | grep -c '\[name: -\] \[prose: -\]' # A-class count today (23; 22 after #18114)
Refs
#18115 (ruling A) · #18080 (ruling C) · #18078 (census §A-true, §B, §D) · #15939 (the parent epic that renamed 21 sibling rows — the precedent for the rename route) · step ① / step ② cards · .claude/skills/spec-property-retirement
Dedup terms: shutdownTimeout DurationMs, loadTime saveTime unit, estimatedMigrationTime, slideInterval, genuine duration conversion, unit-nowhere durations
Generated by Claude Code
Blocked-by: #18122
Filed by the director seat (objectstack #12708, summon #23,
session_01WCEaPsmKY4UyoivKkkaUHt) executing decision batch #134 item 1 — maintainer 「决裁批 #134 同意」 to A on #18115, 2026-09-14T03:44Z. Step ③ of three: the genuine duration rows adopt a declaration.domain:spec; p2;pm:blockeduntil step ① is onorigin/main. Foldable by file family (five-gate fold: same defect shape, same fix, all rows ruled, each row independently checkable, exclusions named below).The population — census #18078 @
fb29f62cee; re-verify each row on today'sorigin/main(lines drift, key + file is the identity)Genuine durations, unit in no channel (class A-true, 6):
packages/spec/src/api/contract.zod.ts:311durationBaseResponseSchema.meta.duration, beside a stringtimestamp, no describe/JSDocpackages/spec/src/data/field-value.zod.ts:424durationFileValueSchema, media lengthpackages/spec/src/kernel/plugin-lifecycle-advanced.zod.ts:413shutdownTimeout.default(30000), "Graceful shutdown timeout"packages/spec/src/kernel/plugin-security-advanced.zod.ts:623maxAgeAccess-Control-Max-AgemirrorexternalVocabularylike its siblingexpiresInpackages/spec/src/system/auth-config.zod.ts:563updateAge.default(60 * 60 * 24), "Session update frequency"; siblingexpiresIncarriesexternalVocabulary: 'better-auth session'externalVocabularyroute as its sibling is legitimatepackages/spec/src/system/metrics.zod.ts:412slideIntervaldurationSecondsin the samewindowobjectUnit only in JSDoc (class B, 2):
packages/spec/src/kernel/plugin-versioning.zod.ts:191 estimatedMigrationTime·packages/spec/src/system/metrics.zod.ts:847 resolution(siblingafterSeconds).Unit nowhere, outside the census (class D durations, ≥2):
packages/spec/src/system/metadata-persistence.zod.ts:259 loadTime,:287 saveTime— barez.number().optional(), no describe, no JSDoc, besidesizeandetag.Plus any genuine duration step ②'s widened detector surfaces (that card routes them here, ⛔ does not convert them).
What each row does — one of two declarations, per the ruling
DurationMs/DurationSeconds(step ①), keep the key name; describe gains the unit. Preferred where the key name is a domain word (duration,resolution,maxAge).shutdownTimeoutMs,loadTimeMs…). This is a RETIREMENT of an authorable key:retiredFromLoadPathconversion + ADR-0087 chain entry + tombstone +**BREAKING**banner atminor(launch-window convention, ADR-0087 amended 2026-09-13), following.claude/skills/spec-property-retirement. Preferred where the name is already unit-suggestive (*Timeout,*Time) and the census's own finding: check:duration-unit-keys reads .describe() but not JSDoc — a duration key documenting its unit only in JSDoc never enters the population, and one card already recorded a wrong reason because of it #15939 sibling rows were renamed that way.externalVocabularymeta stays a legitimate third route where the value mirrors an external contract's unit (maxAge,updateAge) — it is already an exemption class the gate declares.Exclusions (named so the fold is honest)
z.number()wall-clock instants toEpochMs(census #18078 class C ×4 + unit-nowhere instants ×6) — tranche 1 of ruling C on #18080 #18114's, ⛔ not touched here.lamportTimestamp/lamportClock— logical clocks, correctly not time.Acceptance
--listshows zero undeclared time-shaped refusals onpackages/spec/src; the summary line's declared-duration count equals the rows converted by type.Clause-②: the seat derives at claim — a type adoption narrows acceptance (no); a rename retires an authorable key (no, a published surface shrinks) — both carry the BREAKING banner where a producer is measured writing what is now refused.minorwith**BREAKING**banner and ADR-0087 disposition for any renamed or newly-refusing row (pre-GA convention).Fences
content/docs/releases/**untouched.Re-check before claiming
Refs
#18115 (ruling A) · #18080 (ruling C) · #18078 (census §A-true, §B, §D) · #15939 (the parent epic that renamed 21 sibling rows — the precedent for the rename route) · step ① / step ② cards ·
.claude/skills/spec-property-retirementDedup terms:
shutdownTimeout DurationMs,loadTime saveTime unit,estimatedMigrationTime,slideInterval,genuine duration conversion,unit-nowhere durationsGenerated by Claude Code