You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
nits(core,fields) from PR #7621's review: the changeset's byte-identity claim is imprecise for consumers, and (field as any)?.format had a free narrower cast #7747
Filed by the domain:ui PM seat from the contract review of PR #7621 — the two non-blocking items that PR does not carry, recorded so they are not lost when it lands. ⛔ Neither gated the review's PASS.
1. The changeset's byte-identity sentence is imprecise for a consumer
.changeset/7443-datetime-compact-style.md says, in effect, "Every existing cell renders byte-identically."
Measured in-repo it is true: the reviewer's census found zero in-repo datetime fields that author a format, so no cell in this repository changes. But the sentence is a release-note input, and it is read by consumers who are not this repository.
The accurate form: "every cell without an authored non-'compact'format renders byte-identically." PR #7621's own body already declares that DateTimeCellRenderer starts reading field.format — the changeset simply does not spell out the one visible consequence.
⚠️ Worth stating for whoever fixes it: format on a datetime field is objectui's own vocabulary — DateTimeFieldMetadata.format?: string, and @objectstack/spec declares no format on datetime fields. It is undocumented. So "what values are legal, and what does an unrecognised one do" is itself unanswered, and the honest changeset sentence may need that gap named rather than papered over.
2. (field as any)?.format where a narrower cast was free
packages/fields/src/index.tsx (the DateTimeCellRenderer cell) reads const style = (field as any)?.format || 'compact'.
Some narrowing is genuinely load-bearing: FieldMetadata is a 37-member union, BaseFieldMetadata has no format, and format is declared only on Date/DateTime/Time/Phone/AutoNumber — so field.format on the union is TS2339.
But as any is wider than the job needs: DateTimeFieldMetadata is exported from @object-ui/types (types/src/index.ts:21) and that file already imports types from there, so (field as DateTimeFieldMetadata | undefined)?.format was available at zero cost.
⚠️ The DateCellRenderer precedent that was cited for it does more work — it reads accessorKey / key, which are genuinely not on DateFieldMetadata. Here only .format is read, and .formatis on the subtype. Reviewer's verdict: lazy, precedented, not load-bearing. (no-explicit-any is warn-level in this repo, so CI Lint is green either way — which is exactly why this needs a card rather than a gate.)
Refs: objectui#7443 · PR #7621 · sibling follow-ups from the same review: #7745, #7746.
pnpm --filter @object-ui/fields test 与 type-check 绿,报出实际文件数。
第 2 项的收窄确实成立要证明,不是声称:在收窄后的那一行上做一次点亮控制(比如临时把它换成一个不含format 的子类型),确认 tsc 变红并指名该行;然后在 trap … EXIT INT TERM 下恢复,用 blob hash 与空 git diff HEAD 证明已还原。⛔ 不要用 as any 版本当对照 —— 那个版本本来就不会红,测不出任何东西。
第 1 项贴 git diff,显示只有散文行变化、frontmatter 与包名集合不变。
报告里写清 DateTimeCellRenderer 对不认识的 format 的实测行为;⛔ 没测出来就写「未测出」,不要写猜测。
契约复审
Clause-② 是 no ⇒ 本卡不排在契约复审后面。⚠️ 若工作过程中发现必须动运行期行为,那就是上面声明的作废条件:停下、上报。
⛔ 没有用 as any 版本当对照 —— 派发令点名禁止过,理由是它永远不会红,测不出任何东西。它换成了 BooleanFieldMetadata(同一个 barrel 导出、且不带format):tsc --noEmitexit 2,恰好一条错误,src/index.tsx(889,62): error TS2339: Property 'format' does not exist on type 'BooleanFieldMetadata' —— 指名到行到列。
⭐ 还原的证明靠 hash,不靠「trap 有没有触发」:POST_RESTORE c16ba226 等于 HEAD blob c16ba226,git diff HEAD 为空,真实 cast 出现恰一次。它还点出 git checkout HEAD -- <绝对路径> 与裸形式的区别(后者从 index 恢复)。这两条都是本席后续派发令会继续要求的形状。
Activity
claude commentedon Sep 5, 2026
PM 预清障(
domain:uiPM 席,session_01KbJQ1y1J12nZxYzFWhP8Q3,round 6):本卡第 1 项要改的是别人 PR 的、已合并的** changeset。派发前必须先证明这动作合法 —— 已证明,允许。读数如下,接手人不必重推一遍。**前提一:那个文件还在
origin/main=6771c805a(PR #7784 的合并提交)。⇒ 仍在树上,尚未被发版消费。若它已被 release 吃掉,第 1 项就不是「改一句话」而是「改已发的 release note」——那会撞上
content/docs/releases/那条禁令(发版说明集中写、不按 PR 累加),必须改走独立 docs PR 或转 issue。现在不是这个形状。前提二:改别人的 changeset 是否被门拦
scripts/check-changeset-overwrite.mjs就是管这件事的门。逐条读它自己的头部,不是凭印象:exit 0;只有OS_CHANGESET_OVERWRITE_ENFORCE=1才拦。仓库里没有开这个开关。main上 5281 个 first-parent 提交里,12 个提交修改了已存在的 changeset(19 个文件),19 个全部合法,其中明确点名的合法类别就包括**「对散文的事实性更正」**(举的例子正是 "eleven"→"ten" locale packs、以及一处包名笔误)。⇒ 本卡第 1 项 = 对散文的事实性更正,恰好落在那 19 分之 19 里。
同一份测量析出了一条更窄的信号,接手人必须遵守:
⇒ 接手时只改那一句散文,⛔ 不动 frontmatter、⛔ 不动 bump 等级、⛔ 不删任何包名。改完把
git diff贴进 PR 正文,让「名字集合不变」可核。第 2 项无障碍
packages/fields/src/index.tsx把(field as any)?.format收窄为(field as DateTimeFieldMetadata | undefined)?.format。该文件已经从@object-ui/typesimport 类型,DateTimeFieldMetadata在types/src/index.ts:21导出 ⇒ 零成本。no-explicit-any在本仓是 warn 级,两种写法 CI Lint 都绿 —— 所以这是卡不是门,也所以不要顺手把它升成 error,那是另一张卡的事。Clause-② 预判:
no判据是内容不是路径。第 1 项只改散文;第 2 项是纯类型层收窄,运行期零行为差。⇒ 不改变契约的接受/拒绝行为,不扩大公开面。⚠️ 作废条件:任何让
DateTimeCellRenderer在运行期读出不同值的改动 —— 那就是yes,停下上报,⛔ 不要在no申报下推yes的 diff。变更集
第 2 项动了发版包的
src/⇒ 按AGENTS.md:161机械要求,必须新增一个自己的.changeset/*.md。⛔ 那是新文件,与本卡第 1 项要更正的7443-datetime-compact-style.md是两回事,别混。纯类型收窄不发版 ⇒ 写空 frontmatter形式(---紧跟---)显式声明不发版,那是合法的一等通过写法。⇒ 本卡已解除派发前障碍,保持
pm:queue,等并发槽位。Generated by Claude Code
claude commentedon Sep 5, 2026
Claim: PM loop, round 6 — all-opus lane (Clause-②⚠️ 别人 PR 的、已合并的变更集 —— 只改一句散文), ⚠️ Void condition: 任何让 ⚠️ 同批兄弟卡 #7745(
no)Session:
session_01KbJQ1y1J12nZxYzFWhP8Q3Branch:
claude/issue-7747-datetime-format-nitsWorktree:
objectui-issue-7747Domain:
domain:uiFile surface:
.changeset/7443-datetime-compact-style.md(packages/fields/src/index.tsx(一处 cast 收窄), 外加本卡自己的新.changeset/*.md. ⛔ Stop on breach: 任何 frontmatter/bump 等级/包名的改动、任何packages/fields/src/index.tsx之外的运行期改动、任何把no-explicit-any升级成 error 的顺手改动。Explain in the report.Container & model: S,
mode:subagent,model: opusClause-②: no — 第 1 项只改散文;第 2 项
(field as any)→(field as DateTimeFieldMetadata | undefined)是纯类型层收窄,运行期零行为差。不改变契约接受/拒绝行为,不扩大公开面。DateTimeCellRenderer在运行期读出不同值的改动 —— 那就是yes,停下上报,⛔ 不在no申报下推yes的 diff。Serial constraints cleared: none —— 无在飞 PR 触碰
packages/fields/src/index.tsx或.changeset/7443-datetime-compact-style.md;本卡的来源 PR #7621 已合并。DateDisplayOptions.style被formatDateTime读取)与本卡同属 #7621 复审析出,但文件面不相交且本卡不 pin 断言其行为 ⇒ 无读耦合。派发前障碍已由本席清除,读数在 comment 5554176643,⛔ 不必重推:变更集文件仍在
main上(未被发版消费),check-changeset-overwrite.mjs是 report-only 且其自身历史测量把「对散文的事实性更正」列为 19/19 合法。两项都要做,⛔ 不许只做一项
第 1 项 —— 把散文改准,别改多
.changeset/7443-datetime-compact-style.md现在说「每个已有单元格都逐字节相同」。在本仓内测是真的(复审普查:零个 in-repodatetime字段写了format),但变更集是发版说明的输入,读它的是本仓之外的消费者,而 PR #7621 自己的正文已经声明DateTimeCellRenderer开始读field.format。准确形状:「每个没有写非
'compact'的format的单元格逐字节相同」。format是 objectui 自己的词汇(DateTimeFieldMetadata.format?: string),@objectstack/spec在 datetime 字段上没有声明format,而且它没有文档。所以「哪些值合法、不认识的值会怎样」本身是未回答的。⇒ 诚实的写法是把这个缺口点名,而不是写一句读起来像已经定义好的话。先去实测DateTimeCellRenderer拿到一个不认识的format会怎样(回退'compact'?抛错?原样输出?),把测出来的行为写进散文;⛔ 测不出来就写「未定义」,不要猜。⛔ 硬约束(来自
check-changeset-overwrite.mjs自己的历史测量):19 处合法修改里 18 处保住了 base 上声明过的每一个包名,唯一的例外删的是解析不到任何包的笔误名;overwrite 事故的形状恰恰是前一份声明的名字整个没了。⇒ 只动那一句散文,frontmatter 一行不碰,包名一个不少,git diff贴进 PR 正文让「名字集合不变」可核。第 2 项 —— cast 收窄,⛔ 不要顺手扩大
packages/fields/src/index.tsx的DateTimeCellRenderer现在写const style = (field as any)?.format || 'compact'。要收窄多少是有依据的,不是审美:
FieldMetadata是 37 成员联合,BaseFieldMetadata上没有format,format只声明在 Date/DateTime/Time/Phone/AutoNumber 上 ⇒ 直接field.format是TS2339。某种收窄是载荷的。但as any比需要的宽:DateTimeFieldMetadata从@object-ui/types导出(types/src/index.ts:21),而该文件已经从那里 import 类型 ⇒(field as DateTimeFieldMetadata | undefined)?.format零成本。DateCellRenderer先例做的事更多 —— 它还读accessorKey/key,那些确实不在DateFieldMetadata上。这里只读.format,而.format就在那个子类型上。复审判词:lazy, precedented, not load-bearing。⛔
no-explicit-any在本仓是 warn 级,两种写法 CI Lint 都绿 —— 这正是它需要一张卡而不是一道门的原因。⛔ 不要顺手把规则升成 error,⛔ 不要顺手清理文件里其他的any:那是另一张卡的事,塞进来就是擅自扩面。变更集
第 2 项动了发版包的
src/⇒ 按AGENTS.md:161机械要求,必须新增一个自己的.changeset/*.md。⛔ 那是新文件,与第 1 项要更正的7443-datetime-compact-style.md是两回事,别混、别把第 1 项的更正当成「已经有变更集了」。纯类型收窄不发版 ⇒ 写空 frontmatter(---紧跟---)显式声明不发版,那是合法的一等通过写法。⛔ 永不写major(scripts/check-changeset-no-major.mjs会拒)。验收
pnpm --filter @object-ui/fields test与type-check绿,报出实际文件数。format的子类型),确认tsc变红并指名该行;然后在trap … EXIT INT TERM下恢复,用 blob hash 与空git diff HEAD证明已还原。⛔ 不要用as any版本当对照 —— 那个版本本来就不会红,测不出任何东西。git diff,显示只有散文行变化、frontmatter 与包名集合不变。DateTimeCellRenderer对不认识的format的实测行为;⛔ 没测出来就写「未测出」,不要写猜测。契约复审
Clause-② 是⚠️ 若工作过程中发现必须动运行期行为,那就是上面声明的作废条件:停下、上报。
no⇒ 本卡不排在契约复审后面。Generated by Claude Code
claude commentedon Sep 5, 2026
PM 收单(round 6):dev 席回报
status: done,PR objectui#7792 已开(draft)。两项都交付,并带回一条对卡面的更正**。**已核对
check-changeset-overwrite.mjsexit 0,且它打印的「declared at base」与「declares now」完全相同,三个包名全部保住 ⇒ 本席在预清障里立的那条约束可核地满足了,不是声称(field as any)?.format→(field as DateTimeFieldMetadata | undefined)?.format,外加给既有的@object-ui/types类型 import 补一个词。该文件的no-explicit-any警告 137 → 136,且减掉的那条就是被改的那一行.changeset/7747-datetime-format-cast-narrowing.md,空 frontmatter;check-changeset-presence/-no-major/-fixed/-overwrite全 exit 0no,且是测过的:cast 在 emit 时被擦除,改动后逐行复现了改动前的行为 ⇒ 作废条件是被检查过而不是被假定pnpm --filter @object-ui/fields type-check两条腿都 exit 0;仓根vitest run packages/fields/= 133 files / 2178 tests 全过⭐ 点亮控制做对了两个细节
as any版本当对照 —— 派发令点名禁止过,理由是它永远不会红,测不出任何东西。它换成了BooleanFieldMetadata(同一个 barrel 导出、且不带format):tsc --noEmitexit 2,恰好一条错误,src/index.tsx(889,62): error TS2339: Property 'format' does not exist on type 'BooleanFieldMetadata'—— 指名到行到列。POST_RESTORE c16ba226等于 HEAD blobc16ba226,git diff HEAD为空,真实 cast 出现恰一次。它还点出git checkout HEAD -- <绝对路径>与裸形式的区别(后者从 index 恢复)。这两条都是本席后续派发令会继续要求的形状。⭐ 对卡面的一条更正 —— 它加强而不是削弱本卡的论点
卡面写的是「
@objectstack/spec在 datetime 字段上没有声明format」。实测:spec 的字段 schema 确实带format—— 但它长在一个扁平的、非判别式的、被所有字段类型共用的 field schema 上,类型是自由字符串可选项,没有 enum、没有按类型的值域,描述写着 "Format string (e.g. email, phone)",并且'compact'/'short'/''/'not-a-real-style'一律接受。⇒ 准确的表述是:键存在,而 datetime 的值词汇表确实不存在。 这正是卡要求「点名这个缺口而不是粉饰它」的那个缺口,只是位置比卡面说的更精确。⭐ 本卡的可行动结论一字未变,所以
premise_still_valid为真 —— 这是把论点钉得更死,不是推翻它。⭐ 未识别的
format到底会怎样 —— 测出来了,答案是三个猜测都不对临时 harness(读完即删,不在 PR 里),固定一个时刻
2024-07-04T07:00:00.000Z、en-US,跑九个取值:''·null·'compact'7/4/2024 7:00 am'not-a-real-style'·'short'·'relative'·'YYYY-MM-DD'· 数字42Jul 4, 2024, 07:00 AM⇒ 一个不认识的值不抛错、不原样透传、也不回退到
'compact'—— 它静默选中formatDateTime的冗长默认面。⛔ 派发令里我列的三个猜测(回退 compact / 抛错 / 原样输出)一个都不对,这正是为什么要求去测而不是写猜测。后续件
objectui#7791 ——
CapabilityMultiSelectField.specParity-6285.test.tsx把VOCABULARY_ROOT设成'.'(进程 cwd),于是pnpm --filter @object-ui/fields test让它 2/7 红,而仓根形式 7/7 绿 —— 一次假红,恰好发生在AGENTS.md声明「与 CI 同一结论」的那条调用上。到达时无标(半状态),本席已分诊为p2并已派发(认领评论 5554505191)。派发令里明确:⛔ 只修测试侧路径解析,AGENTS.md是治理面、一个字节都不许碰,那句被证伪的话写进 PR 正文交维护者定夺。⇒ 下一步:等 CI 终态 → 逐项枚举 → 转 ready-for-review → 入队。
Generated by Claude Code
claude commentedon Sep 5, 2026
落地确认:PR #7792 已合并,本卡由
Fixes自动关闭。mergedtrue@2026-09-05T20:42:32Zmerge_commit_shadfcbedee5e8ff7241cb34bb08e68e04fe6c778a1added_to_merge_queue 20:27:39Z→ merged20:42:32Z,其间无removed_from_merge_queue7747一个 ⇒ 无误伤closed_at 20:42:33Zpm:dispatched(本席已分诊p2并派发)本卡正文说「
@objectstack/spec在 datetime 字段上没有声明format」——不准确。 实测:spec 的字段 schema 确实带format,但它长在一个被所有字段类型共用的扁平、非判别式 schema 上,类型是自由字符串可选项,无 enum、无按类型值域,描述写着 "Format string (e.g. email, phone)",并且'compact'/'short'/''/'not-a-real-style'一律接受。⇒ 准确表述:键存在,而 datetime 的值词汇表确实不存在。 这是把本卡的论点钉得更死,不是推翻 —— 落地的散文按这个准确形状写。⭐ 未识别的
format的实际行为,测出来了(临时 harness,读完即删、不在 PR 里;固定2024-07-04T07:00:00.000Z/en-US,九个取值):''·null·'compact'7/4/2024 7:00 am'not-a-real-style'·'short'·'relative'·'YYYY-MM-DD'· 数字42Jul 4, 2024, 07:00 AM⇒ 不认识的值不抛错、不原样透传、也不回退到
'compact'—— 它静默选中formatDateTime的冗长默认面。⛔ 派发时列出的三个猜测一个都不对。变更集覆写的硬约束,可核地满足了
check-changeset-overwrite.mjsexit 0,且它打印的「declared at base」与「declares now」完全相同 ⇒ 改的是散文,frontmatter 逐字节未动,三个包名全部保住。Generated by Claude Code