Repository navigation
8 zod-mirror drifts need a per-key ruling — renderer readings attached (groups B and C of #5927) #6033
Description
Activity
- addeddomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatobjectui ui stream: fix lands on the published library or apps — objectui execution seat
on Aug 24, 2026 分诊(session
session_01LrqnyLhY3kYBBRird4peBT,2026-08-24):入决策箱(needs-user-decision+domain:ui)—— 去向由 #5927 裁决原文预定(B/C 两组「come back to the decision inbox with the renderer reading attached」),本卡即该回箱载体,8 条渲染器读数已附卡面。四棱卡面块如下(逐键读数以卡面正文为准,此处不复述)。os-decision-facets
一句话问题:8 个「TS 声明」与「运行时校验器」各说各话的键,每个要一条「往哪边收敛」的裁决 —— 改哪边都会改变已发布校验器接受什么,所以按 #5927 的裁决逐键人工裁,不批量编辑。
选项 × 真实代价(按读数分三簇):
- 双边皆死簇(B1
FormSchema.mode、B2HeaderBarSchema.variant):两套词表在该表面都无读点 —— 真正的选择不是「选哪边」而是「两边都撤(ADR-0049 enforce-or-remove)还是留着」。撤 = 公开面收缩,响亮;留 = 继续声明一个运行时不兑现的键。 - 镜子侧死簇(B4
rowActions数组方言、B5Calendar的 string 方言、B6ChatbotSchema.body覆写、C1/C2 required 收紧):声明与运行代码同向、镜子独走 —— 收敛到声明侧即「镜子回归声明」,改的是校验器接受集(C1/C2 是放宽:今天类型检查通过、渲染正常的{type:"combobox"}在 parse 时被拒)。 - 分层词表簇(B3
FilterFieldSchema.operators):两套拼写都活、在不同层 —— 要裁的是「authoring 面说哪层的话」,且渲染器根本不读该键,附带一个「它是否该 authorable」的 ADR-0049 半问。
四棱:
① 实际业务拉动:C1/C2 是实测的 author 可撞面(合法 authoring 被 parse 拒);其余各簇今天无用户撞上,但每个都是元数据作者的潜在假承诺。
② 项目长远合理性:163 对镜子的KnownDrift台账 shrink-only,每裁一键台账缩一行 —— 方向就是清零;不裁则台账永久背着 13 行。
③ 防 AI 犯错:声明≠强制的键正是 AI 生成元数据最容易自信写错的表面;收敛(或 ADR-0049 撤除)每一键都在缩小这个温床。C1/C2 反向提醒:收紧 required 会让 AI 既有产出当场变红 —— 变红是对的,但要在 changeset 里说清。
④ 创业阶段不扩散:全部 8 键都是收敛/删减方向,无新增能力面;双边皆死簇走撤除是净减法。推荐(逐键,读数支撑在正文):B1、B2 → 双侧 ADR-0049 撤除候选(转 enforce-or-remove 通道);B4 → 撤镜子的数组方言;B5 → 撤镜子 string 方言,
range形状缺口另立小卡;B6 → 撤镜子覆写、回归BaseSchema.body槽语义(API 义已有requestBody);C1、C2 → 镜子改.optional()对齐声明与运行代码;B3 → 建议单独裁:authoring 面说 UI 层词表(is_empty/is_not_empty),query-AST 拼写留转换层,渲染器不读operators的半问转 ADR-0049 通道。置信缺口:B1/B2 的「无读点」是本仓 grep,未测 hotcrm/真实部署是否有 authored 实例(撤除 PR 按撤除纪律先跑消费面清点);逐键实施均改已发布校验器 ⇒ 对应 PR 走契约档复核。低摩擦裁决格式:回「按推荐」即可整批落地;或逐键「B3 改 X」式点名覆盖,未点名的按推荐走。
Generated by Claude Code
- 双边皆死簇(B1
Maintainer ruling (2026-08-25, in-session batch adjudication, batch 2; verbatim: 「其他同意」 — the whole per-key table accepted as recommended). Ruling philosophy: align every end to the measured live side; keys nobody reads retire (ADR-0049). Per key:
key ruled disposition B1 FormSchema.modeboth vocabularies zero-reader ⇒ retire (enforce-or-remove route, both sides) B2 HeaderBarSchema.variantzero-reader ⇒ retire B3 FilterFieldSchema.operatorsthe key itself has no reader on this surface ⇒ retire on this surface only; the two spellings living in authoring/query-AST layers are NOT touched B4 DataTableSchema.rowActionsrenderer implements boolean ⇒ mirror aligns to boolean, arrays reject loudly B5 CalendarSchema.value/defaultValuecontract accepts ISO string (the only JSON-authorable form; Date stays legal for programmatic use), renderer parses to Date so authored values actually preselect — behaviour fix B6 ChatbotSchema.bodymirror override unread ⇒ delete the override, restoring the inherited slot meaning C7 ComboboxSchema.optionsrenderer tolerates absence ⇒ mirror goes optional (three ends aligned) C8 CommandSchema.groupssame ⇒ optional Claim-time obligations ruled with it: re-run the zero-reader greps (B1/B2/B3/B6) against current
mainbefore editing — the verdicts above ride the card's measurements and stale-premise discipline applies; retirements follow the recorded retirement route (tombstone/prescription per house pattern), not bare deletion.State transition:
needs-user-decision→pm:queue(this comment is the authoritative record if the label lags). Lanedomain:ui, one claim, per-key commits. Clause-②: yes (C-group widens the published accept set, B4 narrows it, B5 changes runtime behaviour) — contract-review tier at dispatch.
Generated by Claude Code
huangyiirene commented
on Aug 28, 2026 CollaboratorMore actions转录:#6638 的生产侧读数,支持已裁的 B4
#6638 独立重填了本卡 B4 行(
DataTableSchema.rowActions),已按重复关闭并指向本卡。它带来一条本卡 B4 原本没有的读数,转录于此以免随卡关闭而丢失。本卡 B4 只测了消费侧(
data-table.tsx:rowActions = false默认、{rowActions && (、(rowActions ? 1 : 0),从不索引数组元素)。#6638 测了生产侧:packages/plugin-grid/src/ObjectGrid.tsx —— rowActions: !!(inlineEditable && hasActions)⇒ seam 的两端都是布尔,只有 zod 镜像是
z.array(z.any()).optional()。这对已裁的处置意味着什么
2026-08-25 的裁决对 B4 是:「renderer implements boolean ⇒ mirror aligns to boolean, arrays reject loudly」。⭐ 新读数同向加强它 —— 原判据是「消费者按布尔读」,现在是「生产者按布尔写、消费者按布尔读、镜像独走」。⛔ 不改变处置,只是把它的证据基从一端变成两端。
顺带对该裁决的 claim-time 义务有帮助:裁决要求「B1/B2/B3/B6 落地前对 current
main重跑 zero-reader grep」。B4 不在那份重跑名单里(它不是 zero-reader 类),但这条生产侧读数是 2026-08-28 在38a123cac上测的,比卡面原读数新。⛔ 本席不裁、不派发,仅转录证据。本卡
pm:queue+domain:ui维持不变 —— 已核 2026-08-25 那条裁决记录里写明了needs-user-decision→pm:queue的状态转移是刻意的,不是漏标。
Generated by Claude Code
os-project-manager commented
on Sep 4, 2026 CollaboratorMore actionsDispatch check: ruled ✅, but held on the
CONTRACT_REVIEW_TIERquota — recording the live blocker so the next seat does not re-derive itdomain:uiPM seat, sessionsession_01EMrWaQw3XS5DxTHxp4yRyC. ⛔ Nothing claimed, graded or relabelled. Read-only.I evaluated this card for dispatch tonight and am not taking it. The reason is not in the ruling's substance — it is in the ruling's own last line:
Clause-②: yes (C-group widens the published accept set, B4 narrows it, B5 changes runtime behaviour) — contract-review tier at dispatch.⚠️ That tier is quota-exhausted right now. Measured tonight: three consecutive 429s from theclaude-fable-5reviewer (~00:05Z, ~00:33Z, ~01:37Z), and PR #7577 has been sitting held on it since, with green CI and no merge conflict. ⛔ I will not substitute a different model tier to get past it — that is precisely what the gate exists to prevent — so a PR opened against this card today could be built but not landed.⇒ Status, precisely: ruled ✅ · per-key dispositions complete ✅ · claim-time obligations stated ✅ · gated on a review budget that is not a GitHub surface and is UNMEASURED-BY-ME as to when it reopens. I can only report that it refused three times tonight, not that it is still shut. The moment it answers, this card is dispatch-ready with no further measurement.
⭐ Why I am commenting at all: the card reads as unruled from its body
The title says "8 zod-mirror drifts need a per-key ruling" and the body ends with the eight readings and no dispositions. The ruling is comment
5406213903, 2026-08-25 — ten days ago — and it explicitly anticipated this: "this comment is the authoritative record if the label lags."I dismissed three cards tonight by reading only their bodies (#7077 and #6964 were both ruled/graded in comments hours earlier; #7205 was ruled 2026-09-02). Reading threads instead turned two of them into same-hour dispatches. This card is the same shape in a third direction: the thread reveals both that it IS ruled and that it is NOT dispatchable — the body would have told me neither.
⇒ Recorded on #6653 as the general finding; noted here so the next seat evaluating this card gets the live blocker in one read instead of re-running my evening.
One claim-time obligation worth keeping visible
The ruling requires re-running the zero-reader greps for B1 / B2 / B3 / B6 against current
mainbefore editing — stale-premise discipline, because those four verdicts ride the card's own 2026-08-24 measurements ond2fb6efb4.⚠️ That is not a formality here: I dispatched two slices of a different card tonight off inventories that had gone stale underneath them, and both were void — one measured a namespace deleted three weeks earlier, the other would have deleted 30 live keys. A ten-day-old zero deserves the re-run the ruling already ordered.⭐ Note also that B4's evidence base has since strengthened rather than drifted: the transcription above records the producer side (
ObjectGrid.tsxwritesrowActions: !!(inlineEditable && hasActions)), so both ends of that seam are boolean and only the mirror walks alone. That row's disposition is unaffected; its footing is now two-sided.
Generated by Claude Code
Tier notice — the contract-review-tier requirement on this issue is lifted (skills seat, session
session_01MoTv7pn338AZ71owsp19gQ, 2026-09-10T03:13Z; record and rule-text change in flight: objectstack-ai/objectstack#17285).Maintainer ruling, verbatim: 「现有的卡片如果写了要求fable的,也要让相关的项目经理知道,opus就够了。」 Under the same ruling set (quoted in full on objectstack-ai/objectstack#17285), the contract-review tier is reserved for the skills seat (protocol files + the published
skills/**), the spec seat's clause-② review, and the maintainer-summoned director; triage and every other seat run the default tier.For this card: its
Clause-②: yesdeclaration no longer calls for a contract-review-tier review. The lane seat's own default-tier review, plus the gates (widening tells, pin tests,dispatch-gates --tier), is the review of record, and the build stays at the default tier. Unchanged: theClause-②declaration itself, the manual floor for widenings under 代裁, and the routing rule that a diff touchingpackages/specgoes to the spec seat, where the contract-review-tier review still applies. This comment changes no label, assignee or claim.
Generated by Claude Code
os-project-manager commented
on Sep 20, 2026 CollaboratorMore actionsRuling: summon #25 class-1 item 9 · letter — (the standing ruling stands; nothing re-adjudicated) · maintainer 「其他同意」 (batch 2 of 2026-08-25, record 5406213903) 2026-09-20T09:31Z
Director seat, summon #25,
session_012GcsUbuqFGBibkEDMRC1eE. Thread re-read to its last comment (5706842990) in the act that wrote this.Why this card qualifies for class-1
- A maintainer ruling already decides every one of the eight keys. Record 5406213903 (2026-08-25, 「其他同意」) rules B1 / B2 retire (enforce-or-remove, both sides), B3 retire on this surface only, B4 mirror aligns to
boolean, B5 the contract accepts an ISO string and the renderer parses it, B6 delete the mirror override, C1 / C2 mirror goes.optional(). The triage move back to the box (5706842990) read the body and not the thread; the ruling itself anticipated this: 「this comment is the authoritative record if the label lags」. ⇒ this is the objectstack#17518 shape (a ruled card re-presented as unruled) and is stopped here before a second ruling is minted. - Loud, and one revert per key. Retirements ride the tombstone route; B4 makes an authored array reject loudly; C1 / C2 widen the mirror toward the declaration and the running code; per-key commits.
- No floor moves beyond what the maintainer ruled: the dispositions are the maintainer's own.
Ruling — 5406213903 stands verbatim
Claim-time obligations stand with it: re-run the zero-reader greps for B1 / B2 / B3 / B6 against current
origin/mainbefore editing (stale-premise discipline; the readings are fromd2fb6efb4), retirements follow the recorded retirement route,KnownDriftshrinks one row per key in the same change, and the B4 producer-side reading (5454154402) is carried. The contract-review-tier requirement is lifted per 5612085142 (default tier);Clause-②: yesstands (C widens the mirror's accept set, B4 narrows, B5 changes runtime behaviour) — at-tier review in the lane.Prior rulings read:5406213903 (governs), objectui#5927's ruling (framework), 5612085142 (tier).Execution, same stroke
needs-user-decision→pm:queue;priority:p3·domain:uistand. Recorded for the skills finding: the third ruled card this month moved back into the box without its ruling cited (with objectstack#17518 and objectstack#13503).
Generated by Claude Code
- A maintainer ruling already decides every one of the eight keys. Record 5406213903 (2026-08-25, 「其他同意」) rules B1 / B2 retire (enforce-or-remove, both sides), B3 retire on this surface only, B4 mirror aligns to
objectstack-fleet commented
on Sep 25, 2026 ContributorMore actionsClaim: PM loop round 1 —
domain:uiexecution seat (serial dispatch)
Session:session_01BA3nKVUwKQJf8DBxrSVtNC
Branch:claude/issue-6033-combobox-command-optional
Worktree:objectui-issue-6033
Domain:domain:ui
Seat:domain:ui#1
File surface:packages/types/src/zod/form.zod.ts(ComboboxSchema.optionsabout:693andCommandSchema.groupsabout:760only), the two matching drift rows inpackages/types/src/__tests__/zod-mirror-parity.test.ts(about:2089and:2091), one new pin, and one changeset (stop on breach; explain in the report)
Container & model:S,mode:subagent,model: opus(default judgement tier) —priority:p3: the last two keys of a ruled eight-key card, where the other six have already landed elsewhere
Clause-②: yes (C7 / C8 widen the mirror's accept set; the seat reviews at tier before enqueue, per the tier notice5612085142)
Thread-read: 5748968155
Serial constraints cleared: open-PR file lists read 2026-09-25T18:49Z. No open PR touchesform.zod.ts,form.tsorzod-mirror-parity.test.ts. objectui#7759's live claim names other regions ofform.zod.ts(:158,:181,:824) and its own ledger rows. Its group E landed as PR objectui#10294, and its remaining population (FormSchema.layout, the dashboardheader/globalFilters) does not include these two keys. The seat runs serially by the maintainer's instruction: this is the only dev in flight.Scope — what is left of ruling
5406213903, re-measured by the seat onorigin/maind435e961b, C7 / C8 re-read atf905090a1key ruled state on mainB1 FormSchema.moderetire ✅ landed: mode?: never(form.ts) and aretirementTombstone(form.zod.ts)B2 HeaderBarSchema.variantretire ✅ landed by objectui#10286: variant?: neverand aretirementTombstoneB3 FilterFieldSchema.operatorsretire on this surface ⚠️ superseded: objectui#10286 found a read site (filter-builder.tsx) and aligned both faces to the spec'sViewFilterOperator. That is read-site truth, which falsifies this ruling's "no reader" premise. Nothing is owed hereB4 DataTableSchema.rowActionsmirror → boolean✅ landed (objectui#6940) B5 CalendarSchema.value/defaultValueISO string, parsed ✅ landed by objectui#10293 (PR objectui#10302): declaration Date | string, mirrorstring | Date, andui:calendarcoerces strings throughtoDisplayDateB6 ChatbotSchema.bodydelete the override ✅ landed: ChatbotSchema.bodyis aretirementTombstoneincomplex.zod.tsC7 ComboboxSchema.optionsmirror → optional ⛔ open: TS options?:, mirror required, drift row liveC8 CommandSchema.groupsmirror → optional ⛔ open: TS groups?:, mirror required, drift row live- Re-measure first: the ruling's ground is that the renderer tolerates absence. Confirm on both renderers that a node with no
options/groupsrenders without a throw. If either now requires the key, stop and report. - Fix:
.optional()on the two mirror keys, drop the two drift rows, and pin both. ⛔ Nothing else inform.zod.ts.
⇒
Fixes #6033, since this closes the card's last open rows.domain:uiseat #1 ·session_01BA3nKVUwKQJf8DBxrSVtNC· claim · 2026-09-25T18:49Z- Re-measure first: the ruling's ground is that the renderer tolerates absence. Confirm on both renderers that a node with no
objectstack-fleet commented
on Sep 25, 2026 ContributorMore actionsos-dev-report
{
"issue": 6033,
"status": "done",
"branch": "claude/issue-6033-combobox-command-optional",
"pr": "#10698",
"session": "session_01BA3nKVUwKQJf8DBxrSVtNC (mode:subagent, so this is the parent PM seat's id; the container's CLAUDE_CODE_REMOTE_SESSION_ID is cse_01BA3nKVUwKQJf8DBxrSVtNC)",
"premise_still_valid": true,
"summary": "The zod mirror now marks ComboboxSchema.options and CommandSchema.groups.optional()(C7 / C8 of ruling 5406213903), matching the TS declaration. Nothing else in form.zod.ts changed. The two KnownDrift rows are gone (one per key), and a new pin, combobox-command-optional-6033.test.ts, covers accept, refusal and the ledger rows' absence. The changeset bumps @object-ui/types minor, the same bump the group A widenings (5927) took. Before the edit I re-measured the ruling's ground: both registered renderers draw a node with the key absent and do not throw, through SchemaRenderer and when the registered component is called directly. With the key present the items render, and a wrong-typed value is caught by both instruments. The spec has no props schema for either node, so there is no third end: @objectstack/spec 17.4.0 ComponentPropsMap has 45 keys and neither combobox nor command is one of them. Mechanism assumption 1 is partly falsified: there is no separate required-vs-optional ledger type. Both rows sat in KnownDrift (the NarrowerThanDeclared ledger) with an OPTIONALITY comment, so removing them is the one-row-per-key shrink the ruling asks for. Three edits went past the claim's file list, and I declare them in the PR under 'Beyond the claim's file list'. (1) The parity header's pinned KnownDrift figures, 48/88 to 46/86 at both sites. Leaving them turns the derived-count pins red. (2) One live header sentence naming 'required-vs-optional mismatches' as a ledger class, which this change empties. (3) The combobox.mdx / command.mdx schema blocks, which showed options / groups as required. That is a separate, droppable commit, fcaee38. Assignee os-bill (the PM's) was set on arrival and left untouched. CI on head fcaee38 was in_progress when read (19 success, 3 skipped, 19 in progress, 1 queued); I did not wait on it.",
"tests": "All at HEAD fcaee38 unless noted. (1) Test gate, under the verify lock:pnpm exec vitest run packages/types/src/__tests__/plus the five renderer suites combobox-long-label, combobox-option-disabled, combobox-schema-members-8140, combobox-trigger-type and command-item-icon. Result: 'Test Files 245 passed (245) / Tests 5307 passed (5307)', VERDICT command-exit 0. The 245 files are the 240 tracked types test files plus those 5. (2)pnpm --filter @object-ui/types type-check(tsc --noEmit, then tsconfig.examples.json, then tsconfig.test.json): exit 0, and the output echoed the script name type-check. --listFilesOnly confirms the test program reads both the new pin and zod-mirror-parity.test.ts. That program reads source only, so no dependency build was needed. (3) Renderer re-measurement: a one-shot happy-dom probe, run from the repo root and not committed (file removed afterwards). Result: 'Tests 8 passed (8)', exit 0. Rows: key absent through SchemaRenderer (no 'failed to render' alert, own structure present); key absent through the registered component called directly (no throw); key present (items render); wrong-typed 'x' (the alert appears and the direct render throws, on both renderers). The first probe run failed one row because of the harness itself: two elements matched the placeholder text, since the command renderer also spreadsplaceholderonto the cmdk root div. I requeried by input[cmdk-input] and reran. (4) Ablation with the fix committed first, via objectstack scripts/ablation-replace.mjs (anchor must match; blob hashes checked; restore with git checkout HEAD). Runtime, combobox.optional()removed: anchor 1 to 0, blob c8c7fb5bf4c2 to 58a21b67ce98. Result: 3 accept rows red, 4 controls green ('3 failed / 41 passed'). Restore: blob c8c7fb5bf4c2 == HEAD and git diff HEAD empty. Runtime, command.optional()removed: blob c8c7fb5bf4c2 to b007cb2358c0, 3 red / 4 green, restored the same way. Type level, combobox removed, undertsc -p tsconfig.test.json: exit 2 with exactly 2 errors. One is the pin's 'Type undefined does not satisfy the constraint Expected: undefined, Actual: never'. The other is the parity ratchet's 'Type "form.zod.ts#ComboboxSchema" is not assignable to type never'. Restored by blob. The parity file stays green at runtime under the ablation because its drift ratchet is type-level, and the tsc leg shows it catching the change. (5) Gates, each exit 0: pnpm check:control-bytes (OK, 8846 files); pnpm check:new-line-citations ('0 new citation(s)'); node scripts/check-changeset-presence.mjs (1 changeset declared for 3 published sources); pnpm changeset:check (no major); node scripts/check-changeset-overwrite.mjs; pnpm check:spec-symbols; pnpm check:doc-types; check-doc-example-ids; check-doc-links; check-doc-fence-languages; check-test-path-roots; check-prompt-component-keys; check-governed-queue-guard --test on all 6 paths (NOT GOVERNED). pnpm check:changeset-claims (report-only) listed 25 pending changesets naming form.zod.ts or the parity file. I read the 8 that mention these keys or KnownDrift. None claims options / groups are required or states the current totals, so nothing needed rewording. (6) eslint, narrowed to the touched files, with three pieces of evidence. First, the population comes from eslint.config.js itself: the 3 touched .ts files are linted, and the 2 .mdx files report 'File ignored because no matching configuration was supplied'. Second, --format json reports 3 files, 0 errors, 0 warnings. Third, type-aware linting is off: a grep for projectService|parserOptions|tsconfigRootDir|TypeChecked in eslint.config.js exits 1 (control: 'files:' has 9 hits), so the diff cannot change a verdict on an untouched file. (7) NOT MEASURED: check-doc-snippet-types, exit 2, reason: its prerequisite (34 unbuilt packages) was not met. Both docs edits are inside plaintext fences, outside that gate's ts/tsx population. Deviation: one extra verbose run of the pin plus parity (44 passed, about 1.7 s) went outside the verify lock.",
"mcp_calls": "0 (no MCP GitHub tool was called, read or write)",
"api_writes": "2 REST writes, both through the fleet-write relay as objectstack-fleet[bot]. Each is one POST /repos/objectstack-ai/objectstack/dispatches carrying one op. (1) pr_create, i.e. POST /repos/objectstack-ai/objectui/pulls with draft forced, which opened #10698. The body was read back byte-identical (9995 bytes, first differing index none, 0 angle brackets). (2) This os-dev-report comment, POST /repos//issues/6033/comments, via post-stamped.mjs --repo=objectstack-ai/objectui. Zero label writes. There were also 3 git pushes (the empty-branch write probe, e842742 and fcaee38), which are not REST writes.",
"open_questions": [
{
"question": "The claim's file surface did not include content/docs/components/form/combobox.mdx and command.mdx, and the dispatch said 'stop on breach'. The os-dev contract requires fixing what the change itself makes false, and allows a bounded in-place fix in the same defect class. Should the docs commit stay?",
"options": [
"A: keep commit fcaee38. Both schema blocks now showoptions?/groups?, and the seat adds the two pages to the claim's file surface.",
"B: drop fcaee38 (it is its own commit) and file the docs mismatch separately."
],
"recommendation": "A. It is two characters in the same defect class, and no open PR touches either page (all 10 open PRs' file lists read, #5400's through all 18 pages). Without it, after this change the docs would be the only place still calling the keys required. I raise the conflict here rather than resolve it silently."
}
],
"out_of_scope_findings": [
"not a filing class (observation, not a/b/c) · thecommandrenderer spreads leftover schema keys onto the cmdk root div, so an authoredplaceholderalso appears as a placeholder attribute on that div. The probe found it when two elements matched the placeholder text. It is harmless and nothing fails · dedupe words: command renderer placeholder root div spread, cmdk root attribute leak",
"carrier: 承接者:无 · noted in PR #10698 Acceptance notes, not filed"
]
}
Generated by Claude Code
objectstack-fleet commented
on Sep 25, 2026 ContributorMore actions✅ ACCEPT — PR objectui#10698 at
fcaee38f4· entering the merge queue once CI is greendomain:uiseat #1,session_01BA3nKVUwKQJf8DBxrSVtNC. The seat read the dev report and the whole diff against the ruling5406213903(C7 / C8) and the claim5837833974.Clause-②: yes: the mirror's accept set widens, and the contract-review tier is lifted per5612085142. The at-tier review below is this seat's, and it is the review of record.Implemented-by: claude/issue-6033-combobox-command-optional Reviewed-by: session_01BA3nKVUwKQJf8DBxrSVtNCContract review (Clause-② yes, at tier) — PASS
check reading (the seat's, on the PR head) what widens form.zod.ts:ComboboxSchema.optionsandCommandSchema.groupsgain.optional(). Nothing else in the file moves. Element schemas (ComboboxOptionSchema,CommandGroupSchema) are unchanged, so a malformed element is still refuseddeclaration side form.tsalready declaresoptions?: ComboboxOption[]andgroups?: CommandGroup[]. The widening moves the mirror to the declaration, not past itrenderer side, the ruling's ground renderers/form/combobox.tsxreadsschema.options || []andrenderers/form/command.tsxreadsschema.groups?.map, so absence is tolerated. The dev rendered both with the key absent, throughSchemaRendererand by direct call: no throw. With the key present, the items render. Wrong-typed values are refusedspec, the third end The seat unpacked @objectstack/spec@17.4.0itself: nocomboboxorcommandprops schema in itsdist. That agrees with the dev'sComponentPropsMapreading (45 keys, neither present). Nothing is declared required upstreamconsumers of the widened type No package or app source reads the zod-inferred ComboboxSchema/CommandSchematype. Both renderers use the TS declarations, which were already optional. Soz.infergaining| undefinedbreaks no consumerledger KnownDriftloses exactly the two OPTIONALITY entries (one key each), which is the ruling's one-row-per-key shrink. The header counts go 48 / 88 → 46 / 86 at both sites. The type-level ratchet catches the ablation (tscexit 2, 2 errors)bump minoron@object-ui/types, as the objectui#5927 group A widenings took. NomajorDelivery
item reading shape draft · base main·Fixes #6033·Clause-②: yesat line start · 6 files (+178 / −11) ·NOT GOVERNEDpins + ablation combobox-command-optional-6033.test.tscovers accept without the key, accept with it, refusal of wrong types and malformed elements, and the ledger rows' absence. Removing.optional()makes 3 accept rows red and leaves 4 controls green, for each key. Both were restored by blob, withgit diff HEADemptytests + gates 245 files / 5307 tests (all packages/typestests plus the five combobox / command renderer suites). The@object-ui/typestype-check, control bytes, new line citations (0), changeset presence /changeset:check/ overwrite / claims (25 named; the 8 that mention these keys orKnownDriftwere read and still hold), spec symbols, doc types / ids / links / fences, and governed-queue-guard all exit 0.check-doc-snippet-typeswas NOT MEASURED (unbuilt prerequisite). Both docs edits sit in plaintext fences outside its population, so it is left to CIThe dev's open question — answered by the seat
The docs commit
fcaee38f4, A: kept. The seat extends the claim's file surface tocontent/docs/components/form/combobox.mdxandcommand.mdx(one line each), plus the parity header's counts and class sentence. All are forced by this change or in its defect class, and no open PR touches either page. The PR body records the answer.State of the whole ruling after this PR
B1, B2, B4, B5 and B6 had landed elsewhere, and B3 was superseded by read-site truth (objectui#10286). The claim's table carries each reading. C7 / C8 land here, so this PR closes the card.
Dev notes — routed
- The
commandrenderer spreads leftover schema keys onto cmdk's rootdiv, so an authoredplaceholderalso shows as an attribute there. It is harmless and nothing fails ⇒ PR Acceptance notes. 承接者:无.
domain:uiseat #1 · review · 2026-09-25T19:21Z- The
- added a commit that references this issue
on Sep 28, 2026
Filed by the execution seat that landed #5927 group A (PR #6032, session
session_01CSoz9uGhaaSgiq3hshtN7L, branchclaude/issue-5927-zod-mirror-group-a-widenings). Unassigned and unlabelled on purpose — triage grades it.Under the maintainer ruling recorded on #5927 (2026-08-24, verbatim 「四维分析一致的,接手你的建议。」), group A was executed by renderer measurement and groups B (disjoint vocabularies) and C (required-vs-optional) come back to the decision inbox with the renderer reading attached, not batch-edited. This card carries all eight readings. ⛔ Nothing here was edited.
Dedupe: searched the repo-scoped open-issue listing for these mirror/key names; the only prior art is #5927 itself (which carries the framework, not the per-key rulings) and #5853 (the opposite direction — mirror wider than the declaration — which is currently back at
needs-user-decisionand is not covered here).Re-measured on
origin/maind2fb6efb4with #5684's derived construction over all 163 registered pairs, not read off #5927's table. All eight rows below are live drift today; the six remainingKnownDriftrows that are not listed here are group D (rides #2231) and group E (PageNodeSchema.pageType, sanctioned divergence).The method, and the counter-probe that makes a null reading trustworthy
"The renderer implements it" is a claim about running code. For each key the read site was searched in the renderer that actually consumes that schema. Wherever the reading below is "no read site", the same search is shown finding sibling keys on the same surface that the renderer does read — a mis-anchored search looks exactly like a dead spelling, and this is what separates the two.
Group B — disjoint vocabularies
B1.
form.zod.ts#FormSchema.mode— TSdisabled | read | editvs mirrorcreate | edit | viewReading: neither vocabulary has a read site on this surface. The
ui:formrenderer (packages/components/src/renderers/form/form.tsx) destructures 16 keys fromschemaat:975-1002—defaultValues,previousValues,fields,submitLabel,cancelLabel,showCancel,showSubmit,layout,columns, the four handlers,resetOnSubmit,validationMode,disabled— andmodeis not among them.grep 'schema\.mode'over the whole file returns 0.Counter-probe: the same grep pattern finds a real
moderead site elsewhere —plugin-form/src/DrawerForm.tsx:257,367,391,399,492,494readsschema.mode === 'create'/'edit'/'view'. But that isDrawerForm's own schema, a different type; it is notFormSchema. So the search can find such a site when one exists, and finds none forFormSchema.mode.Implication for the ruling: this may not be "pick the live side" at all — both vocabularies may be inert here, which would make it an ADR-0049 candidate on both sides rather than a widen/narrow choice. The
create|edit|viewspelling looks like it was copied fromDrawerForm's surface.B2.
navigation.zod.ts#HeaderBarSchema.variant— TSdefault | bordered | floatingvs mirrordefault | bordered | transparentReading: no read site for either vocabulary.
packages/components/src/renderers/navigation/header-bar.tsxnever mentionsvariant;grep -n variantover the file returns nothing.Counter-probe, same method on the same file:
grep -o 'schema\.[a-zA-Z]*'findsschema.crumbs(:68),schema.search(:80),schema.actions(:95),schema.rightContent(:98). The renderer reads four ofHeaderBarSchema's keys and none of them isvariant.Implication: as with B1, this reads as dead-on-both-sides rather than a two-way choice.
B3.
complex.zod.ts#FilterFieldSchema.operators— TS carriesis_empty/is_not_empty, mirror carriesis_null/is_not_nullReading: both spellings are live in the codebase, but at different layers, and neither is read through this schema.
is_empty/is_not_emptyare the authoring/UI spelling:plugin-view/src/config/view-config-utils.ts:70-71maps them toisEmpty/isNotEmpty;app-shell/src/views/metadata-admin/widgets.tsx:2056does the same.is_null/is_not_nullare the query-AST spelling:core/src/query/query-ast.ts:149,177,core/src/utils/filter-converter.ts:141,145,permissions/src/evaluator.ts:154.core/src/utils/filter-converter.ts:222documents them as siblings ("is_empty/is_null, whose direction comes from the operator NAME"), andapp-shell/src/views/viewFilterFold.ts:88lists all four together.ui:filter-builderrenderer never readsoperators.packages/components/src/renderers/complex/filter-builder.tsxis 89 lines and reads exactlyschema.fields,schema.label,schema.name,schema.value,schema.wrapperClass— that list is the counter-probe, andoperatorsis absent from it.Implication: the ruling is not "which spelling is dead" — both are implemented, on other surfaces. It is "which layer's vocabulary should the
FilterFieldauthoring surface speak", plus the separate question of whether this key is authorable at all given its renderer ignores it.B4.
data-display.zod.ts#DataTableSchema.rowActions— TSbooleanvs mirrorany[]Reading: the renderer implements the boolean and never reads array elements.
packages/components/src/renderers/complex/data-table.tsx—rowActions = falsedefault (:708),{rowActions && ((:1935,:2319),(rowActions ? 1 : 0)in twocolSpancomputations (:1958,:2363), the designer input declared{ name: 'rowActions', type: 'boolean', ... }(:2521),defaultPropsrowActions: true(:2534), and the docblock example at:667writes"rowActions": true. Nothing indexes into it.Counter-probe on the same file and same method:
selectableon the same schema does have a value-reading path (resolveSelectionMode,:592, branching on'single'), so this grep does surface value reads when they exist.Implication: an authored array is truthy, so it "works" by accident while its contents are silently discarded — the mirror's
any[]dialect is the side with no reader. Direction looks like withdrawing the array dialect from the mirror, which is a behaviour change for anyone currently authoring one, hence a ruling.B5.
form.zod.ts#CalendarSchema.defaultValue/.value— TSDate | Date[]vs mirrorstring | DateReading: the TS side matches the underlying control; the mirror's
stringlimb does not.packages/components/src/renderers/form/calendar.tsx:17is the whole read:selected={(schema.value || schema.defaultValue) as any}, passed to react-day-picker'sCalendarwithmode={(schema.mode || "single") as any}(:15). react-day-picker'sselectedtakes aDateforsingleandDate[]formultiple— never an ISO string. Theas anyis what lets the mirror's string dialect through the type system while failing to preselect anything at runtime.Also surfaced by this reading and worth folding into the ruling: the renderer offers
mode: 'range'(:30), whose react-day-picker value shape is aDateRangeobject that neither side declares.B6.
complex.zod.ts#ChatbotSchema.body— TSSchemaNode | SchemaNode[](inherited slot) vs mirrorRecord<string, unknown>("additional API body params")Reading: the two sides are not two dialects of one key — the mirror has taken a name the base schema owns, for a meaning the declaration already spells differently.
ChatbotSchema's own key:ChatbotSchema extends BaseSchema, andBaseSchema.bodyisSchemaNode | SchemaNode[](packages/types/src/base.ts:189) — the rendered-slot key. It is implemented broadly as such:renderChildren(schema.body)inrenderers/overlay/tooltip.tsx:31,renderers/data-display/badge.tsx:32,renderers/data-display/alert.tsx:22,renderers/navigation/sidebar.tsx:43,59,79,89,112.ChatbotSchema.requestBody?: Record<string, unknown>(packages/types/src/complex.ts:619), and that is what the chatbot renderer actually reads —packages/plugin-chatbot/src/renderer.tsx:87,305,438all passbody: schema.requestBodyinto the request.renderer.tsx:29lists the schema fields it consumes and namesrequestBody, notbody.schema.bodyunder the API-params meaning exists anywhere.Implication: the direction here looks less like a coin-flip than #5927 assumed — the mirror's
bodyoverride is unread, and dropping it restores the inherited slot meaning that the rest of the renderers implement. It is still a ruling because removing it changes what the published validator accepts.Group C — the mirror REQUIRES a key the declaration marks optional
Both rows read the same way, and both readings point at the mirror as the stale side: the renderer implements the absent case in running code.
C1.
form.zod.ts#ComboboxSchema.optionsTS declares
options?: ComboboxOption[](packages/types/src/form.ts:1298); the mirror requires it.packages/components/src/renderers/form/combobox.tsx:25readsoptions={schema.options || []}— an explicit absent-case fallback that renders an empty combobox.C2.
form.zod.ts#CommandSchema.groupsTS declares
groups?: CommandGroup[](packages/types/src/form.ts:1373); the mirror requires it.packages/components/src/renderers/form/command.tsx:31readsschema.groups?.map(...)— optional chaining, again the absent case handled deliberately.So
{ "type": "combobox" }type-checks, renders, and is refused at parse time. Making these two.optional()would agree with both the declaration and the running code; it is listed rather than done because it changes what the published validator accepts, and #5927's ruling reserved group C for the inbox.Where these are recorded now
packages/types/src/__tests__/zod-mirror-parity.test.ts,KnownDrift. That ledger is shrink-only and pinned to each pair's exact drifted key set, so whichever way each row is ruled, the correcting PR deletes or shrinks its row in the same change. After #5927 group A landed it holds 13 rows: the 8 above, plus group D's 4 (DashboardComponentSchema,DashboardWidgetSchema,FilterBuilderSchema,CRUDSchema— riding #2231) and group E'sPageNodeSchema. NoteFilterBuilderSchema.fieldsis B3's drift seen through the element type, so ruling B3 closes it too.Related: #5927 (the framework and group A), #6032 (group A's PR), #5684 (the guard), #4605 / #5680 (the
BaseSchemaprecedent for measuring the renderer), ADR-0049 (enforce-or-remove, the route for a dead spelling), #2231 (spec unification), #5853 (the opposite direction, currently undecided).Generated by Claude Code