…ctstack#25762)
Each of these keys stated its unit only in its describe prose, which
objectstack's `check:duration-unit-keys` refuses once objectui merges into
objectstack (maintainer decision E on objectstack#25677). Renamed the way
objectstack renamed its own (`...Ms` / `...Seconds`, the unit the describe
already stated):
carousel autoPlay -> autoPlayMs
chatbot* autoResponseDelay -> autoResponseDelayMs
ActionSchema timeout -> timeoutMs
ActionSchema retry.delay -> retry.delayMs
toast duration -> durationMs
tooltip delayDuration -> delayDurationMs
hover-card openDelay -> openDelayMs
hover-card closeDelay -> closeDelayMs
report refreshInterval -> refreshIntervalSeconds
Each old spelling stays declared as a retirement tombstone whose message
names the new key (new `durationKeyRenamedGuidance` in tombstone.zod.ts),
and its TypeScript twin is `?: never`. Renderers, registration inputs and
defaults, docs snippets, catalog examples and tests follow the rename. The
changeset carries the FROM -> TO table; it is `minor` per this repo's
version policy.
Part of objectstack#25677. Refs objectstack#25762.
Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019dczBdb6awqr3txksmEa9W
Requested by Tesla · project thread
Part of objectstack#25762 (cutover prep E of the epic). Part of objectstack#25677.
Clause-②: yes
Metadata-protocol: added
Why
After objectui merges into objectstack as
ui/, objectstack's spec gatecheck:duration-unit-keyswalksui/packages/types/src/zodtoo. It refuses a numeric key whose describe names a unit that the key name lacks. Nine keys here do that, sotimeout: 300reads as either milliseconds or seconds. The maintainer chose to rename them (decision 17e = A, director thread 2026-10-25 09:06Z). Exemptingui/from the gate was the rejected option. The renames follow objectstack's own (objectstack#15679 / objectstack#15680):…Ms/…Seconds, the unit each describe already stated.The nine renames
carousel(CarouselSchema)autoPlayautoPlayMschatbot,chatbot-enhanced,chatbot-floatingautoResponseDelayautoResponseDelayMsActionSchematimeouttimeoutMsActionSchema.retrydelaydelayMstoast(ToastSchema)durationdurationMstooltip(TooltipSchema)delayDurationdelayDurationMshover-card(HoverCardSchema)openDelayopenDelayMshover-card(HoverCardSchema)closeDelaycloseDelayMsreport(ReportComponentSchema)refreshIntervalrefreshIntervalSecondsEach name is the one the gate itself suggests in its finding line.
Retirement mechanism
objectui has no conversion registry. A grep for
retiredKey,UNKNOWN_KEY_GUIDANCE,spec-changesandconversionsunderpackages/typesandscriptsfinds only comments that cite the spec's helper. So, as the card allows, the conversion is the refusal plus the changeset table:retirementTombstone(the objectui counterpart of the spec'sretiredKey). The tombstone is fed by a new guidance builder,durationKeyRenamedGuidance(retired, renamed, unit)inzod/tombstone.zod.ts. An authored value is refused at the key's own path with codeinvalid_type, and the message names the new key: "RENAMED (objectstack#25762, ADR-0087) —timeoutis nowtimeoutMs… the value (milliseconds) is unchanged." Without the tombstone,.passthrough()would keep the value unread. On the two chatbot twins, the tombstone is picked offChatbotSchemathe same waymaxToolRoundtripsis.?: neverwith@deprecated, so a typed literal fails to compile..changeset/25762-duration-keys-carry-unit.mddeclaresminoron@object-ui/types,components,plugin-chatbotandplugin-report, and carries the FROM → TO table.Readers that follow the rename
toast,tooltipandhover-cardrenderers (@object-ui/components): the read, the registrationinputsanddefaultProps.@object-ui/plugin-chatbot): the read,inputsanddefaultProps. TheuseObjectChathook option keeps the nameautoResponseDelay. It is a programmatic API, not a schema key (the same pattern asbody: schema.requestBody).ReportViewer(@object-ui/plugin-report) readsreport.refreshIntervalSeconds.toast,tooltip,hover-card,plugin-chatbot,report-schema,enhanced-actions), three schema-catalog examples, and the tests that authored the old keys.packages/types/src/__tests__/duration-unit-key-renames-25762.test.ts. For each rename it checks that the new key accepts a number and refuses a string, and that the old key is refused at its own path naming the new key. It also has@ts-expect-errorpins for every TypeScript twin.Verification
Head
a63bbdf3fd: the card's two commits (117dea6rename,7396eb3docs) plus a merge of objectuimainbe64bf6. That merge brought in #13512 and #13513 at the dispatcher's request, with no conflict. The only shared file wascontent/docs/plugins/plugin-chatbot.mdx, in a different section. The local runs below were on base0063f22ce(pin43edf90b56). CI on this head covers the merged base and the new pin.Merged layout (the card's acceptance). Verify head
9e37acd126, plusgit diff 0063f22ce..117dea6applied withgit apply --directory=ui(the docs commit touches nosrc/), thenpnpm install --frozen-lockfile, thenpnpm --filter @objectstack/spec check:duration-unit-keys:ui/offendersunit-in-prose-not-in-name)objectui standalone.
type-check:@object-ui/types(src, examples, tests),components,plugin-chatbot,plugin-reportpackages/types/phase2-schemasfailed because it authoredtimeout/retry.delay; its fixture is renamed in this PR, and the file re-runs 35/35packages/components/+examples/schema-catalog/packages/plugin-chatbot/+packages/plugin-report/+ the overlay / feedback / complex renderersapps/console/check:spec-symbols,check:doc-snippets,check:doc-examples,check:doc-json,check:doc-types,check:readme-exports,check:skill-examples(after the scoped build each one asks for)check:doc-jsonfirst answered 1 onschema-reference.md'sretry.delay, which is fixed in7396eb3check:component-surface-parity,check:handler-key-reads,check:prompt-keys,check:action-forward-parity,check:designer-field-key-parity,check:doc-example-readers,check:registry-bare-namescheck:new-line-citations,check:control-bytes,check:changeset-claims,check:pending-changeset-literals.ts/.tsxfilesno-explicit-any)check-governed-queue-guard --teston the file listAblation. With the five
zod/*.zod.tsfiles reverted to0063f22ce, the new pin file reads 22 failed / 13 passed. Restored, it reads 35/35.Deviations from the order
minor, notmajor. The card asks for amajorchangeset. objectui's AGENTS.md version policy forbidsmajorin the fixed group (objectui's major follows@objectstack's), andscripts/check-changeset-no-major.mjsrefuses it in CI. So the bump isminor, and the body states the breaking semantics and the FROM → TO table..strict(). These mirrors are.passthrough()by design. A.strict()refusal would refuse every undeclared key on nine node types, which is wider than the card. objectui's own counterpart of the spec'sretiredKeyisretirementTombstone, and it refuses exactly the old key with a message that names the new one.useObjectChat'sautoResponseDelayoption is not renamed. It is the hook's programmatic API, not a schema key, and the gate does not judge it. The renderer mapsschema.autoResponseDelayMsonto it.7396eb3is theschema-reference.mdJSON fence thatcheck:doc-jsonrefused.Residual, not widened (reported to the dispatcher).
autoPlayMs(carousel),timeoutMsandretry.delayMs/retry.maxAttempts(ActionSchema) have no renderer read, before or after this PR. They are declared but inert, andenhanced-actions.mdx's "Retry Logic" section describes behaviour that nothing implements. The carousel registration spreads its props onto adiv. An authoredautoPlayused to land there as a validautoplayattribute; an authoredautoPlayMsnow lands as an unknown one. Enforcing or removing these keys is a separate decision.🤖 Generated with Claude Code
https://claude.ai/code/session_019dczBdb6awqr3txksmEa9W