Skip to content

finding(plugin-detail): #7129 retired hideEmpty on the premise that the spec refuses it — the pin moved to spec 17.3.0 six days later and 17.3.0 DECLARES it, with a describe() promising the behaviour this repo removed #8603

Description

@os-warren

维护者速读

一句话:平台的契约今天明确告诉作者「明细区有 hideEmpty,写 false 就把空区的骨架留下」,而这个应用在那之前刚把这个行为删掉。作者照契约写,发布是绿的,界面上什么都不会变。

⭐ 新读数(本席 2026-09-15T07:47Z 复测,补的正是原卡列为「第一个要问、而且⛔ 没人量过」的那个问题):spec 加这个键不是顺手带进来的,是一次维护者裁定。spec 自己的发布说明写着(照抄):

Declare hideEmpty / collapsible / showBorder on record:details sections — the three keys objectui's renderer has honoured all along

Accept-set widening only; the renderer is unchanged (maintainer ruling 2026-08-23, direction 1).

⇒ spec 声明这个键的理由,正是「objectui 的渲染器一直在兑现它」,并且明确写了「只扩大接受集,渲染器不动」。而 2026-09-01 objectui 这边的裁定(#7129,总监批 #28,维护者「同意」)第一款写的是(照抄):「objectui 侧收敛到 spec —— 退役 @object-ui/types 的 DetailViewSection.hideEmpty 声明与 RecordDetailsRenderer 的读取;spec 照旧拒收该键」。

两条裁定方向相反,后一条的前提在上游已经不成立。⛔ 这不是说 #7129 当时判错了 —— 那时本仓的 pin 还在 17.2.0,从这个仓能看见的一切都支持那个前提。

你对本席的常设口头裁定直接压在这道题上(照抄;⛔ GitHub 上无记录,写在这里以便你确认或否认):

以 objectstack 协议为准,文档应该以实际实现为准。协议不正确的应该先修改协议

按它,这题只剩一个岔口:协议说的对不对。

两个选择:

我的建议是 A:两次裁定里只有 2026-08-23 那一次是带着客户症状做的,#7129 那一次建立在一个已经不成立的前提上;加上你那条常设裁定把协议放在第一位。回退项是 B。

选哪个:A / B?

os-decision-facets

  • ① 项目长远合理性:A 让契约与实现向同一个方向收敛,不新增任何特例;B 也收敛,但要在已发布契约上做一次减法,并连带推翻「渲染器不动、只扩大接受集」那次裁定 —— 推翻哪一条,决定这个平台以后按哪一条办事。
  • ② 实际业务拉动:今天就有人撞上 —— 作者按契约写 hideEmpty,校验通过,行为没有;而 2026-08-23 那次裁定记录的症状(新建记录上三个区丢了两个)正是这个键当初存在的理由。
  • ③ 防 AI 犯错:今天这个状态是最坏的一种 —— 契约本身在骗写它的人:AI 读契约、看到 describe() 承诺的行为、写下去、通过校验,然后什么都没发生,没有任何人会看见这个错。A 让承诺兑现,B 让承诺消失;两者都比「声明着、不兑现」好,拖着不裁最坏。
  • ④ 创业阶段不扩散:B 表面更省(删一个键),但它付的是推翻一次带症状的裁定;A 要写代码,写完之后这个键只有一个读者、一条规则、一个出处,没有第二套需要长期维护的说法。
  • 推荐:A(回退:B)。
  • 本分析看不见什么:我看不见 17.3.0 的发布时刻 —— 本仓的 objectstack 检出是浅的(160 个提交),量不到 4cb2a90 哪天进的上游 main,所以「2026-09-01 那天前提就已经假了」只在两次裁定的日期这一层成立,在已发布产物那一层 ⛔ 没量过;我也看不见 2026-08-23 那次裁定的完整正文(只读到 spec 发布说明对它的转述),若它另有限定条件,A 的理由会被削弱。

Re-measured 2026-09-15T07:47Z by the domain:spec @ objectui PM seat — the card's first open question, answered

The card records its first question as whether @objectstack/spec added hideEmpty at 17.3.0 deliberately, with intent for consumers to implement it, and states that ⛔ nobody had measured which. Measured now, in objectstack-ai/objectstack at origin/main, tip 5741ff1, fetched at the timestamp above.

① The key is declared today, and its describe() promises renderer behaviour. In packages/spec/src/ui/component.zod.ts, the strict record:details section schema carries, verbatim:

hideEmpty: z.boolean().optional().describe('Hide this section\'s empty fields (renderer default: on — and a section whose fields are ALL empty then renders nothing at all: no heading, no skeleton). Set `false` to render empty rows, keeping the section\'s label skeleton on an all-empty record (e.g. a brand-new one).'),

⚠️ The name hideEmpty appears twice in that file for two different components — the other is the reference rail's own component-level key, whose describe() is about folding empty entries into a + N empty expander chip. The reading above is the record:details section one, distinguished by its own text, ⛔ not by a name match.

② It shipped in 17.3.0, and the declaration was a ruling, not an accident. The changeset entry 4cb2a90 sits under the ## 17.3.0 heading in packages/spec/CHANGELOG.md and reads, verbatim:

Declare hideEmpty / collapsible / showBorder on record:details sections — the three keys objectui's renderer has honoured all along

RecordDetailsRenderer spreads every authored section through to DetailSection, which reads all three — but the strict section schema declared only name / label / columns / fields, so objectstack validate warned that an authored key "did nothing". For hideEmpty the warning hid the one key that decides whether a section exists at all: the renderer forces hideEmpty ?? true, and a section whose fields are all empty then renders nothing — no heading, no skeleton — with no declarable spelling to ask the skeleton back (a freshly created record losing two of its three authored sections is how this surfaced).

Accept-set widening only; the renderer is unchanged (maintainer ruling 2026-08-23, direction 1). All three keys are optional with no schema default — the fallbacks are the renderer's, and the describe() texts state them as measured at the .objectui-sha pin: hideEmpty on; collapsible off; showBorder derived (on for a titled section, off for an untitled one).

⇒ The third possibility the card refused to assume away resolves to deliberate — but with a shape the card could not have guessed: spec declared the key because it measured this repository's renderer as already honouring it, and said in the same breath that no renderer was to change. The two rulings are therefore not merely inconsistent in outcome; each was made on a measurement of the other side, taken at a different moment.

③ The objectui ruling's premise, from the ruling itself. objectui#7129's closing comment (id 5494797031, created_at 2026-09-01T13:35:48Z, author huangyiirene) states as its first clause: 「objectui 侧收敛到 spec —— 退役 @object-ui/types 的 DetailViewSection.hideEmpty 声明与 RecordDetailsRenderer 的读取;spec 照旧拒收该键」.

④ ⛔ What is still NOT measured. The date 4cb2a90 landed on objectstack's main, and the publication date of 17.3.0. This container's objectstack checkout is shallow — 160 commits — and a history search for the ## 17.3.0 heading in the spec CHANGELOG returns only a 2026-09-12 commit, which is the depth boundary, ⛔ not the release date. So the claim "the premise was already false when the objectui ruling was made" stands at the level of the two rulings' own dates (2026-08-23 vs 2026-09-01T13:35:48Z), and ⛔ NOT at the level of the published artifact. A ruling that turns on the artifact's timing needs that reading taken in objectstack-ai/objectstack with a full checkout.


Filed by the domain:spec @ objectui seat (session_01Jmxdo7bmeqCQHLSfmLVX9w) from a contract-review verdict on PR #8601 (card #8583). Measured by an independent reviewer running at CONTRACT_REVIEW_TIER; the full verdict is objectui#8583 comment 5587871554. ⛔ Not claimed. ⛔ No domain:* or grading — triage's product.

⚠️ This is a ruling whose premise has since become false. It is ⛔ not a claim that the ruling was wrong when it was made.

The ruling

objectui#7129, closed by ruling at comment 5494797031 — 项目总监席, 总监批 #28, maintainer 「同意」: Q1-A / Q2-C, retire DetailViewSection.hideEmpty and the renderer read. Landed in PR #7464.

The premise was that @objectstack/spec refuses the key by name, so a declared-but-unenforced local key had no upstream home. Correct at the time — the pin was on 17.2.0.

What has changed since — measured, not inferred

reading value
spec pin at the 2026-09-01 ruling 17.2.0 — hideEmpty refused
lockfile moved to 17.3.0 2026-09-07, commit 639114c — six days after the ruling
installed 17.3.0, RecordDetailsProps.sections[] 12 keys, catchall: never, and hideEmpty is among them
runtime probe { fields, hideEmpty: true } → parses OK
the spec's own describe() on it promises renderer behaviour this repo removed under #7129
readers in packages/plugin-detail/src none on the record:details path (only the reference-rail's own key and an i18n label)

Why that combination is a defect and not just a stale note

⇒ An author reads the spec — the contract, the thing the platform tells them is authorable — sees hideEmpty declared with a description promising what it does, writes it, and it parses green at publish. The renderer then silently does nothing.

That is the declared-vs-enforced gap running in the direction that hides, and it is worse than the original #7129 defect: before, the key was local and undeclared upstream; now it is declared upstream and unimplemented downstream, so the contract itself is what misleads the author.

⛔ What this card does NOT decide

Both directions reverse or reinterpret a maintainer-confirmed ruling, so ⛔ neither is a seat's to take:

⚠️ There is a third possibility that should not be assumed away: the spec may have added hideEmpty at 17.3.0 deliberately and with intent for consumers to implement it, in which case #7129 was overtaken by a product decision rather than by an accident. ⛔ Nobody has measured which — that is the first question, and it is answered upstream, not here.

Dedup — with its control

repo:objectstack-ai/objectui hideEmpty → 1 hit, objectui#8376 (closed) — the DetailSection emptiness-predicate card. It mentions #7129 and #7064 as the hideEmpty retirement but is about a different fact (a whitespace-only value counting as filled). ⛔ Not a duplicate. The control is that the query returned a real, relevant hit, so the absence of a premise-inversion card is a reading rather than a dead query.

Provenance note

Found because a contract reviewer checked an implementer's justification for an omission rather than just accepting the omission. PR #8601 correctly declined to declare hideEmpty on the lane's reader criterion, and correctly cited #7129 — the reviewer verified the ruling existed and said what was claimed, and in doing so noticed the pin had moved underneath it.

Refs: objectui#7129 (the ruling) · PR #7464 (the retirement) · objectui#8583 / PR #8601 and verdict 5587871554 (where this was measured) · objectui#8376 (adjacent, not this)


Generated by Claude Code

Activity

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:specobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec lanepriority:p3

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions