Filed by the director seat (objectstack #12708, summon #23, session_01WCEaPsmKY4UyoivKkkaUHt) executing decision batch #133 item 4 — maintainer 「决裁批 #133 同意」 to the recommended C on #18080, whose ruling names this as tranche 1 (the B half: instants first, because the remedy already exists in the tree and needs no gate change), 2026-09-14T03:29Z. domain:spec by landing site; p2 (silent ×1000 unit ambiguity on published keys); triage may re-rank.
The population — census #18078 @ fb29f62cee, re-verify on today's origin/main before touching anything
10 bare z.number() wall-clock instants that are not EpochMs (the census's own list; the 6 already-declared EpochMs users are the convention):
| file:line |
key |
how the unit is stated today |
packages/spec/src/data/document.zod.ts:35 |
createdAt |
JSDoc "(Unix milliseconds)" only |
packages/spec/src/data/document.zod.ts:402 |
expiresAt |
JSDoc only |
packages/spec/src/system/supplier-security.zod.ts:146 |
assessedAt |
JSDoc only |
packages/spec/src/system/supplier-security.zod.ts:151 |
validUntil |
JSDoc only |
packages/spec/src/identity/identity.zod.ts:111 |
expiresAt |
describe "Token expiry timestamp (Unix)" — names the epoch, not the unit |
packages/spec/src/kernel/plugin-loading.zod.ts:99 |
timestamp |
nowhere (five lines above durationMs in the same object) |
packages/spec/src/kernel/plugin-loading.zod.ts:154 |
startedAt |
nowhere |
packages/spec/src/kernel/plugin-loading.zod.ts:159 |
completedAt |
nowhere |
packages/spec/src/shared/connector-auth.zod.ts:38 |
tokenExpiry |
nowhere (the one instant INSIDE the census — it matches the expiry token) |
packages/spec/src/system/supplier-security.zod.ts:188 |
deadline |
nowhere ("Remediation deadline timestamp") |
⛔ Not instants, deliberately excluded by the census: system/collaboration.zod.ts:207 lamportTimestamp and :221 lamportClock — logical clocks.
What to do
Each row adopts EpochMs (packages/spec/src/shared/epoch.zod.ts, z.number().int()), keeping its own .describe() and JSDoc (the EpochMs docblock says: name the key *At; ⛔ this card does not rename keys — timestamp, tokenExpiry, deadline, validUntil keep their names; a rename is a separate retirement with its own ADR-0087 entry and is not ruled here). Where a JSDoc says "(Unix milliseconds)" it stays; where nothing says it, the describe gains the unit the type now carries.
Measurements the PR must carry (they decide the changeset shape, ⛔ not the letter)
EpochMs is int(): does any first-party producer (tests, fixtures, examples, plugins) write a non-integer into any of the 10 keys? Grep + run the suites. Zero ⇒ this is alignment to a declared convention (level per AGENTS.md changeset rules, no banner). Any hit ⇒ it is a tightening that refuses a value someone writes today: **BREAKING** banner + ADR-0087 disposition per the launch-window section (level stays minor pre-GA).
- The gate's own summary line
N declared EpochMs instant(s) reads 6 today and 16 after (acceptance reading; run tsx scripts/check-duration-unit-keys.ts --list in packages/spec).
tokenExpiry leaves the census's A-class 23 (it stops being a [name: -] [prose: -] row) — record the before/after --list count.
Clause-②: the seat derives at claim. Prediction for the record: a narrowing of the accepted set on 10 published keys, no new symbol, no widening ⇒ no; if measurement 1 finds a producer, still no (tightening), but the banner applies.
Fences
- ⛔ Durations (census classes A-true, B, D-durations) are tranche 2, gated on the definition sub-card filed alongside this one — do not touch
shutdownTimeout, loadTime, saveTime, estimatedMigrationTime, resolution or any duration row here.
- ⛔ No gate change in
check-duration-unit-keys.ts; this tranche is data-only.
- ⛔
content/docs/releases/** untouched.
Re-check before claiming
git fetch origin main && cd packages/spec
git grep -n -E 'EpochMs' origin/main -- src | wc -l # baseline reference count
tsx scripts/check-duration-unit-keys.ts --list | grep -c 'declared EpochMs' # the summary line exists
Confirm each of the 10 rows is still a bare z.number() at the named line (lines drift; the key + file are the identity).
Refs
#18080 (ruling C, batch #133 item 4) · #18078 (census) · #15676 (ruling B, the instants shape) · #15939 (parent epic, closed) · packages/spec/src/shared/epoch.zod.ts
Dedup terms: EpochMs, bare z.number() instant, tokenExpiry, plugin-loading timestamp startedAt completedAt, supplier-security deadline, census 18078 class C
Generated by Claude Code
Filed by the director seat (objectstack #12708, summon #23,
session_01WCEaPsmKY4UyoivKkkaUHt) executing decision batch #133 item 4 — maintainer 「决裁批 #133 同意」 to the recommended C on #18080, whose ruling names this as tranche 1 (the B half: instants first, because the remedy already exists in the tree and needs no gate change), 2026-09-14T03:29Z.domain:specby landing site; p2 (silent ×1000 unit ambiguity on published keys); triage may re-rank.The population — census #18078 @
fb29f62cee, re-verify on today'sorigin/mainbefore touching anything10 bare
z.number()wall-clock instants that are notEpochMs(the census's own list; the 6 already-declaredEpochMsusers are the convention):packages/spec/src/data/document.zod.ts:35createdAtpackages/spec/src/data/document.zod.ts:402expiresAtpackages/spec/src/system/supplier-security.zod.ts:146assessedAtpackages/spec/src/system/supplier-security.zod.ts:151validUntilpackages/spec/src/identity/identity.zod.ts:111expiresAtpackages/spec/src/kernel/plugin-loading.zod.ts:99timestampdurationMsin the same object)packages/spec/src/kernel/plugin-loading.zod.ts:154startedAtpackages/spec/src/kernel/plugin-loading.zod.ts:159completedAtpackages/spec/src/shared/connector-auth.zod.ts:38tokenExpiryexpirytoken)packages/spec/src/system/supplier-security.zod.ts:188deadline⛔ Not instants, deliberately excluded by the census:
system/collaboration.zod.ts:207 lamportTimestampand:221 lamportClock— logical clocks.What to do
Each row adopts
EpochMs(packages/spec/src/shared/epoch.zod.ts,z.number().int()), keeping its own.describe()and JSDoc (theEpochMsdocblock says: name the key*At; ⛔ this card does not rename keys —timestamp,tokenExpiry,deadline,validUntilkeep their names; a rename is a separate retirement with its own ADR-0087 entry and is not ruled here). Where a JSDoc says "(Unix milliseconds)" it stays; where nothing says it, the describe gains the unit the type now carries.Measurements the PR must carry (they decide the changeset shape, ⛔ not the letter)
EpochMsisint(): does any first-party producer (tests, fixtures, examples, plugins) write a non-integer into any of the 10 keys? Grep + run the suites. Zero ⇒ this is alignment to a declared convention (level per AGENTS.md changeset rules, no banner). Any hit ⇒ it is a tightening that refuses a value someone writes today:**BREAKING**banner + ADR-0087 disposition per the launch-window section (level staysminorpre-GA).N declared EpochMs instant(s)reads 6 today and 16 after (acceptance reading; runtsx scripts/check-duration-unit-keys.ts --listinpackages/spec).tokenExpiryleaves the census's A-class 23 (it stops being a[name: -] [prose: -]row) — record the before/after--listcount.Clause-②: the seat derives at claim. Prediction for the record: a narrowing of the accepted set on 10 published keys, no new symbol, no widening ⇒no; if measurement 1 finds a producer, stillno(tightening), but the banner applies.Fences
shutdownTimeout,loadTime,saveTime,estimatedMigrationTime,resolutionor any duration row here.check-duration-unit-keys.ts; this tranche is data-only.content/docs/releases/**untouched.Re-check before claiming
Confirm each of the 10 rows is still a bare
z.number()at the named line (lines drift; the key + file are the identity).Refs
#18080 (ruling C, batch #133 item 4) · #18078 (census) · #15676 (ruling B, the instants shape) · #15939 (parent epic, closed) ·
packages/spec/src/shared/epoch.zod.tsDedup terms:
EpochMs,bare z.number() instant,tokenExpiry,plugin-loading timestamp startedAt completedAt,supplier-security deadline,census 18078 class CGenerated by Claude Code