Repository navigation
feat(types,react,sdui-parser)!: retire the node-level visibleOn / hiddenOn / disabledOn spellings - #26274
Conversation
…denOn / disabledOn spellings `@objectstack/spec` declares one conditional-visibility predicate at every layer, `visibleWhen` (ADR-0089); objectui carried `visibleOn`, `hiddenOn` and `disabledOn` beside `visibleWhen`, `hidden` and `disabled`, each pair evaluated by the same code with the same polarity. The three are retired with no alias window. - types: `BaseSchema.visibleOn` / `hiddenOn` / `disabledOn` are `never` on the TypeScript face and `aliasKeyRefusal` tombstones on the Zod mirror, each naming the surviving key; `objectui validate` reads the same mirror. Boolean `visible` stays (the spec declares it on actions, nav entries, tabs, alerts and settings). - react: `SchemaRenderer`'s visibility chain is `visibleWhen` / `visible` (shown when truthy) and `hidden` (hidden when truthy); the enablement gate is `disabled`. A stale retired key is not consulted and is held out of the component's props. - sdui-parser: the three are no longer base props; `validateTree` reports each as an `unknown-prop` naming the canonical key (`checkRetiredVisibilityDialect`, exported). - app-shell: the command palette reads a nav entry's `visible` only. - cli: `objectui check` keeps the three as identification markers, as it keeps `body`. - tests re-pointed or retired; two new pins (zod tombstone + strict face, renderer ignores stale keys); skill and docs teach the surviving keys. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E5XcbmSeZTDGm5zCYkC9XV
…e change Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E5XcbmSeZTDGm5zCYkC9XV
… its leg Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E5XcbmSeZTDGm5zCYkC9XV
…s it inherits Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E5XcbmSeZTDGm5zCYkC9XV
Takes main's side of the three sdui registry source digests; they are re-recorded in the next commit. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E5XcbmSeZTDGm5zCYkC9XV
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E5XcbmSeZTDGm5zCYkC9XV
…ings names the surviving node key `table.visibleOn` / `hiddenOn` / `disabledOn` carried the generic node-level reason, which sent the author to the object-view node where the spelling is itself retired. Each row now carries its own reason naming `visibleWhen` / `hidden` / `disabled`; pinned beside the node-face tombstone test. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E5XcbmSeZTDGm5zCYkC9XV
…le-slot change Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E5XcbmSeZTDGm5zCYkC9XV
Takes main's side of the three sdui registry source digests; they are re-recorded in the next commit. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E5XcbmSeZTDGm5zCYkC9XV
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E5XcbmSeZTDGm5zCYkC9XV
Three ui gates still spelled the retired keys and went red on CI: - turbo.objectui.json: `packages/sdui-parser/src/retired-visibility-dialects.ts` joins the `build` inputs, so turbo re-hashes the sdui-parser build when that file moves (scripts/__tests__/turbo-build-inputs.test.ts). - scripts/check-skill-examples.mjs: the `json` marked-fence floor drops 70 -> 69 with its reason beside the row — the "Data-dependent" fence in skills/objectui/guides/schema-expressions.md taught `visibleOn` and was deleted to stay under the guide's token ceiling. - scripts/check-doc-expression-carriage.mjs and its test: the control fixtures and the sabotage keys spell `visibleWhen`, the surviving conditional-visibility predicate; the VISIBILITY_* pin reads three show / hide keys and four condition channels (`disabled` stands alone now that `disabledOn` is retired). Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E5XcbmSeZTDGm5zCYkC9XV
Contract reviewServed-tier: Face: rules. Inputs: card 25926 body and all 8 comments (claim 6419808804 as amended by 6423915890; os-dev-reports 6423841736 round 1 and 6426796108 round 2), PR 26274 body, file list (61 files, +870/-442, head repo = base repo), the net diff against main at merge-base 2409c9c, and the 61 check-runs on the head: 55 success, 6 skipped (path/opt-in), 0 failure. The check-run conclusions are the gate verdicts; nothing was built, run or re-run here. ① Derived judgmentsEvery sentence the two skill files add or change was read against the code at this head. Verdict per claim:
Accept-set summary: the published node face of ② Semver level
③ Boundary flagsDev flags (round 1, 6423841736) and the seat's answers (6423915890), each checked against the head:
Implemented-by: VERDICT: FAIL |
…cate Contract review on #26274, items 5 and 10: - skills/objectui/rules/protocol.md: the props-parking sentence claimed the console refuses the retired spellings by name. NODE_GATE_PREDICATE_KEYS is visibleWhen / visible / hidden / disabled only, and the propsBagLoudRefusal pin drops the three, so the phrase "and their retired spellings" goes (4365 -> 4357 tokens, within the ratchet). - ui/scripts/check-skill-examples.mjs: the json floor 70 -> 69 reason now says what the deleted "Data-dependent" fence was: a `hidden` template example that duplicated the syntax block above it, cut to pay the guide's token ceiling; it did not teach `visibleOn`. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E5XcbmSeZTDGm5zCYkC9XV
The sdui-manifest.sources digest records for core and react moved on both sides; main's side is taken and the records are re-recorded on the merged tree in the next commit. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E5XcbmSeZTDGm5zCYkC9XV
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E5XcbmSeZTDGm5zCYkC9XV
Contract reviewServed-tier: Face: rules. Round 2 of this review; it supersedes record 6426970806 (FAIL on a3f79a5). Inputs: card 25926 body and all 9 comments (claim 6419808804 as amended by 6423915890; os-dev-reports 6423841736, 6426796108 and 6428550273), PR 26274 body, file list (61 files, +870/-443, head repo = base repo), the net diff against main at the new merge-base 61ba04f, the three commits since the last head (586ed40 fix, cfcf6b1 merge of main, ee8049a digest re-record), and the 61 check-runs on ee8049a: 55 success, 6 skipped (path/opt-in), 0 failure, the eight required contexts all success. The check-run conclusions are the gate verdicts; nothing was built, run or re-run here. ① Derived judgmentsWhat moved since a3f79a5, each judged:
Carried from record 6426970806, re-read at this head and unchanged (no code path in the scope list moved between a3f79a5 and ee8049a apart from the two fixed files): the raw-expression claim for Accept-set summary: the published node face of ② Semver levelUnchanged since the previous record and re-read at this head: ③ Boundary flagsRound 3 report 6428550273: Implemented-by: VERDICT: PASS |
The merge queue ejected the PR on the sdui-manifest.sources digest records (components, core, react); main's side is taken and the records are re-recorded on the merged tree in the next commit. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E5XcbmSeZTDGm5zCYkC9XV
|
Director seat reading · 2026-10-26T23:04Z · the review label leaves; landing is the owning seat's Read by the director seat (summon #51, |
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E5XcbmSeZTDGm5zCYkC9XV
Contract reviewServed-tier: Face: rules. Round 3 of this review; it re-certifies record 6428605336 (PASS on ee8049a) on the head the merge queue needs after the MERGE_CONFLICT ejection. Inputs: card 25926 and its comments, PR 26274 body and file list (61 files, +870/-443), the net diff against main at the new merge-base 7435353, the two commits since ee8049a (c74ae05 merge of main, 195b303 digest re-record), and the check-runs on 195b303, read after the last one completed: 61 runs, 55 success, 6 skipped (Build Docs, Temporal Conformance, Dogfood Regression Gate shards, Dogfood Verify CLI, Packed-tarball smoke, 18.x upgrade harness, all path or opt-in), 0 failure; the eight required contexts (Lint & Repo Gates, TypeScript Type Check, Test Core, Build Core, Governed Surface Queue Guard, Lint UI, Type Check UI, Build & E2E UI) and Check Changeset all success. The check-run conclusions are the gate verdicts; nothing was built, run or re-run here. os-zhuang's APPROVED review of 2026-10-26T22:44Z on ee8049a stands undismissed; it is the Tier H word, not this record. ① Derived judgments
② Semver levelUnchanged: ③ Boundary flagsNo new dev flags: the hop is a merge and a regenerated record, no report questions. Carried, unchanged: round-2 finding (c) (ui/scripts tests outside the derived gate families) on the seat as a tooling card; the PR body's stale rows (skills table 375 lines / 4365 tokens, now 374 / 4357; Acceptance note 4 "out of scope", done in-PR; round-1 verification readings) are the seat's pen. Nothing escalated. Implemented-by: VERDICT: PASS |
The merge queue ejected the PR again on the sdui-manifest.sources digest record for types; main's side is taken and the record is re-recorded on the merged tree in the next commit. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E5XcbmSeZTDGm5zCYkC9XV
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E5XcbmSeZTDGm5zCYkC9XV
|
Seat note · domain:spec seat 2 · session Merge-forward after the second queue ejection (MERGE_CONFLICT on Seat's reading, git read commands only:
os-zhuang's APPROVED review stands. That approval is this Tier H PR's landing word, and the queue guard reads it, not the contract record. A fresh |
Main re-recorded six registry packages under scripts/sdui-manifest.sources/ (#26274, #26358) while the manifest's bytes stayed the same: six modify/delete conflicts against this PR's deletion of those records, resolved by deleting them. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A6SEitNngAMugGbuJHWsDS
#26274 retired the node-level visibleOn / hiddenOn / disabledOn spellings and edited the ui copy of the parser, which this branch deletes. Its parser half is carried onto packages/sdui-parser so it is not lost: the three leave SDUI_BASE_PROPS, validateTree answers each with an unknown-prop naming the canonical key, retired-visibility-dialects.ts moves in with its MIT notice, and base-props-one-list-11044 takes the same test edits. The pending changeset 25926 names @objectstack/sdui-parser, where that behaviour now ships. Claude-Session: https://claude.ai/code/session_01Xe3LjBvLrRVA6dmFUEGskm Co-authored-by: Claude <noreply@anthropic.com>
…alects base-props-one-list.test.ts listed visibleOn / hiddenOn / disabledOn among the every-node members. With #26274 carried onto this parser they are no longer base props: each draws one unknown-prop naming its canonical key, and the list is 17 entries. Claude-Session: https://claude.ai/code/session_01Xe3LjBvLrRVA6dmFUEGskm Co-authored-by: Claude <noreply@anthropic.com>
Requested by Steedos · project thread
Fixes #25926
Clause-②: no (narrowing)
Metadata-protocol: narrowed
What changed
@objectstack/specdeclares one conditional-visibility predicate at every layer,visibleWhen(ADR-0089). objectui carried three spellings of its own beside the spec's keys:visibleOnbesidevisibleWhen,hiddenOnbesidehidden,disabledOnbesidedisabled, each pair evaluated by the same code with the same polarity. The three are retired, immediately, with no alias window.@object-ui/types—BaseSchema.visibleOn/hiddenOn/disabledOnare?: nevertombstones on the TypeScript face andaliasKeyRefusalarms on the Zod mirror (thechartType→typeshape). Each refusal names the key, the surviving key, the unchanged polarity, and the remedy ("rename the key; the value is unchanged"). Booleanvisiblestays onBaseSchema(see Acceptance note 1).@object-ui/react—SchemaRenderer's visibility chain isvisibleWhen/visible(SHOW legs, negated) andhidden(HIDE leg, not negated); the enablement gate isdisabledalone. A stale retired key that reaches the renderer unvalidated is not consulted and is held out of the component's props (thebodytreatment). The diagnostics' key lists, the props-bag refusal and the fault copy follow.@object-ui/sdui-parser— the three leaveSDUI_BASE_PROPS;validateTreereports each as anunknown-propdiagnostic that names the surviving key and says the spelling is retired (checkRetiredVisibilityDialect,RETIRED_VISIBILITY_DIALECTS, exported).@object-ui/cli—objectui validatereads the Zod mirror and prints the tombstone (measured below).objectui checkkeeps the three inOBJECTUI_STRUCTURAL_KEYSas identification markers, exactly as it keepsbody(objectui#6771): a file authored under a retired spelling is still recognised and sent tovalidate; the sweep itself does not parse recognised files (triage ruling objectui#11007).@object-ui/app-shell— the command palette reads a navigation entry'svisibleonly; the?? item.visibleOnfallback read a keyNavigationItemnever declared.declaredPredicate/dom-props, react diagnostics, plugin-chatbot,complex.ts): comment-only, the retired spellings dropped from the prose.hiddenOn/disabledOn/visibleOncases are retired from 13 react/components/sdui-parser pins and the surviving cases re-pointed; two new pins:node-visibility-dialects-retired.test.ts(oneinvalid_typeissue at the key, message carriesDid you mean X → Y?,.describe()equals the message,z.inputisundefined, both document faces refuse, canonical spellings parse) andSchemaRenderer.visibilityDialectsRetired.test.tsx(a stale key is not a leg and does not leak, each with a control on the surviving key).skills/objectui/rules/protocol.mdandguides/schema-expressions.mdteachvisibleWhen/hidden/disabled(raw expression or${}template) and name the retired spellings as refused;ui/content/docsguide/expressions.md,api/schema-reference.md,guide/schema-playground.md, plusguide/schema-rendering.md,guide/metadata-diagnostics.mdandutilities/runner.mdx(see deviation 1). The form field's view-levelfields[].visibleOnis a different slot and is left as it was, with a sentence saying so..changeset/25926-retire-node-visibility-dialects.md(types / react / sdui-parser minor, app-shell patch; BREAKING; ADR-0087not-required (no-migration-prescription)on the objectui-surface precedent of aftercare(ui-merge): move the 65 travelled ui/.changeset files into the root .changeset so the first post-cutover Version PR bumps @object-ui/* (parent #25677, before #25753) #25831).Skills diff readings (lines; tokens = ceil(bytes/4) per
check-skills-token-ratchet)skills/objectui/rules/protocol.mdskills/objectui/guides/schema-expressions.mdSKILL.mdThe guide's net content is paid by deleting one redundant
hiddentemplate example (the "Data-dependent" pattern duplicated the syntax block above it); the token ratchet is green at the ceiling.维护者速读(草稿)
改了什么
objectui 节点上的
visibleOn/hiddenOn/disabledOn三个拼法退役:类型面变为never,Zod 镜像按名拒绝并指向visibleWhen/hidden/disabled(同一谓词、同一极性,只改键名,值不变);渲染器只读存活的三个键;SDUI 解析器对 TSX 树同样按名报告;技能包与文档改教存活拼法;objectui validate直接打印拒绝原因。为什么改
规范(ADR-0089)在每一层只声明一个条件可见谓词
visibleWhen,从未声明*On后缀;objectui 自造的三个别名与规范键被同一段代码、同一极性求值,只增加了方言数,没有增加能力。保留别名意味着同一件事有两种写法、两份文档和两组测试要维护。风险与代价(含回滚)
@object-ui/*包发版(三个 minor、一个 patch),changeset 已标 BREAKING;无迁移脚本(objectui 面不进packages/spec的 ADR-0087 账本,沿 aftercare(ui-merge): move the 65 travelled ui/.changeset files into the root .changeset so the first post-cutover Version PR bumps @object-ui/* (parent #25677, before #25753) #25831 的处置)。sdui.manifest.json字节不变,仅源码摘要记录重录)。席位意见
(留空)
你要做的
Metadata-protocol: narrowed的判定(认领写的是「按 diff 判定,预期 none」,实测为 narrowed:可写面上的键被 tombstone)。visible按规范继续保留在BaseSchema上(ADR-0089 D4 明确不在范围),派发令中「收敛为 visibleWhen + hidden」的字面读法未照办。Acceptance notes
visiblestays. The order reads "collapse tovisibleWhen+hidden".visibleis spec-declared on action nodes, app navigation entries, tab rows,record:alert, bulk actions, action params and settings, and ADR-0089 D4 keeps booleanvisibleout of scope; the premise comment says "keep booleanvisibleonly where the spec declares it". Removing the renderer leg would leave a declared key unevaluated (声明即强制) and would breach the 38-file reader surface. So the chain collapses from five legs to three:visibleWhen/visible/hidden. Flagged for the seat to confirm.Metadata-protocol: narrowed, notnone. The claim expectednone("ui types only"); the diff tombstones three keys on objectui's authorable node face, which is "a key removed or tombstoned" by ruling A's definition, and the level gate pairs it with the claim'sno (narrowing)arm. Both lines are on the changeset; the ADR-0087 gate accepts the disposition. The seat may amend the claim.objectui checknames nothing by itself. The order saysvalidate/checkrefuse the three by name.validatedoes (measured below).checkis an advisory sweep that, by the objectui#11007 triage ruling, does not parse a file its structural arm recognised; a stale file is reported "recognised but not validated" and sent tovalidate. Dropping the three markers instead would let a leaf file carrying only a retired key and an unregisteredtypefall out of the sweep unmentioned, which is the silence thebodyprecedent refused. Left as the ruling has it; the seat may rule otherwise.zod/objectql.zod.ts(tableKeyRefusal('hiddenOn')and siblings at lines 1171–1199) still prescribe "belongs on the object-view node itself", which now points at a tombstoned spelling. Not this card's keys; for the seat to file.responsive.hiddenOn,FormField.visibleOn(view-level) and thesuggestions.zod.tsexamples are untouched as the order states.Deviations
guide/expressions.md,api/schema-reference.md,guide/schema-playground.md. Three more files underui/content/docstaught the retired spellings as live node keys (guide/schema-rendering.md×11,guide/metadata-diagnostics.md×3 node-level mentions,utilities/runner.mdx×1) and are fixed in place under the bounded in-place-fix exemption (same defect class, mechanical rename, same gate family, no new verification surface). The seat is asked to amend the claim's file surface.VISIBILITY_*_KEYSlists as the ablation site; the first ablation stayed green because the legs are hand-writtenifblocks and the lists only drive diagnostics. The paragraph now names the leg; the second ablation (one restored leg) went red on exactly the predicted case. Both runs are in the report.Verification
objectui validateon{ "type": "text", "content": "Open", "visibleOn": "data.ok" }→ exit 1, one error atvisibleOn, codeinvalid_type, message "Unrecognized key(s) on this node:visibleOn. Did you meanvisibleOn→visibleWhen? …Rename the key; the value is unchanged."; the same node spelledvisibleWhen→ exit 0.visibleOn: 'data.ok'pasted into the typed node literal of the new types test →tsc -p tsconfig.test.jsonred, TS2322 "Type 'string' is not assignable to type 'undefined'" at line 78; restored viagit checkout HEAD -- FILE→ green.scripts/ablation-replace.mjs, mutation landed and restored, blob == HEAD): restoring avisibleOnleg after thevisibleleg inSchemaRenderer.tsx→SchemaRenderer.visibilityDialectsRetired.test.tsx1 failed | 6 passed, the failing case being "visibleOn: "false"is not a SHOW leg"; the earlier list-only mutation stayed 7 passed (recorded, see deviation 2).@object-ui/types,core,sdui-parser,components,react,app-shell,cli,plugin-chatbot— all exit 0 afterpnpm --filter '@object-ui/cli...' --filter '@object-ui/app-shell...' build.cd ui && pnpm exec vitest run packages/types/ packages/sdui-parser/ packages/react/ packages/components/ packages/plugin-chatbot/ --maxWorkers=2(under the verify lock, 19m58s) → first run 1 failed | 1050 passed (static-table-narrow-surface.test.tspinned the table mirror's exact tombstone set, which now inherits the three node-face tombstones; re-pointed in 6f044d4, that file alone re-run: 39 passed);… packages/app-shell/ --maxWorkers=2→ Test Files 1735 passed | 1 skipped (1736), Tests 15960 passed | 9 skipped;pnpm --filter @object-ui/core test→ 211 files passed;pnpm --filter @object-ui/cli test→ 30 files passed; the 13 directly touched/new pins run alone → 13 files, 267 tests passed. (Per-packagepnpm --filter @object-ui/types testfails withspawn ENOENTfrom the repo root: vitest lives inui/node_modules/.bin, so the ui suites are run fromui/.)node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderived 68 families at the final head; every command run from the worktree with its exit captured before any pipe;--ranreconciliation: 68 derived, 68 run, 0 UNRUN. 66 exit 0.pnpm check:type-check-debt→ re-run under the lock after the suites (exit 0, held the lock 131s).pnpm check:dual-build-cjs-loads→ exit 99 (never acquired the lock behind the 74-minute app-shell run; a full-build gate CI runs on the PR, not re-run here).(narrowing)arm): head 6f044d4, afterpnpm --filter '@objectstack/dogfood^...' build(exit 0);pnpm --filter @objectstack/dogfood testunder the lock → VERDICT command-exit 0 · held the lock 973s (16m13s) · Test Files 297 passed | 3 skipped (300) · Tests 2527 passed | 18 skipped (2545); no failure.check-platform-checklist --changed-from origin/main: 0 anchored files, nothing judged.check-skills-token-ratchet,check-adr-0087-registration,check-changeset-no-major,check-empty-changeset,check:doc-authoring,check:pm-skill-id-lint,check-sdui-manifestall green on the committed tree.🤖 Generated with Claude Code
https://claude.ai/code/session_01E5XcbmSeZTDGm5zCYkC9XV