Skip to content

docs(spec): name the millisecond unit in three duration keys' describe() prose - #17953

Draft
claude[bot] wants to merge 2 commits into
mainfrom
claude/issue-17786-duration-describe-units
Draft

docs(spec): name the millisecond unit in three duration keys' describe() prose#17953
claude[bot] wants to merge 2 commits into
mainfrom
claude/issue-17786-duration-describe-units

Conversation

@claude

@claude claude Bot commented Sep 13, 2026

Copy link
Copy Markdown
Contributor

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^...' buildVERDICT 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 buildVERDICT command-exit 0 (203s, shared box), run before every dist-reading gate.
  • pnpm --filter @objectstack/spec typecheckVERDICT command-exit 0 (tsc --noEmit + check:scripts-typecheck + check:test-typecheck).
  • pnpm --filter @objectstack/spec testVERDICT 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.tsVERDICT 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 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.
  • 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 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.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 spec: duration-shaped number keys carry their unit in describe prose only — two ttl keys with different units in one block, bare timeout keys, 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 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

`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>
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation protocol:system tooling labels Sep 13, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

3 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to listnot 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
  • the SDK route bridge reached 60 of 215 client-bound route-ledger rows — the other 155 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 155: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 100 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 136 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 225197cdbaff647a8b12e52bcb9d7bbb5b07ed08packageMentionDocs.

Which tree this was computed on

This run read content/docs from d2ab4e4a79caa80823a4c6c75ed530e11f5f46a2 — the merge of head eac52ecb7b94d18cf0a16d52f43132fc3bf19ed3 into base 225197cdbaff647a8b12e52bcb9d7bbb5b07ed08, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# 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

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation protocol:system size/s tooling

Projects

None yet

1 participant