docs(spec): name the millisecond unit in three duration keys' describe() prose - #17953
docs(spec): name the millisecond unit in three duration keys' describe() prose#17953claude[bot] wants to merge 2 commits into
Conversation
`durationMs` (kernel/plugin-loading.zod.ts, system/app-install.zod.ts) and `estimatedDurationMs` (system/deploy-bundle.zod.ts) already carry their unit in the key name and in the JSDoc above them, but the `.describe()` prose — the channel that reaches the published reference pages and the published dist — named no unit at all, and plugin-loading's key carried no describe whatsoever. Prose only: no key added, no export moved, no accept/reject outcome changed. Claude-Session: https://claude.ai/code/session_015c5G6TmpMKgnusmTpD7Ntt Co-authored-by: Claude <noreply@anthropic.com>
…hed-surface measurement `pnpm --filter @objectstack/spec check:generated` proved exactly one artifact stale — `content/docs/references/**` — and it was regenerated with the repo's own generator (`gen:docs`), never hand-edited. The three rows that moved are the three describe strings this change edits. The changeset records the measurement the card asked for: the changed describe text reaches the published `dist/` bundles and `json-schema/`, with a positive control that ships, a negative control outside `files[]` that does not, and a dark control at zero. Claude-Session: https://claude.ai/code/session_015c5G6TmpMKgnusmTpD7Ntt Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift Check3 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to list — not a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run. What this run could not see
Coarse fallback — 136 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin d2ab4e4a79caa80823a4c6c75ed530e11f5f46a2 && git checkout d2ab4e4a79caa80823a4c6c75ed530e11f5f46a2
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 225197cdbaff647a8b12e52bcb9d7bbb5b07ed08 eac52ecb7b94d18cf0a16d52f43132fc3bf19ed3 && git checkout -B drift-repro 225197cdbaff647a8b12e52bcb9d7bbb5b07ed08 && git merge --no-ff eac52ecb7b94d18cf0a16d52f43132fc3bf19ed3
node scripts/docs-audit/affected-docs.mjs --json 225197cdbaff647a8b12e52bcb9d7bbb5b07ed08 |
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:
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 carriesMsin 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..describe()before.describe()afterpackages/spec/src/kernel/plugin-loading.zod.ts:104durationMsDuration in millisecondspackages/spec/src/system/app-install.zod.ts:146durationMsInstallation durationInstallation duration in millisecondspackages/spec/src/system/deploy-bundle.zod.ts:129estimatedDurationMsEstimated execution timeEstimated execution time in millisecondsPlus the generated mirror of exactly those three rows under
content/docs/references/**, and onepatchchangeset.⛔ 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.tsis 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 frombd25e897d. All three sites were re-located by content, never by the card's line numbers.z.numberper fileplugin-loadingdurationMshas no describe at all.describe(calls, neither on this keydurationMs,durationMs,estimatedDurationMsmigrations/registry.tsClause-②: nostill holds on the actual diffcheck:api-surfaceandcheck:authorable-surfaceboth greensystem/MigrationPlan:estimatedDurationMsstays put in the authorable ratchetauthorable-surface.base.jsonandauthorable-surface/system.jsonare 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 pathspackages/specfiles[]actually publishes:dist/json-schema/Duration in millisecondsInstallation duration in millisecondsEstimated execution time in millisecondsObjects created/updatedno exemption by blindness(lives only underpackages/spec/scripts/, a path outsidefiles[])⇒ A changeset is owed,
patch,@objectstack/spec. There is a second published consumer beyonddist: 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 sentenceObjects created or updatedreads 4 indist/and 0 injson-schema/, and the//commentPlugin loaded at runtimereads 4 indist/and 0 injson-schema/. Sodistpresence 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 adistgrep 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 ownVERDICT command-exitline, never a bare status.pnpm --filter '@objectstack/spec^...' build—VERDICT command-exit 0, empty closure (No projects matched the filters):packages/spechas 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.deploy-bundle.test.ts(which pinsestimatedDurationMs: 150),app-install.test.ts,plugin-loading.test.ts—VERDICT command-exit 0, 3 files / 40 tests passed.pnpm --filter @objectstack/spec check:generatedproved 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.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 spec: three duration keys already name their unit in the key but not in.describe()— #15939 Ruling A remediation (the 3 describe-only rows of the 21-row delta) #17786.packages/specandcontent/docs/referencescarries any of the three describe strings, and no test file anywhere namesplugin-loading.mdx,app-install.mdxordeploy-bundle.mdx(lit control: 145.mdxmentions do exist in test files, so that zero is a reading). The two cross-package tests that readcontent/docs/references/**at all readautomation/time-relative-trigger.mdxandapi/analytics.mdx.Deviation from the dispatched file surface — declared, not silent
The dispatch declared the file surface as the three
.zod.tsfiles plus.changeset/. This PR also carries three regeneratedcontent/docs/references/**pages. That is not scope creep:check:docsis a required gate, AGENTS.md requires the spec artifacts be regenerated before pushing, andcheck:generatednamed 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 incontent/docs/moved.Acceptance notes
packages/spec/scripts/check-duration-unit-keys.tsand its self-test are untouched — that gate belongs to PR feat(spec): refuse a duration key whose JSDoc names a unit its describe does not #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.tsstates every one of its member descriptions in JSDoc and gives only the enclosing object a.describe(), so the generated reference table forPluginLoadingEventrenders empty description cells fortype,pluginId,timestamp,metadataanderror. OnlydurationMsis 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 reachingdist/verbatim (measured above) means the phrase "JSDoc is developer commentary and is not prose in the sense of the spec: duration-shaped number keys carry their unit in describe prose only — twottlkeys with different units in one block, baretimeoutkeys, unit-less tenant timeouts #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 thenodeclaration. That review is the PM's step.Authored in session
session_015c5G6TmpMKgnusmTpD7Ntt(epic PM lane for the #15939 subtree; the claim comment on #17786 is5651943470).Generated by Claude Code