Repository navigation
Commit 00c332b
docs(spec): name the millisecond unit in three duration keys' describe() prose (#17953)
Fixes #17786
Clause-②: no
Ruling A on #15939 (director seat, 2026-09-11T14:08Z, maintainer 「同意」,
decision batch #115) split PR #17635's 21-row delta into seven per-file
remediation cards and sequenced the gate itself to land last. This is
the one of the seven that needs no rename:
> …plus **one small card for the three describe-only sites**
(`durationMs` ×2, `estimatedDurationMs`)
## What changed
Three `.describe()` strings — the governed prose channel batch #65
named: *"The governed prose channel is `.describe()` / `.meta({
description })` — the text that reaches the reference pages and the
published dist"*. Each of these keys already carries `Ms` in its name
and already says milliseconds in the JSDoc above it; only the channel an
author (very often a model, ADR-0033) actually reads was missing it.
| site | key | `.describe()` before | `.describe()` after |
|:--|:--|:--|:--|
| `packages/spec/src/kernel/plugin-loading.zod.ts:104` | `durationMs` |
none at all | `Duration in milliseconds` |
| `packages/spec/src/system/app-install.zod.ts:146` | `durationMs` |
`Installation duration` | `Installation duration in milliseconds` |
| `packages/spec/src/system/deploy-bundle.zod.ts:129` |
`estimatedDurationMs` | `Estimated execution time` | `Estimated
execution time in milliseconds` |
Plus the generated mirror of exactly those three rows under
`content/docs/references/**`, and one `patch` changeset.
⛔ **Not a rename.** No key is added, removed or renamed, so there is no
ADR-0087 conversion, no tombstone, and
`packages/spec/src/migrations/registry.ts` is untouched — this PR does
not contend with the open PRs that move that generated file.
## Premise verification, re-measured on this tree
The card's table was measured on `origin/main` @ `fce7cd4c46`; this
branch is cut from `bd25e897d`. All three sites were re-located **by
content**, never by the card's line numbers.
| premise | reading on this tree |
|:--|:--|
| three sites exist, one per file | found by content; the line numbers
happen to be unmoved (104 / 146 / 129) |
| lit control `z.number` per file | 6 / 2 / 7 — identical to the filing
seat's reading |
| dark control (fabricated key name) | 0 / 0 / 0 |
| `plugin-loading` `durationMs` has no describe at all | confirmed — the
whole file carried 2 `.describe(` calls, neither on this key |
| the other two have a describe without a unit | confirmed verbatim |
| every key name already carries its unit | confirmed — `durationMs`,
`durationMs`, `estimatedDurationMs` |
| the diff does not move `migrations/registry.ts` | confirmed — 7
changed paths, none of them that file |
| `Clause-②: no` still holds on the actual diff | confirmed — no key, no
export, no accept/reject outcome moves; `check:api-surface` and
`check:authorable-surface` both green |
| `system/MigrationPlan:estimatedDurationMs` stays put in the authorable
ratchet | confirmed — `authorable-surface.base.json` and
`authorable-surface/system.json` are byte-unchanged and still carry the
entry (dark control 0) |
## The measurement the card asked for: is a changeset owed?
The card and its triage comment both refused to let this be assumed.
Measured after `pnpm --filter @objectstack/spec build`, over the paths
`packages/spec` `files[]` actually publishes:
| probe | `dist/` | `json-schema/` | verdict |
|:--|--:|--:|:--|
| **subject** `Duration in milliseconds` | 24 | 6 | ships |
| **subject** `Installation duration in milliseconds` | 8 | 2 | ships |
| **subject** `Estimated execution time in milliseconds` | 8 | 2 | ships
|
| **positive control** describe `Objects created/updated` | 4 | 2 |
ships, as expected |
| **negative control** `no exemption by blindness` (lives only under
`packages/spec/scripts/`, a path outside `files[]`) | 0 | 0 | does not
ship, as expected |
| **dark control** a fabricated needle | 0 | 0 | 0 everywhere, so the
zeros above are readings |
⇒ **A changeset is owed**, `patch`, `@objectstack/spec`. There is a
second published consumer beyond `dist`: the generated reference pages,
all three of which render the changed string.
⭐ **One measured refinement worth recording, because it cuts against the
obvious reading of "reaches the published dist".** `dist/` alone does
**not** discriminate the two prose channels. JSDoc text — and even a
bare `//` line comment — rides into the emitted bundles verbatim: the
JSDoc-only sentence `Objects created or updated` reads **4** in `dist/`
and **0** in `json-schema/`, and the `//` comment `Plugin loaded at
runtime` reads **4** in `dist/` and **0** in `json-schema/`. So `dist`
presence is necessary and not sufficient evidence that a string reached
the governed channel; `json-schema/` is the surface that decides it. The
ruling is unaffected — describe prose is the only one of the two that
reaches the reference pages and the JSON schemas — but a future round
measuring "does it ship" with a `dist` grep alone would get a true
answer for a false reason.
## Local verification
Heavy runs routed through `scripts/pm/os-verify-lock.sh`; verdicts read
from the lock's own `VERDICT command-exit` line, never a bare status.
- ① dependency closure `pnpm --filter '@objectstack/spec^...' build` —
`VERDICT command-exit 0`, **empty closure** (`No projects matched the
filters`): `packages/spec` has no workspace dependencies, so step ① is a
documented no-op here.
- `pnpm --filter @objectstack/spec build` — `VERDICT command-exit 0`
(203s, shared box), run before every dist-reading gate.
- `pnpm --filter @objectstack/spec typecheck` — `VERDICT command-exit 0`
(`tsc --noEmit` + `check:scripts-typecheck` + `check:test-typecheck`).
- `pnpm --filter @objectstack/spec test` — `VERDICT command-exit 0`,
**475 test files / 13504 tests passed**.
- Targeted re-read of the three sites' own suites —
`deploy-bundle.test.ts` (which pins `estimatedDurationMs: 150`),
`app-install.test.ts`, `plugin-loading.test.ts` — `VERDICT command-exit
0`, 3 files / 40 tests passed.
- `pnpm --filter @objectstack/spec check:generated` proved exactly **1
of 15** artifacts stale — `content/docs/references/**` — and it was
regenerated with the repo's own generator (`pnpm --filter
@objectstack/spec gen:docs`), ⛔ never hand-edited. Re-run after
regeneration: all 15 up to date.
- Gate families derived from the **actual** change set with `node
scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack`, every command run with its exit code landed
to disk, then reconciled with `--ran`. Results are in the report comment
on #17786.
- Consumer sweep, measured rather than predicted: no tracked file
outside `packages/spec` and `content/docs/references` carries any of the
three describe strings, and no test file anywhere names
`plugin-loading.mdx`, `app-install.mdx` or `deploy-bundle.mdx` (lit
control: 145 `.mdx` mentions do exist in test files, so that zero is a
reading). The two cross-package tests that read
`content/docs/references/**` at all read
`automation/time-relative-trigger.mdx` and `api/analytics.mdx`.
## Deviation from the dispatched file surface — declared, not silent
The dispatch declared the file surface as the three `.zod.ts` files plus
`.changeset/`. This PR also carries **three regenerated
`content/docs/references/**` pages**. That is not scope creep:
`check:docs` is a required gate, AGENTS.md requires the spec artifacts
be regenerated before pushing, and `check:generated` named this artifact
and only this artifact as stale. The three regenerated rows are the same
three rows this change edits, byte for byte. Nothing else in
`content/docs/` moved.
## Acceptance notes
- ⛔ `packages/spec/scripts/check-duration-unit-keys.ts` and its
self-test are **untouched** — that gate belongs to PR #17635, which
Ruling A sequences to land last. Its red on that branch is designed and
is not addressed here.
- ⛔ `content/docs/releases/` untouched.
- `noted, not filed:` `packages/spec/src/kernel/plugin-loading.zod.ts`
states every one of its member descriptions in JSDoc and gives only the
enclosing object a `.describe()`, so the generated reference table for
`PluginLoadingEvent` renders empty description cells for `type`,
`pluginId`, `timestamp`, `metadata` and `error`. Only `durationMs` is in
this card's population, so only it was given prose. Successor: whoever
widens the describe-coverage question past duration keys — no PR or
person is queued on that file today, so this is an observation with no
carrier, deliberately not filed as a card.
- `noted, not filed:` the JSDoc channel reaching `dist/` verbatim
(measured above) means the phrase "JSDoc is developer commentary and is
not prose in the sense of the #14478 rule" is a statement about the
**governed** channel, not about what is physically published. Nothing in
the ruling depends on the difference, and no gate reads it. Successor:
none.
⛔ This PR is a draft and stays one. An in-seat clause-② contract review
is owed before it may turn ready or enqueue — the clause-② enqueue
gate's path leg fires on `packages/spec/src/**` regardless of the `no`
declaration. That review is the PM's step.
Authored in session `session_015c5G6TmpMKgnusmTpD7Ntt` (epic PM lane for
the #15939 subtree; the claim comment on #17786 is `5651943470`).
---
_Generated by [Claude
Code](https://claude.ai/code/session_015c5G6TmpMKgnusmTpD7Ntt)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent f62787c commit 00c332b
7 files changed
Lines changed: 24 additions & 6 deletions
File tree
- .changeset
- content/docs/references
- kernel
- system
- packages/spec/src
- kernel
- system
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
43 | 43 | | |
44 | 44 | | |
45 | 45 | | |
46 | | - | |
| 46 | + | |
47 | 47 | | |
48 | 48 | | |
49 | 49 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
86 | 86 | | |
87 | 87 | | |
88 | 88 | | |
89 | | - | |
| 89 | + | |
90 | 90 | | |
91 | 91 | | |
92 | 92 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
189 | 189 | | |
190 | 190 | | |
191 | 191 | | |
192 | | - | |
| 192 | + | |
193 | 193 | | |
194 | 194 | | |
195 | 195 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
101 | 101 | | |
102 | 102 | | |
103 | 103 | | |
104 | | - | |
| 104 | + | |
105 | 105 | | |
106 | 106 | | |
107 | 107 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
143 | 143 | | |
144 | 144 | | |
145 | 145 | | |
146 | | - | |
| 146 | + | |
147 | 147 | | |
148 | 148 | | |
149 | 149 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
126 | 126 | | |
127 | 127 | | |
128 | 128 | | |
129 | | - | |
| 129 | + | |
130 | 130 | | |
131 | 131 | | |
132 | 132 | | |
| |||
0 commit comments