Repository navigation
cutover-prep(ui-merge): the 9 @object-ui/types duration keys carry their unit in the key name, with ADR-0087 conversions, before the freeze (parent #25677, decision E) #25762
Description
Filing gate ③ + ④: pre-freeze prep card of the epic #25677 (pm:epic; epic PM session_01RSDL3SFtJaKzcymvCD8RsU), decided by the maintainer as cutover decision E (director thread, 2026-10-25 09:06Z: 「17e · 同意」). Part of #25677. Lands in objectstack-ai/objectui main before the cutover freeze (#25752 step 2); the objectstack side needs no change.
谁付代价(业务筛): after the cutover, objectstack's spec gate check:duration-unit-keys runs over ui/packages/types/src/zod and refuses 9 keys whose description names a unit the key name lacks. Either every ui author keeps reading timeout: 300 without knowing whether it is ms or s (the defect ADR-0087 retired on the objectstack side in #15679/#15680), or the gate is exempted for ui/, which keeps two vocabularies in one repo. The maintainer chose the rename.
维护者速读
objectui 的 @object-ui/types 里 9 个时长键(autoPlay、autoResponseDelay、timeout、delay、duration、delayDuration、openDelay、closeDelay、refreshInterval)只在描述里写单位,键名没有。合并后 objectstack 的 check:duration-unit-keys 会把它们判红。你裁定改名(E),本卡在冻结前于 objectui 完成:键名带单位、旧拼写按 ADR-0087 的方式退役(拒绝并给出修法)、附转换项与 major changeset。
What to change (objectui, one draft PR to main)
- The 9 keys, as the gate names them on the merged tree (verify run 8 head 3b0be7c,
pnpm --filter @objectstack/spec check:duration-unit-keys):autoPlay,autoResponseDelay,timeout,delay,duration,delayDuration,openDelay,closeDelay,refreshInterval— all underui/packages/types/src/zod/**. Re-measure on the merged layout first (recipe below); the list is a lead, the gate's output is the spec. - Each key is renamed so the name carries the unit its description states (the objectstack precedent is [#14478 stack 4/6]
system/: the 15 remaining duration keys carry their unit in the key name — ADR-0087 conversions with readers;metrics.zod.tssizeneeds an honest name, not the mechanical one #15679/[#14478 stack 5/6]data/·ui/·ai/·integration/: the 7 remaining duration keys carry their unit in the key name — ADR-0087 conversions with readers #15680:…Ms/…Secondsas the description says; read the description, do not assume ms). - The old spelling is retired the way ADR-0087 prescribes, adapted to objectui: an author who still writes the old key gets a refusal that names the new key (objectui's retired-key / guidance mechanism if it has one — measure:
grep -rn "retiredKey\|UNKNOWN_KEY_GUIDANCE\|spec-changes" ui/packages/types ui/scripts— otherwise a.strict()refusal with the guidance in the error text), plus a conversion entry where objectui keeps a conversion registry, plus amajorchangeset carrying the FROM → TO mapping for every key. - Every reader of the 9 keys inside objectui (renderers, plugins, tests, docs snippets, examples, QUICK_REFERENCE rows) follows the rename in the same PR;
pnpm type-check, the affectedpnpm test,pnpm check:spec-symbolsand the docs/snippet gates stay green.
Acceptance
- On the merged layout (verify branch
claude/project-thread-dwhq6r-25697-verifyhead 9e37acd + your diff applied withgit apply --directory=ui),pnpm --filter @objectstack/spec check:duration-unit-keysreports 0ui/findings and the objectstack-side findings are unchanged. - objectui standalone:
pnpm type-check, affectedpnpm test,pnpm check:spec-symbols, docs gates green; CI green on the draft PR. - The PR body lists the 9 FROM → TO pairs, the retirement mechanism used, and the changeset.
Size: M. Clause-②: yes (new authorable keys enter @object-ui/types's published zod; old ones are refused with guidance). Metadata-protocol: added (new unit-bearing keys) + the old spellings refused. Responsibility: n/a — not a defect card.
风险
- Consumers outside objectui (customer apps on
@object-ui/types) see amajorfor 9 renames; the conversion + guidance is the mitigation, and 18.x is the version line that already carries the merge. - If objectui has no conversion registry, the card's deliverable for "conversions" is the
majorchangeset + the refusal guidance; say so in the PR body rather than inventing a registry.
Dedupe
search_issues 2026-10-25 09:12Z 「duration-unit-keys ui zod rename」: 7 hits, all closed objectstack-side ADR-0087 remediations (#15679, #15680, #17780–#17785, #18075); none covers @object-ui/types. New.
Activity
objectstack-fleet commentedon Oct 25, 2026
Claim: PM loop round 1
Session:
session_019dczBdb6awqr3txksmEa9WAccount:
os-tesla(the seat's linked user; the card's assignee)Branch:
claude/project-thread-2kvihmWorktree:
objectui-issue-25762Domain:
domain:devxSeat:
domain:devx#1File surface:
ui/packages/types/src/zod/** and every objectui reader of the 9 keys (renderers, plugins, tests, docs snippets, examples, QUICK_REFERENCE rows), .changeset/**(stop on breach; explain in the report)Container & model:
M,mode:thread,model: CONTRACT_REVIEW_TIER (contract review tier; reason: Clause-② yes — the card renames 9 authorable keys in the @object-ui/types zod contract with tombstones, a public-contract change under the pm-dispatch Clause-② tier rule),effort: xhighClause-②: yes
Metadata-protocol: added (new unit-bearing keys; old spellings refused with guidance)
Responsibility: n/a — not a defect card
Thread-read: none
Premise: holds
Serial constraints cleared: objectui #25727 2c round 2 on claude/project-thread-gxm6ze is in flight in the same repo (pin, plugin-chatbot, plugin-dashboard SKILL.md, ADR-0075 spelling, auth, DraftChangesPanel test); no shared file expected — report to the dispatcher before touching any of those
Resume-from: none
Dispatched by the epic seat (
session_01RSDL3SFtJaKzcymvCD8RsU) under the pm-dispatch frame; order:/mnt/project-files/d1-merge/dispatch/(E-25762 / F-25763). Part of #25677.Generated by Claude Code
objectstack-fleet commentedon Oct 25, 2026
Cutover prep E: draft PR up, CI green. Posted 2026-10-25T10:56Z.
PR: objectstack-ai/objectui#13515 (draft), head
a63bbdf3fd. The head holds the rename commit117dea6, the docs commit7396eb3, and a merge of objectuimainbe64bf6, which brought in objectui#13512 (pin5874d25302) and objectui#13513.The nine renames
carousel(CarouselSchema)autoPlayautoPlayMschatbot,chatbot-enhanced,chatbot-floatingautoResponseDelayautoResponseDelayMsActionSchematimeouttimeoutMsActionSchema.retrydelaydelayMstoast(ToastSchema)durationdurationMstooltip(TooltipSchema)delayDurationdelayDurationMshover-card(HoverCardSchema)openDelayopenDelayMshover-card(HoverCardSchema)closeDelaycloseDelayMsreport(ReportComponentSchema)refreshIntervalrefreshIntervalSecondsHow the old names are retired
objectui has no conversion registry, so the conversion is a refusal plus the changeset table.
retirementTombstone, objectui's counterpart ofretiredKey. A new builder,durationKeyRenamedGuidance(retired, renamed, unit), writes its message. A value written under the old name is refused at the key's own path withinvalid_type. The message names the new key: "timeoutis nowtimeoutMs… the value (milliseconds) is unchanged".?: neverand marked@deprecated..changeset/25762-duration-keys-carry-unit.mdisminoron@object-ui/types,components,plugin-chatbotandplugin-report. It carriesClause-②: yes,Metadata-protocol: addedand the FROM → TO table.The
toast,tooltipandhover-cardrenderers, the three chatbot registrations andReportViewernow read the new keys. So do the docs, three schema-catalog examples and the tests that used the old names. A new test file,duration-unit-key-renames-25762.test.ts, covers all nine renames.Acceptance
The merged-layout check started from verify head
9e37acd126, applied this diff withgit apply --directory=ui, ranpnpm install --frozen-lockfile, then ranpnpm --filter @objectstack/spec check:duration-unit-keys:ui/offendersOn the merged head with the new pin, re-run locally:
type-checkfor@object-ui/types,components,plugin-chatbotandplugin-reportexits 0.packages/types,plugin-chatbot,plugin-reportand the overlay, feedback and complex renderers: 533 files and 11879 tests pass.The doc and symbol gates were run on the base before the merge, and all exit 0:
check:spec-symbols,check:doc-snippets,check:doc-examples,check:doc-json,check:doc-typesand the rest. The per-gate table is in the PR body.CI on
a63bbdf3fd: 36 check runs, 33 success and 3 skipped (the coverage and dependabot jobs), 0 failed.Deviations
minor, notmajor, because objectui's version policy andcheck-changeset-no-majorrefusemajor..strict(). The mirrors are.passthrough()by design, and.strict()would refuse every undeclared key on nine node types.useObjectChathook optionautoResponseDelaykeeps its name. It is a programmatic API, not a schema key.Not widened (left for a separate decision): nothing reads
autoPlayMs,timeoutMsorretry.delayMs, before or after this PR. The carousel spreads its props onto adiv, soautoPlayMslands there as an unknown attribute.Generated by Claude Code
objectstack-fleet commentedon Oct 25, 2026
ACCEPT — dispatch seat. objectui#13515 (head
a63bbdf3fd, branchclaude/project-thread-2kvihm, +422/−103, 32 files) is accepted as the fix for this card.Checked against the order (
/mnt/project-files/d1-merge/dispatch/E-25762-objectui-duration-keys.md) and the seat's report (/mnt/project-files/d1-merge/out/E-25762-final-report.md, card comment 6396767633):retirementTombstones with guidance (metadata-protocol: added). Deviation 2 accepted: the mirrors are.passthrough()by design and objectui has no conversion registry, so a tombstone per key is the refusal that exists.ui/): objectstack'scheck:duration-unit-keysexit 1 with 9ui/offenders before → exit 0 with 0 after; objectstack side 0 in both.minor— objectui's AGENTS.md andcheck-changeset-no-majorrefusemajor, and the fixed group moves to 18.2.0 together. Deviation 3 accepted:useObjectChat'sautoResponseDelayis a hook option, not an authorable schema key, so the key gate does not reach it. Deviations 4–6 accepted (docs commit forcheck:doc-json;mainmerged at the dispatcher's request; no assignee/reviewer step under the frame).check-governed-merges --pr objectstack-ai/objectui#13515on the final file list: NOT governed (0 of 32 paths hit the register), 525 changed lines ≤ 3000 — ordinary queue landing.autoPlayMs, ActionSchematimeoutMsorretry.delayMs/maxAttemptsbefore or after (theenhanced-actions.mdx"Retry Logic" section describes unimplemented behaviour); the carousel spreads its props onto adiv, so an authoredautoPlayMsnow lands as an unknown attribute whereautoPlayused to pass asautoplay.Landing: the seat flips the PR ready and arms auto-merge via the relay now. The PR body says
Part of objectstack#25762(cross-repo, no auto-close), so the seat closes this card with a landing note once the merge lands; the cutover pin bump (#25752 step 2) picks the merged objectuimainup.Generated by Claude Code
objectstack-fleet commentedon Oct 25, 2026
Landed. objectui#13515 (head
a63bbdf3fd) MERGED into objectuimainas1212e3ef75on 2026-10-25 12:12Z (merge queue, auto-merge armed by the dispatch seat after the ACCEPT above). The 9 duration keys now carry their unit in the name on all 11 schema sites, the old spellings are refused by named tombstones with guidance, and on the merged layout objectstack'scheck:duration-unit-keysreads 0ui/offenders (was 9). Residue (unreadautoPlayMs/timeoutMs/retry.*keys; the carousel's unknown-attribute spread) is on the cutover bill as item 31 for the aftercare card #25753. The cutover pin bump (#25752 step 2) picks thismainup. Closing this card; the parent #25677 ticks prep card E.Generated by Claude Code