Skip to content

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

Closed

Description

@objectstack-fleet

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)

  1. 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 under ui/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.
  2. 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.ts size needs 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 / …Seconds as the description says; read the description, do not assume ms).
  3. 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 a major changeset carrying the FROM → TO mapping for every key.
  4. 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 affected pnpm test, pnpm check:spec-symbols and the docs/snippet gates stay green.

Acceptance

  • On the merged layout (verify branch claude/project-thread-dwhq6r-25697-verify head 9e37acd + your diff applied with git apply --directory=ui), pnpm --filter @objectstack/spec check:duration-unit-keys reports 0 ui/ findings and the objectstack-side findings are unchanged.
  • objectui standalone: pnpm type-check, affected pnpm 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 a major for 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 major changeset + 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

  1. objectstack-fleet commented on Oct 25, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1
    Session: session_019dczBdb6awqr3txksmEa9W
    Account: os-tesla (the seat's linked user; the card's assignee)
    Branch: claude/project-thread-2kvihm
    Worktree: objectui-issue-25762
    Domain: domain:devx
    Seat: domain:devx#1
    File 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: xhigh
    Clause-②: 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

  2. objectstack-fleet commented on Oct 25, 2026

    @objectstack-fleet
    ContributorAuthor

    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 commit 117dea6, the docs commit 7396eb3, and a merge of objectui main be64bf6, which brought in objectui#13512 (pin 5874d25302) and objectui#13513.

    The nine renames

    Node / schema FROM TO Unit
    carousel (CarouselSchema) autoPlay autoPlayMs ms
    chatbot, chatbot-enhanced, chatbot-floating autoResponseDelay autoResponseDelayMs ms
    ActionSchema timeout timeoutMs ms
    ActionSchema.retry delay delayMs ms
    toast (ToastSchema) duration durationMs ms
    tooltip (TooltipSchema) delayDuration delayDurationMs ms
    hover-card (HoverCardSchema) openDelay openDelayMs ms
    hover-card (HoverCardSchema) closeDelay closeDelayMs ms
    report (ReportComponentSchema) refreshInterval refreshIntervalSeconds seconds

    How the old names are retired

    objectui has no conversion registry, so the conversion is a refusal plus the changeset table.

    • zod. Each old key stays declared as a retirementTombstone, objectui's counterpart of retiredKey. 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 with invalid_type. The message names the new key: "timeout is now timeoutMs … the value (milliseconds) is unchanged".
    • TypeScript. Each old member is ?: never and marked @deprecated.
    • Changeset. .changeset/25762-duration-keys-carry-unit.md is minor on @object-ui/types, components, plugin-chatbot and plugin-report. It carries Clause-②: yes, Metadata-protocol: added and the FROM → TO table.

    The toast, tooltip and hover-card renderers, the three chatbot registrations and ReportViewer now 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 with git apply --directory=ui, ran pnpm install --frozen-lockfile, then ran pnpm --filter @objectstack/spec check:duration-unit-keys:

    before after
    exit 1 0
    ui/ offenders 9 0
    objectstack-side offenders 0 0
    census 210 keys / 4321 files 210 / 4321

    On the merged head with the new pin, re-run locally:

    • type-check for @object-ui/types, components, plugin-chatbot and plugin-report exits 0.
    • vitest over packages/types, plugin-chatbot, plugin-report and 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-types and 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

    1. The changeset is minor, not major, because objectui's version policy and check-changeset-no-major refuse major.
    2. The old keys are refused by named tombstones rather than .strict(). The mirrors are .passthrough() by design, and .strict() would refuse every undeclared key on nine node types.
    3. The useObjectChat hook option autoResponseDelay keeps its name. It is a programmatic API, not a schema key.

    Not widened (left for a separate decision): nothing reads autoPlayMs, timeoutMs or retry.delayMs, before or after this PR. The carousel spreads its props onto a div, so autoPlayMs lands there as an unknown attribute.


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 25, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT — dispatch seat. objectui#13515 (head a63bbdf3fd, branch claude/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):

    • The 9 duration keys carry their unit in the name on all 11 schema sites (the chatbot key sits on three twins); the old spellings are refused by named 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.
    • Merged layout (verify head 9e37acd + diff under ui/): objectstack's check:duration-unit-keys exit 1 with 9 ui/ offenders before → exit 0 with 0 after; objectstack side 0 in both.
    • Ablation: the new pin test fails 22 of 35 with the old zod files and passes 35/35 with the new ones.
    • Merged head on the new pin 5874d25: type-check of types / components / plugin-chatbot / plugin-report exit 0; vitest 533 files / 11,879 tests green. Base: spec-symbols, the doc gates (doc-json fixed by the docs commit), parity/reads gates, changeset gates, ESLint all exit 0.
    • Deviation 1 accepted: the changeset is minor — objectui's AGENTS.md and check-changeset-no-major refuse major, and the fixed group moves to 18.2.0 together. Deviation 3 accepted: useObjectChat's autoResponseDelay is a hook option, not an authorable schema key, so the key gate does not reach it. Deviations 4–6 accepted (docs commit for check:doc-json; main merged at the dispatcher's request; no assignee/reviewer step under the frame).
    • CI on the PR head: 36 runs, 33 success, 3 skipped (coverage, dependabot), 0 failed. check-governed-merges --pr objectstack-ai/objectui#13515 on the final file list: NOT governed (0 of 32 paths hit the register), 525 changed lines ≤ 3000 — ordinary queue landing.
    • Writes: 2 relay writes (pr_create, one card comment), 0 MCP writes, no review requested.
    • Residual, recorded on the cutover bill (item 31) for the aftercare card aftercare(ui-merge): the first joint 18.x release from the merged repo — both scopes at one version, @object-ui published, docs site deployed — then close #25556 as superseded and the epic (parent #25677, 6/6) #25753, not for this card: nothing reads carousel autoPlayMs, ActionSchema timeoutMs or retry.delayMs / maxAttempts before or after (the enhanced-actions.mdx "Retry Logic" section describes unimplemented behaviour); the carousel spreads its props onto a div, so an authored autoPlayMs now lands as an unknown attribute where autoPlay used to pass as autoplay.

    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 objectui main up.


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 25, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed. objectui#13515 (head a63bbdf3fd) MERGED into objectui main as 1212e3ef75 on 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's check:duration-unit-keys reads 0 ui/ offenders (was 9). Residue (unread autoPlayMs / 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 this main up. Closing this card; the parent #25677 ticks prep card E.


    Generated by Claude Code

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

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions