Skip to content

finding(fields): DateTimeCellRenderer and DateCellRenderer accept DIFFERENT field.format vocabularies — format: 'relative' is honoured on a date column and silently dropped on a datetime one #8853

Closed

Description

@claude

Found while implementing objectui#8352 (the same defect class on the dataset-measure path) by the objectui domain:ui seat, session session_01611D6ZaRaMmwTNQmSbk8MH. Filed rather than fixed there: different package, different surface, and it needs its own verification surface. Not claimed, no code written.

The asymmetry

packages/fields/src/index.tsx, the two neighbouring cell renderers:

:919   DateCellRenderer      const style = dateField.format || 'relative';
:928                         formatDate(safe, style, { dueLike, locale, t });

:970   DateTimeCellRenderer  const style = field.format || 'compact';
:994                         formatDateTime(date, { style, locale, t });

Both read the author's field.format and hand it to a formatter. The formatters do not accept the same vocabulary:

  • formatDate honours 'short' and 'relative'; anything else falls to the default locale face.
  • formatDateTime honours 'compact' alone; anything else falls to the verbose default.

⇒ An author who writes format: 'relative' on a field gets the relative face on a date column and silently the verbose default on a datetime column. No error, no warning, no fallback — the same silent-drop signature as objectui#8352, one surface over. The reverse holds too: format: 'compact' is honoured on a datetime column and inert on a date one.

DateDisplayOptions.style's own docblock states the split as intended for the functions ("'compact' is formatDateTime's dense grid cell face, 'short' and 'relative' are formatDate's"). What is not stated anywhere is that the two CELL renderers therefore accept different authored vocabularies for the same key name on the same metadata surface.

Measured vs read

Measured (unit level, TZ=UTC, en-US, clock pinned to 2026-09-09T12:00:00Z, value 2026-09-11T09:30:00.000Z):

call output
formatDateTime(v, { locale }) Sep 11, 2026, 09:30 AM
formatDateTime(v, { locale, style: 'relative' }) Sep 11, 2026, 09:30 AM — unchanged
formatDateTime(v, { locale, style: 'short' }) Sep 11, 2026, 09:30 AM — unchanged
formatDate('2026-09-11', 'relative', { locale }) In 2 days

Read from source, not driven: that DateTimeCellRenderer passes field.format into that style key unchanged (lines above). A browser run through a real datetime grid column has not been done, so the end-to-end claim is inference from a measured formatter plus a read pass-through — worth one driven confirmation before anyone acts on it.

Why it is a metadata trap, not a nitpick

field.format is authored metadata: written by one person, stored, and re-authored later by another. The runtime accepts the key, parses it, drops it, and renders something plausible. That is the shape that made objectui#8352 survive a triage pass — a reader cannot tell an honoured style from a dropped one by looking at the output, because both look like a legitimate date.

Scope note

Whether to fix this by widening formatDateTime's vocabulary, by mapping at the renderer, or by rejecting the unaccepted spelling is a real design call with a published signature nearby, so it is left to a ruling rather than guessed. objectui#8352 answered the analogous question on the measure path at the CALL SITE, mapping the vocabulary explicitly and refusing to widen the published signature; that precedent is available but was decided for a different surface and should not be assumed to transfer.

⛔ Not the same as objectui#8209 (the readonly datetime WIDGET faces, which do not call formatDateTime at all) and not objectui#8507's bare-em-dash family. Dedupe: one targeted search over the datetime / field.format / relative-style vocabulary returned 12 results including a known-present control, covering objectui#7443, objectui#7745, objectui#7816, objectui#8209 and objectui#8352 — none names this renderer-level vocabulary split. Not upgraded to exhaustive.


Generated by Claude Code

Activity

  1. added
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    on Sep 9, 2026
  2. self-assigned this
    on Sep 10, 2026
  3. os-steve commented on Sep 10, 2026

    @os-steve
    Collaborator

    Claim: PM loop round 1
    Session: session_01MPaVWWMuWeT5LgB1qoXjVB
    Branch: claude/issue-8853-datetime-cell-format-vocabulary
    Worktree: objectui-issue-8853
    Domain: domain:ui
    File surface: packages/fields/src/index.tsx (the DateCellRenderer / DateTimeCellRenderer region only) · packages/fields/src/__tests__/ · .changeset/ (stop on breach; explain in the report)
    Container & model: M, mode:subagent, model: TIER_DEFAULT. ⚠️ dispatch-gates.mjs does not exist in objectui — gate families derived by hand and declared as such.
    Clause-②: no
    Thread-read: none (0 comments on the card at claim time)
    Serial constraints cleared: no in-flight branch touches packages/fields/ — measured, not assumed: claude/kanban-gantt-family-retirement (PR #8865) 0 hits, claude/issue-8903-… (PR #8954) 0, claude/issue-8912-… (PR #8951) 0. Lit control on the same probe: the kanban branch returns 16 hits under packages/plugin-kanban/, so those zeroes are readings and not a broken grep. ⚠️ objectui#8263 holds formatMeasureDate in packages/core — a different file and package; this card does not touch it.


    裁定 —— 本卡继承母单 objectui#8352 的裁决,⛔ 不进决策箱

    卡片正文写「left to a ruling rather than guessed」,并列了三条路(扩宽 formatDateTime 词汇表 / 在渲染器映射 / 拒收未接受的拼写),同时提醒 objectui#8352 的先例「should not be assumed to transfer」。这份克制是对的,但成文纪律已经回答了它。

    SKILL.md 〈升级与决策〉两条元判据之①,逐字:

    静默丢弃的声明并入既有拒收集;新支复用母单裁决直接入队,真实语义差异才重开。继承的是裁决连同理由;母单理由被实测为分支特有时本条不适用。

    本卡正文自己第一句就说它是「the same defect class on the dataset-measure path」的同族分支 ⇒ 母单是 objectui#8352,已 closed / completed,由 PR **#8852(MERGED)**落地。

    ⛔ 母单裁决由本席在 origin/main 上实测,⛔ 未采信卡片的转述

    卡片声称 #8352「mapped the vocabulary explicitly at the CALL SITE and refused to widen the published signature」。⚠️ 而 PR #8852 的标题是「honour measure.format on the datetime arm」,字面上并不排除它是靠扩宽签名做到的。所以本席自己读了落地后的树:

    读数 结果
    formatDateTime 的已发布签名 export function formatDateTime(value, options?): string —— 未变,仍没有 style 形参
    formatMeasureDate 的 datetime 臂 其自带注释:「The datetime arm honours the SAME two words the date arm does, by choosing a formatter here (objectui#8352). It cannot thread format…」

    ⇒ 母单裁决实为:在调用点按 format 选择格式化器,让 datetime 臂兑现与 date 臂相同的两个词;⛔ 不扩宽已发布的 formatDateTime 签名。 卡片的转述准确,但那是本席核出来的,不是信来的。

    ⇒ 本卡的 ZONE 1

    继承上述裁决:在 DateTimeCellRenderer 的调用点解决,让同一个作者写下的 field.format 在 date 与 datetime 两个单元格渲染器上意义一致;⛔ 不扩宽 formatDateTime 的已发布签名,⛔ 不新增拒收(那是对已发布元数据面的破坏性收窄)。

    ⇒ 三条路中,选项 1 出局(移动已发布签名 = 扩大公开面,属人工地板),选项 3 出局(破坏性收窄),选项 2 是母单已裁的那一条。

    ⚠️ 但继承是可被证伪的 —— 这是派发令的 ZONE 2,不是形式

    元判据自带出口:「母单理由被实测为分支特有时本条不适用」。⇒ 交付席必须先验证母单的理由在本分支成立(两处都是「一个作者写的 format 键,跨相邻类型语义应当一致」),若实测为 measure 路径特有,停手回报,⛔ 不硬套。

    同时卡片自己申报了一个未测的洞:「A browser run through a real datetime grid column has not been done — the end-to-end claim is inference from a measured formatter plus a read pass-through」。⇒ 施工前先把端到端缺陷驱动出来,⛔ 不在一个只由推断支撑的前提上动手。

    ⚠️ 分诊缺口,记录不自行修补

    本卡无 priority:* 标签(现有标签只有 pm:queue / finding / domain:ui / package: fields)。定级是分诊席的唯一产出,⛔ 本执行席不代打,已记入轮次报告的分诊缺口项。另:finding 本应在首次定级时离标,此处两者皆缺,是同一个缺口的两面。


    Generated by Claude Code

  4. os-steve commented on Sep 10, 2026

    @os-steve
    Collaborator

    os-dev-report

    {
      "issue": 8853,
      "status": "done",
      "branch": "claude/issue-8853-datetime-cell-format-vocabulary",
      "pr": "https://github.com/objectstack-ai/objectui/pull/8959",
      "premise_still_valid": true,
      "summary": "ZONE 1 inherited ruling applied: the vocabulary is mapped at the CALL SITE in DateTimeCellRenderer, so one authored field.format means the same thing on the date and datetime cell renderers. 'relative' now selects formatRelativeDate (the same function the date cell reaches) and 'short' selects the compact face of this type; every other spelling, 'compact' and date patterns included, falls to the default face unchanged. formatDateTime's published signature was NOT widened (route 1 out) and no refusal was added for a currently-accepted spelling (route 3 out); both refusals are pinned. ZONE 2/1 falsification attempt FAILED to falsify: the parent's reason (one authored key, one meaning across adjacent type arms) was tested against this surface and holds. Differing DEFAULTS (date cell defaults to 'relative', datetime to 'compact') are not a branch-specific reason for differing vocabularies - objectui#8352 ruled with the same asymmetry present on the measure path and said so in its own docblock. ZONE 2/2 end-to-end drive done BEFORE any edit: a real ObjectGrid column reproduced the drop. ZONE 2/4: line numbers re-located by anchor, not arithmetic (they happened to still coincide with the card's 919/928/970/994). dueLike is deliberately not threaded and is filed as objectui#8958.",
      "premise_drive": {
        "when": "before any source edit, on origin/main at 63601abb8",
        "surface": "a real ObjectGrid over an object with a date field and a datetime field, both authored format: 'relative'",
        "conditions": "TZ=UTC (pinned by the root vitest config), en-US, clock pinned 2026-09-09T12:00:00.000Z, value 2026-09-11T09:30:00.000Z",
        "before": "grid tbody cells = [\"1Open\", \"Row One\", \"In 2 days\", \"Sep 11, 2026, 09:30 AM\"]",
        "after": "grid tbody cells = [\"1Open\", \"Row One\", \"In 2 days\", \"In 2 days\"]",
        "note": "cell 3 is the date column, cell 4 the datetime column. Grid rows DO render under happy-dom, so no layout claim and no Chromium was needed; the preinstalled browser was not used."
      },
      "two_way_mutation": {
        "method": "git checkout <pinned BASE 63601abb84467363e224b6734f7b5b377f131da7> -- packages/fields/src/index.tsx, then git checkout HEAD -- <abs path> to restore; trap on EXIT INT TERM with absolute paths",
        "mutation_reached_disk": "PROVEN BEFORE READING ANY RESULT - blob hash 9e593ba1ef810ae26ca66bed833495b1507b5e57 -> 8e9e7621a9752f0fa4f567f1a2f05f19047858dc, anchor counts \"if (style === 'relative')\" 1 -> 0 and \"authoredFormat\" 2 -> 0",
        "mutated_run": "exit 1 - Tests 6 failed | 14 passed (20)",
        "red_cases": [
          "a datetime cell honours `relative`, and renders the phrase its date sibling renders",
          "en-US / zh / de-DE - the two cells agree in a non-US locale too (3 cases)",
          "`short` reaches the dense face of THIS type - the time of day is kept",
          "an out-of-window datetime renders the absolute DATE face - the declared cost"
        ],
        "green_under_mutation": "the 14 controls and regression pins - they measure surfaces this change does not touch, which is why they can vouch for the run",
        "restore_proven_by_state": "git diff HEAD empty AND on-disk blob back to 9e593ba1ef810ae26ca66bed833495b1507b5e57 - never by an exit code",
        "restored_run": "exit 0 - Tests 20 passed (20)",
        "rebuild_relevance": "none needed - the test imports the renderer by relative source path (../index), so no dist sits between the mutation and the assertion"
      },
      "gates": [
        { "gate": "pnpm --workspace-concurrency=2 --filter '@object-ui/fields...' build", "exit": 0, "evidence": "os-verify-lock VERDICT command-exit 0, held 92s" },
        { "gate": "pnpm --filter @object-ui/fields type-check (tsc --noEmit AND tsc -p tsconfig.test.json)", "exit": 0, "evidence": "the second tsconfig covers the new test file, so this is NOT the excluded-tests trap" },
        { "gate": "pnpm --filter @object-ui/fields lint", "exit": 0, "evidence": "990 problems (0 errors, 990 warnings) - warnings pre-exist; --no-inline-config never passed" },
        { "gate": "pnpm exec vitest run packages/fields/ (repo ROOT)", "exit": 0, "evidence": "Test Files 155 passed (155), Tests 2658 passed (2658)" },
        { "gate": "pnpm exec vitest run packages/plugin-grid/ packages/plugin-detail/ packages/core/src/utils/", "exit": 0, "evidence": "Test Files 339 passed (339), Tests 3792 passed (3792); os-verify-lock VERDICT command-exit 0" },
        { "gate": "node scripts/check-control-bytes.mjs", "exit": 0, "evidence": "scanned 7156 tracked text files; plus a manual grep -naP over the three changed files, no hits" },
        { "gate": "node scripts/check-changeset-presence.mjs", "exit": 0, "evidence": "1 source file of 1 released package changed, 1 changeset declared" },
        { "gate": "node scripts/check-changeset-no-major.mjs", "exit": 0, "evidence": "No changeset declares a major bump; this one is minor" },
        { "gate": "node scripts/check-governed-queue-guard.mjs --test <3 paths>", "exit": 0, "evidence": "NOT GOVERNED - 3 paths checked against 5 governed surfaces, none matched" }
      ],
      "gates_prerequisite_not_met": [],
      "gate_derivation": "scripts/pm/dispatch-gates.mjs does NOT exist in objectui - families derived BY HAND from the actual changed-file set and declared as such in the PR body. Every exit code above was redirected to a file and captured before any pipe; no bare $? was read through a pipeline.",
      "tests": "New pin file packages/fields/src/__tests__/datetimeCell.formatVocabulary-8853.test.tsx, 20 tests, all green. It carries the repro (both cells asserted on the same instant in the same run), a POSITIVE control (the date cell, which already honoured 'relative'), a NEGATIVE control (out-of-window, where 'relative' and no-format render IDENTICALLY on the fully working date cell - the objectui#8352 trap, pinned so the in-window choice is a measured decision not luck), regression pins for every datetime spelling that rendered before, and two pins that the published signature did not move (formatDateTime.length === 2, and formatDateTime still ignoring 'relative'/'short' in options.style). Two-way discriminating power measured, see two_way_mutation.",
      "mcp_calls": "9",
      "deviations": [
        "The end-to-end ObjectGrid drive is a one-time DRIVEN MEASUREMENT quoted in the PR body and changeset, not a committed test. Reason: the claim's declared file surface is packages/fields/**, and a committed grid test would need a packages/fields test to import @object-ui/plugin-grid - an inverted package-layer dependency that check:phantom-deps guards. The committed pins assert the two neighbouring renderers side by side, which IS the card's claim; the grid only proves the routing, which existing plugin-grid tests already cover.",
        "The claim comment's serial-constraint snapshot was re-derived rather than trusted, per the dispatch. It named PR #8951, which is no longer open, and predates nine of the twelve currently-open PRs. Re-measured: 0 of 12 open PRs touch packages/fields/src/index.tsx. Two touch packages/fields/package.json only (lucide bumps #8941 and #7058); changeset-release/main is stale relative to main, not a concurrent editor. Lit control: every branch measured returns a non-zero TOTAL changed-file count, so the zeroes are readings.",
        "Consumer suites (plugin-grid, plugin-detail, core/utils) were run beyond the dispatch's stated minimum, because the RENDERED OUTPUT changed even though the exported byte surface did not.",
        "The PR body was PATCHed once after creation to repair a factual error (a rendered face quoted as 9:00 am where the measured face is 9:30 am). Read back after: the body is byte-intact and, contrary to AGENTS.md GitHub-mutation item 3, NO second attribution footer was appended by the PATCH - the create call HAD appended one, and the PATCH replaced it. Reported, not acted on: AGENTS.md is a governed surface.",
        "The draft flag was set at creation and never written again, per the dispatch."
      ],
      "files_changed": [
        "packages/fields/src/index.tsx (DateTimeCellRenderer only: the authored-vocabulary mapping and the relative branch, plus the comment block explaining the inherited ruling and the two declared costs)",
        "packages/fields/src/__tests__/datetimeCell.formatVocabulary-8853.test.tsx (new, 261 lines)",
        ".changeset/datetime-cell-format-vocabulary-8853.md (new, minor on @object-ui/fields, break spelled out in the body)"
      ],
      "open_questions": [],
      "out_of_scope_findings": [
        "filed as #8958: `dueLike` is declared for \"date/datetime\" fields in the Zod DetailViewFieldSchema describe text, but DateTimeFieldMetadata does not carry it and DateTimeCellRenderer never reads it - measured byte-identical output with and without the key, while the date cell paints `Overdue 3d` in text-red-600. Successor: whoever takes #8958; it names the two routes (thread it, or narrow the describe text under enforce-or-remove).",
        "noted, not filed: `DateTimeCellRenderer` passes `t` into `formatDateTime`, which never reads it - an inert argument that predates this card and that this change does not touch. Successor: the next PR in this renderer, most likely #8958's, which will have to decide what `t` is for on this surface anyway."
      ]
    }

    Generated by Claude Code, session session_01MPaVWWMuWeT5LgB1qoXjVB.


    Generated by Claude Code

  5. os-steve commented on Sep 10, 2026

    @os-steve
    Collaborator

    ACCEPT(待 CI)— PR #8959;⛔ 本轮不放行,门禁未收敛

    派发席 domain:ui @ objectui,session_01MPaVWWMuWeT5LgB1qoXjVB,2026-09-10T10:2xZ。⛔ 对 GitHub 与 diff 核验,不对终报自述核验。

    ⭐ 继承裁决被试图证伪而未被证伪 —— 这是本轮最该记的

    派发令 ZONE 2 明写:元判据 ① 的出口是「母单理由被实测为分支特有时本条不适用」,并要求交付席主动去推翻它。它去推了,推不动,并给出了理由:

    两个渲染器的默认值确实不同(date 默认 'relative',datetime 默认 'compact')—— 但默认值不同不是词汇表应当不同的理由;objectui#8352 是在 measure 路径上带着同一个不对称做出裁决的,并在自己的 docblock 里说了这一点。

    ⇒ 继承成立,且成立的方式是可核的:不是"没找到反例",而是点名了最像反例的那个事实并说明它为何不是。

    复核清单

    项 读数
    文件面 3 文件,与申报一致:packages/fields/src/index.tsx(仅 DateTimeCellRenderer,+65/−1)· 新 pin 261 行 · changeset
    ZONE 1 路线一(⛔ 不扩宽已发布签名) 兑现,且被钉住:formatDateTime.length === 2,以及 formatDateTime 仍忽略 'relative'/'short'
    ZONE 1 路线三(⛔ 不新增拒收) 兑现,且被钉住:'default' / 'YYYY-MM-DD' 仍渲染为原来的默认面
    前提端到端驱动(动手之前) 做了:真实 ObjectGrid 一行两列,["…","In 2 days","Sep 11, 2026, 09:30 AM"] → ["…","In 2 days","In 2 days"]
    负控 做了且钉住:窗口外 'relative' 与无 format 在完好的 date 列上渲染相同 ⇒ 若用窗口外的值做探针,"相同"在正确与错误的路径上都会出现
    双向变异 mutated exit 1(6 红 / 14 绿),restored exit 0(20 绿);变更到盘由 blob hash + 锚点计数在读结果之前证明,还原按状态证明(git diff HEAD 空 + blob 复原)
    changeset @object-ui/fields: minor,破坏性在正文逐条写明 ✅(本仓禁 major)
    Clause-② no —— 无新导出、无签名移动;field.format 的接受集未变(同一批拼写仍全部通过),变的是行为

    ⚠️ 一条我没有直接接受、而是单独审过的行为代价

    本 PR 带来一处真实的信息损失,交付席主动申报并钉住了它:

    窗口(±7 天)之外,format: 'relative' 的 datetime 单元格不再显示时刻。2026-09-20T09:30:00Z 由 Sep 20, 2026, 09:30 AM 变为 Sep 20。

    本席的判断是接受,理由按四棱里最硬的那条:

    • ⭐ 此前那个"更丰富"的面不是设计,是缺陷的副产品。 作者写下 'relative' 被静默丢弃,于是拿到默认面 —— 他要的是相对,拿到的是绝对。现在他拿到的是相对。⇒ 「nothing was taken from a working feature」成立,不是修辞。
    • 'relative' 在本仓按构造就是日粒度(窗口内也不显示时刻),⇒ 它的降级形态是日面,自洽。
    • 窗口归 formatRelativeDate 所有;在调用点重新决定它会在渲染器里放第二份同一约定的拷贝 —— 那正是 objectui#4576 的形态。
    • 任何其它回退都会让两列再次不相等,而两列相等正是本卡要闭合的缺陷。

    ⇒ 这是一次已声明的行为变更,按本仓约定以 minor + 正文写明破坏发布,不是被掩盖的回归。

    ⚠️ 本席派发令里的一条读数已过期,由交付席指出

    我的认领评论把串行约束锚在 PR #8951 上。它已经不再 open,且那份快照早于当前 12 个 open PR 中的 9 个。交付席重测而非沿用:12 个 open PR 中 0 个触及 packages/fields/src/index.tsx(两个只碰 packages/fields/package.json 的 lucide bump),点亮对照是每个被测分支的总改动文件数皆非零。⇒ 结论不变,但结论的依据换成了新鲜的。⭐ 记一条:认领评论里的串行读数会随轮次老化,交付席应当重测而不是引用它。

    顺带产出

    objectui#8958 已立:dueLike 在 Zod DetailViewFieldSchema 的 describe 文本里被声明为「date/datetime」可用,但 DateTimeFieldMetadata 不带它、DateTimeCellRenderer 从不读它 —— 实测带与不带输出逐字节相同,而 date 单元格会画 Overdue 3d 且带 text-red-600。这是 declared≠enforced 的标准形态,⛔ 未借本卡通道夹带,单独立卡。

    ⛔ 未放行

    33 个 check 中 Type Check 与四个 Test (shard n/4) 仍 in_progress(10:16Z 起)。入队资格是每一个 check 全绿,⛔ 不是 required 子集。收敛后复查再翻 ready。


    Generated by Claude Code

  6. removed their assignment
    on Sep 10, 2026
  7. os-steve commented on Sep 10, 2026

    @os-steve
    Collaborator

    ✅ 已落地 — PR #8959 MERGED;半状态在本笔清掉(今日第三次)

    派发席 domain:ui @ objectui,session_01MPaVWWMuWeT5LgB1qoXjVB,2026-09-10T11:1xZ。

    落地核验 —— 按内容带两向对照,⛔ 不按 sha

    origin/main 现为 58be55e78。

    腿 合并前 现在
    主体 packages/fields/src/__tests__/datetimeCell.formatVocabulary-8853.test.tsx 存在 NO YES
    主体 packages/fields/src/index.tsx 含 if (style === 'relative') 0 1
    正控 packages/fields/src/index.tsx 存在 YES YES
    负控 packages/fields/src/zzzNoSuchFieldsFile.tsx NO NO

    ⚠️ Fixes 自动关卡不摘 pm:*、不清 assignee —— 今日第三次

    objectui#8912(10:15Z)、objectui#8903(10:43Z)、本卡(11:11Z)。三次全部相同 ⇒ 这不是偶发,是这条落地路径的固定产物,已写进座位贴车道事实。已清:pm:dispatched 摘除、assignee 清空;finding / domain:ui / package: fields 保留。

    ⚠️ 本卡仍无 priority:* 标签。定级与 finding 的生命周期归分诊席,⛔ 执行席不代打,记为分诊缺口(与 objectui#8912 的 finding 未随定级离标是同一类)。

    本卡实际交付的东西

    一个作者写下的 field.format 现在在 date 与 datetime 两个相邻单元格渲染器上意义一致:'relative' 走 formatRelativeDate(date 单元格用的同一个函数),'short' 走本类型的密集面;其余拼写(含 'compact' 与日期模式)原样落到默认面。

    ⭐ 已发布签名没有移动,而且这件事被钉住了:formatDateTime.length === 2,以及 formatDateTime 仍忽略 'relative' / 'short' —— 将来谁去扩宽那个签名,这两条会红。

    ⭐ 本轮最值得记的三件

    ① 继承裁决被试图证伪而未被证伪。 派发令要求交付席主动推翻元判据 ① 的继承。它去推了、推不动,并点名了最像反例的那个事实再说明它为何不是:两个渲染器的默认值确实不同,但默认值不同不是词汇表该不同的理由 —— 母单 objectui#8352 正是带着同一个不对称做的裁决。

    ② 前提在动手之前被端到端驱动出来。 卡片自己申报过它的端到端断言未测(是"测过的 formatter + 读过的 pass-through"的推断)。交付席先驱动真实 ObjectGrid:["…","In 2 days","Sep 11, 2026, 09:30 AM"] → ["…","In 2 days","In 2 days"]。⛔ 没有在一个推断上动手。

    ③ 母单记录的那个陷阱被钉成了负控。 formatRelativeDate 在 ±7 天窗口外回落到绝对面 ⇒ 窗口外 'relative' 与无 format 在完好的 date 列上渲染相同。若用窗口外的值做探针,"相同"在正确与错误的路径上都会出现。该塌缩被显式断言,使窗口内的选择是测出来的决定而不是运气。

    ⚠️ 一处已声明的行为代价,复核时单独审过

    窗口外 'relative' 的 datetime 不再显示时刻(Sep 20, 2026, 09:30 AM → Sep 20)。判接受,最硬的理由:此前那个"更丰富"的面不是设计,是缺陷的副产品 —— 作者写 'relative' 被静默丢弃才拿到默认面。已按本仓约定以 minor + changeset 正文逐条写明发布。

    顺带产出

    objectui#8958 已立:dueLike 在 Zod describe 文本里声明为 date/datetime 可用,但 datetime 侧不带、不读,实测带与不带输出逐字节相同 —— declared≠enforced 的标准形态,⛔ 未借本卡通道夹带。


    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

No one assigned

    Labels

    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatfindingpackage: fields

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions