Skip to content

feat(types,react,sdui-parser)!: retire the node-level visibleOn / hiddenOn / disabledOn spellings - #26274

Merged
objectstack-fleet[bot] merged 18 commits into
mainfrom
claude/project-thread-flw1js
Oct 27, 2026
Merged

objectstack-fleet[bot] merged 18 commits into
mainfrom
claude/project-thread-flw1js

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Steedos · project thread

Fixes #25926

Clause-②: no (narrowing)
Metadata-protocol: narrowed

What changed

@objectstack/spec declares one conditional-visibility predicate at every layer, visibleWhen (ADR-0089). objectui carried three spellings of its own beside the spec's keys: visibleOn beside visibleWhen, hiddenOn beside hidden, disabledOn beside disabled, 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 / disabledOn are ?: never tombstones on the TypeScript face and aliasKeyRefusal arms on the Zod mirror (the chartType → type shape). Each refusal names the key, the surviving key, the unchanged polarity, and the remedy ("rename the key; the value is unchanged"). Boolean visible stays on BaseSchema (see Acceptance note 1).
  • @object-ui/react — SchemaRenderer's visibility chain is visibleWhen / visible (SHOW legs, negated) and hidden (HIDE leg, not negated); the enablement gate is disabled alone. A stale retired key that reaches the renderer unvalidated is not consulted and is held out of the component's props (the body treatment). The diagnostics' key lists, the props-bag refusal and the fault copy follow.
  • @object-ui/sdui-parser — the three leave SDUI_BASE_PROPS; validateTree reports each as an unknown-prop diagnostic that names the surviving key and says the spelling is retired (checkRetiredVisibilityDialect, RETIRED_VISIBILITY_DIALECTS, exported).
  • @object-ui/cli — objectui validate reads the Zod mirror and prints the tombstone (measured below). objectui check keeps the three in OBJECTUI_STRUCTURAL_KEYS as identification markers, exactly as it keeps body (objectui#6771): a file authored under a retired spelling is still recognised and sent to validate; the sweep itself does not parse recognised files (triage ruling objectui#11007).
  • @object-ui/app-shell — the command palette reads a navigation entry's visible only; the ?? item.visibleOn fallback read a key NavigationItem never declared.
  • Readers (components form/action/disclosure renderers, core declaredPredicate / dom-props, react diagnostics, plugin-chatbot, complex.ts): comment-only, the retired spellings dropped from the prose.
  • Tests — the hiddenOn / disabledOn / visibleOn cases are retired from 13 react/components/sdui-parser pins and the surviving cases re-pointed; two new pins: node-visibility-dialects-retired.test.ts (one invalid_type issue at the key, message carries Did you mean X → Y?, .describe() equals the message, z.input is undefined, both document faces refuse, canonical spellings parse) and SchemaRenderer.visibilityDialectsRetired.test.tsx (a stale key is not a leg and does not leak, each with a control on the surviving key).
  • Skill + docs — skills/objectui/rules/protocol.md and guides/schema-expressions.md teach visibleWhen / hidden / disabled (raw expression or ${} template) and name the retired spellings as refused; ui/content/docs guide/expressions.md, api/schema-reference.md, guide/schema-playground.md, plus guide/schema-rendering.md, guide/metadata-diagnostics.md and utilities/runner.mdx (see deviation 1). The form field's view-level fields[].visibleOn is a different slot and is left as it was, with a sentence saying so.
  • Changeset .changeset/25926-retire-node-visibility-dialects.md (types / react / sdui-parser minor, app-shell patch; BREAKING; ADR-0087 not-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)

file lines before → after tokens before → after
skills/objectui/rules/protocol.md 377 → 375 4368 → 4365
skills/objectui/guides/schema-expressions.md 646 → 642 6571 → 6571
whole package, sum of all SKILL.md 4525 → 4525 —

The guide's net content is paid by deleting one redundant hidden template 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 自造的三个别名与规范键被同一段代码、同一极性求值,只增加了方言数,没有增加能力。保留别名意味着同一件事有两种写法、两份文档和两组测试要维护。

风险与代价(含回滚)

席位意见

(留空)

你要做的

  1. 确认接受「立即退役、无别名窗口」(派发令如此要求)与 Metadata-protocol: narrowed 的判定(认领写的是「按 diff 判定,预期 none」,实测为 narrowed:可写面上的键被 tombstone)。
  2. 确认 Acceptance note 1:布尔 visible 按规范继续保留在 BaseSchema 上(ADR-0089 D4 明确不在范围),派发令中「收敛为 visibleWhen + hidden」的字面读法未照办。
  3. 按席位复核后合入;本 PR 为 Tier H,等人批。

Acceptance notes

  1. visible stays. The order reads "collapse to visibleWhen + hidden". visible is spec-declared on action nodes, app navigation entries, tab rows, record:alert, bulk actions, action params and settings, and ADR-0089 D4 keeps boolean visible out of scope; the premise comment says "keep boolean visible only 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.
  2. Metadata-protocol: narrowed, not none. The claim expected none ("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's no (narrowing) arm. Both lines are on the changeset; the ADR-0087 gate accepts the disposition. The seat may amend the claim.
  3. objectui check names nothing by itself. The order says validate / check refuse the three by name. validate does (measured below). check is 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 to validate. Dropping the three markers instead would let a leaf file carrying only a retired key and an unregistered type fall out of the sweep unmentioned, which is the silence the body precedent refused. Left as the ruling has it; the seat may rule otherwise.
  4. Out of scope, found while reading: the table-slot refusals in 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 the suggestions.zod.ts examples are untouched as the order states.

Deviations

  1. File surface, docs: the claim lists guide/expressions.md, api/schema-reference.md, guide/schema-playground.md. Three more files under ui/content/docs taught 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.
  2. Ablation docblock corrected once: the new react pin's reverse-verification paragraph first named the VISIBILITY_*_KEYS lists as the ablation site; the first ablation stayed green because the legs are hand-written if blocks 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 validate on { "type": "text", "content": "Open", "visibleOn": "data.ok" } → exit 1, one error at visibleOn, code invalid_type, message "Unrecognized key(s) on this node: visibleOn. Did you mean visibleOn → visibleWhen? …Rename the key; the value is unchanged."; the same node spelled visibleWhen → exit 0.
  • Reverse verification (TypeScript face): visibleOn: 'data.ok' pasted into the typed node literal of the new types test → tsc -p tsconfig.test.json red, TS2322 "Type 'string' is not assignable to type 'undefined'" at line 78; restored via git checkout HEAD -- FILE → green.
  • Ablation (scripts/ablation-replace.mjs, mutation landed and restored, blob == HEAD): restoring a visibleOn leg after the visible leg in SchemaRenderer.tsx → SchemaRenderer.visibilityDialectsRetired.test.tsx 1 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).
  • Type-check: @object-ui/types, core, sdui-parser, components, react, app-shell, cli, plugin-chatbot — all exit 0 after pnpm --filter '@object-ui/cli...' --filter '@object-ui/app-shell...' build.
  • Tests: 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.ts pinned 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-package pnpm --filter @object-ui/types test fails with spawn ENOENT from the repo root: vitest lives in ui/node_modules/.bin, so the ui suites are run from ui/.)
  • Gates: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 68 families at the final head; every command run from the worktree with its exit captured before any pipe; --ran reconciliation: 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).
  • Dogfood battery (final head, (narrowing) arm): head 6f044d4, after pnpm --filter '@objectstack/dogfood^...' build (exit 0); pnpm --filter @objectstack/dogfood test under 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-manifest all green on the committed tree.

🤖 Generated with Claude Code

https://claude.ai/code/session_01E5XcbmSeZTDGm5zCYkC9XV

…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
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
…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
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
@github-actions github-actions Bot added size/xl documentation Improvements or additions to documentation tests tooling labels Oct 26, 2026
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
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: a3f79a571f9efdda3d7d35444f67c998920d3b84
Local-runs: none
Scope: skills/objectui/guides/schema-expressions.md, skills/objectui/rules/protocol.md + ui/packages/types/src/base.ts, ui/packages/types/src/zod/base.zod.ts, ui/packages/types/src/zod/tombstone.zod.ts, ui/packages/types/src/zod/objectql.zod.ts, ui/packages/types/src/tests/node-visibility-dialects-retired.test.ts, ui/packages/react/src/SchemaRenderer.tsx, ui/packages/react/src/utils/propsBagDiagnostic.ts, ui/packages/react/src/tests/SchemaRenderer.propsBagLoudRefusal.test.tsx, ui/packages/core/src/evaluator/ExpressionEvaluator.ts, ui/packages/sdui-parser/src/validate.ts, ui/packages/sdui-parser/src/retired-visibility-dialects.ts, ui/packages/cli/src/commands/check.ts, ui/scripts/check-skill-examples.mjs, scripts/check-skills-token-ratchet.mjs, .changeset/25926-retire-node-visibility-dialects.md. The remaining 45 paths (ui docs, reader comments, test re-points, sdui-manifest.sources records, turbo inputs, check-doc-expression-carriage) are the lane seat's ACCEPT at TIER_DEFAULT.

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 judgments

Every sentence the two skill files add or change was read against the code at this head. Verdict per claim:

  1. RIGHT. visibleWhen / hidden / disabled each take a boolean, a ${} template or a raw expression (guide lines 239-240, 253, 289; protocol.md table rows). ExpressionEvaluator.evaluateCondition returns a boolean as-is, evaluates a string containing ${ on the template path, and otherwise treats the ENTIRE trimmed string as one expression via evaluateExpression. SchemaRenderer's evaluateVisibilityPredicate and evaluateEnablementPredicate hand the RAW authored value to that function. So "hidden": "data.status === 'draft'" and "disabled": "!data.hasPermission" without ${} are evaluated exactly as the table now says, and the old "hidden needs ${}" sentence was the one that under-reported.

  2. RIGHT. The retired spellings are refused by name with the surviving key and "Rename the key; the value is unchanged" (guide 257-258, protocol.md 24, checklist item 4). base.zod.ts carries aliasKeyRefusal('visibleOn', 'visibleWhen', ...), ('hiddenOn', 'hidden', ...), ('disabledOn', 'disabled', ...); tombstone.zod.ts renders that as z.never({ error }) so the issue is invalid_type at the key with "Did you mean X → Y?" plus the detail, and the same string is the .describe(). node-visibility-dialects-retired.test.ts pins StrictAnyComponentSchema (the objectui validate face) and AnyComponentSchema refusing each key with that remedy, and the canonical node parsing on both faces; Test UI is green on the head. objectui validate therefore prints the refusal, as protocol.md says.

  3. RIGHT. TypeScript face: BaseSchema.visibleOn / hiddenOn / disabledOn are ?: never; the test's Eq assertions pin the input type as undefined. Polarity unchanged: each tombstone and each refusal text says shown-when-truthy / hidden-when-truthy / disabled-when-truthy, which is what the surviving legs do.

  4. RIGHT. Renderer chain and precedence. VISIBILITY_SHOW_KEYS = ['visibleWhen', 'visible'], VISIBILITY_HIDE_KEYS = ['hidden'], ENABLEMENT_NODE_GATE_KEYS = ['disabled']; shouldHide tests visibleWhen, then visible, then a declared hidden; the visibleOn / hiddenOn / disabledOn legs are gone and the three keys are held out of the props spread (the body treatment). protocol.md's "visible takes priority over hidden" holds, and visible is still a legal, evaluated leg (ADR-0089 D4, seat answer 1).

  5. WRONG, and the reason for the verdict. protocol.md lines 111-113 (changed hunk): "Silent for every key except the node-gate predicates (visibleWhen / visible / hidden / disabled, and their retired spellings), which are refused by name on the console, with the migration". At this head the by-name refusal is reportRefusedPropsPredicate(type, id, NODE_GATE_PREDICATE_KEYS, ...), and NODE_GATE_PREDICATE_KEYS is the spread of VISIBILITY_CHAIN_KEYS and ENABLEMENT_NODE_GATE_KEYS, i.e. exactly visibleWhen, visible, hidden, disabled. A visibleOn / hiddenOn / disabledOn parked under props is NOT in that set, so it is not refused by name there, and the PR's own diff proves it: SchemaRenderer.propsBagLoudRefusal.test.tsx deletes the three retired keys from the "is named on the console" it.each and rewrites the two-key case from hiddenOn to hidden. props is also undeclared on BaseSchemaCore (passthrough), so no Zod path names it either. What such a node gets is the generic dropped-bag console.warn on a non-element type (no retirement, no migration) and a plain React-prop spread on an element:* type. The code is right to narrow the set (a retired key is not a node gate any more); the sentence that says the opposite is the defect. Fix: delete ", and their retired spellings" (3 tokens, inside protocol.md's 3-token headroom), or say that a retired spelling under props is refused on the node by objectui validate, not on the console.

  6. RIGHT. SDUI parser: the three leave SDUI_BASE_PROPS; validateTree answers each with an unknown-prop diagnostic naming the canonical key (checkRetiredVisibilityDialect, exported with RETIRED_VISIBILITY_DIALECTS). Not taught by the skill files, consistent with them.

  7. RIGHT. objectui check keeps the three in OBJECTUI_STRUCTURAL_KEYS as identification markers and names nothing itself (ruling objectui#11007, seat answer 3); protocol.md attributes the by-name refusal to objectui validate only, which is the true statement.

  8. RIGHT. The object-view table slot: visibleOn / hiddenOn / disabledOn rows in objectql.zod.ts now carry their own reason naming the surviving node key; the other node-level rows keep the generic prescription; pinned in the types test with a control on table.visibleWhen. Closes the dev's out-of-scope finding (b) in-PR as the seat ordered.

  9. RIGHT. Skills token ratchet, read independently as ceil(utf8 bytes / 4): protocol.md 17457 bytes = 4365 tokens against ceiling 4368 (was 4368); schema-expressions.md 26283 bytes = 6571 against ceiling 6571 (was 6571). Both green; the PR body's readings match; the guide sits at zero headroom, which is what paid for the deleted fence.

  10. WRONG in its reason, green in its number. ui/scripts/check-skill-examples.mjs lowers MARKED_FLOOR json 70 to 69 and the comment says the deleted "Data-dependent" fence "taught visibleOn, which that card retires". The deleted fence (guide hunk at line 272) is { "hidden": "${!data.items || data.items.length === 0}" }: it taught hidden, the surviving key, and was deleted as a duplicate of the syntax block above it to pay the token ceiling, exactly as the PR body says. The floor move is honest (a deletion, not an unmark; Lint UI green); the provenance line is false and should name the real reason (a redundant hidden example deleted for ceiling room). Fix in the same patch round; it is a script comment, not a governed sentence, so it is a finding beside item 5, not a second FAIL ground.

  11. ADVISORY, not false. Guide line 239: "The node-level condition keys are visibleWhen (show), hidden and disabled" omits visible, which is still legal and is taught fifteen lines down ("Status-based") and in protocol.md's table. Read as the three keys that take the raw-expression form being introduced, it is not wrong; naming visible there would cost tokens the guide does not have. Leave or fix at the dev's choice.

  12. RIGHT (lane remainder, read for consistency only). CommandPalette drops the ?? item.visibleOn fallback (NavigationItem never declared it); SDUI manifest sources re-recorded with sdui.manifest.json byte-identical (check-sdui-manifest green); turbo.objectui.json lists the new parser file; check-doc-expression-carriage controls spell visibleWhen.

Accept-set summary: the published node face of @object-ui/types narrows by three keys (refused by name, no alias window); @object-ui/react stops reading them; @object-ui/sdui-parser stops accepting them as base props; @object-ui/app-shell stops reading an undeclared nav key. No packages/spec key, export or registry moves. All of that is the card's order as amended by the seat.

② Semver level

.changeset/25926-retire-node-visibility-dialects.md: @object-ui/types minor, @object-ui/react minor, @object-ui/sdui-parser minor, @object-ui/app-shell patch; BREAKING banner with Before / Now / Fix (FROM → TO rename, value unchanged; fields[].visibleOn named as a different slot); exactly one ADR-0087 marker, not-required (no-migration-prescription), with the category closure spelled out (check-adr-0087-registration green). Both declaration lines are on the PR body and the changeset and match the claim as amended: Clause-②: no (narrowing) and Metadata-protocol: narrowed. The level follows the kind: narrowed takes at least one moved package minor or above (check-changeset-no-major's rule), and three of the four moved publishers are minor; app-shell patch is right, since what narrows there is a consumer-side read of a key NavigationItem never declared, not an authorable face. Packages with comment-only edits (components, core, cli, plugin-chatbot) carry no entry; Check Changeset is green. skip-changeset would be wrong and is not used. Clause-②: no (narrowing) is the correct arm: a published accept set shrinks, nothing widens.

③ Boundary flags

Dev flags (round 1, 6423841736) and the seat's answers (6423915890), each checked against the head:

  • Q1 keep boolean visible: answered A; the head keeps it on BaseSchema and as the second SHOW leg. Matches.
  • Q2 Metadata-protocol: narrowed: answered A, claim amended; changeset unchanged. Matches.
  • Q3 objectui check names nothing itself: answered A (ruling 11007); check.ts keeps the markers with the reason in its comment. Matches.
  • Q4 three extra ui docs: answered A, surface amended. The three files are in the file list. Matches.
  • Out-of-scope (b), table-slot refusals: ruled in scope and fixed in-PR; verified in objectql.zod.ts and pinned. Closed.
  • Out-of-scope (c), per-package vitest spawn ENOENT: filed as tooling(ui): pnpm --filter @object-ui/PKG test from the repo root fails with vitest: not found / spawn ENOENT, because vitest is installed only under ui/node_modules/.bin #26276. Not this PR's.
    Round 2 (6426796108): open_questions is empty. One new out-of-scope finding (c): ui/scripts/__tests__ are outside every derived dispatch-gates family and red only on the PR's Test UI shards — carried by the seat, noted, not filed. Escalated here as a tooling card for the seat to file or decline; it does not bear on this PR.
    Deviation 1 (three extra docs) accepted by the seat. Deviation 2 (ablation docblock corrected to name the leg) is recorded in the test and both reports.
    PR body: the seat holds the pen and must correct it before the maintainer reads it. Stale or false as of this head: Acceptance note 4 calls the table-slot refusals out of scope (done in-PR); Verification still reads check:dual-build-cjs-loads exit 99 and 68/66 gate families (round 2: 65/65 green, dual-build green) and names head 6f044d4 for the dogfood battery (round 2: a3f79a5, 297/2527); the Skills readings table is correct. The 维护者速读 draft: its three claims (no in-repo example, fixture or skill example hits; objectui validate prints the refusal; docs and skill rewritten in the same PR) hold at this head.
    Not escalated: none beyond the two findings in ① (items 5 and 10), both one-line fixes in the dev's patch round; a new head then needs a fresh record.

Implemented-by: claude/project-thread-flw1js
Reviewed-by: session_01RyMY3prh9GRva1Tt37Di3i

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
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: ee8049acc90d737caaf384629e6ca25858468933
Local-runs: none
Scope: skills/objectui/guides/schema-expressions.md, skills/objectui/rules/protocol.md + ui/packages/types/src/base.ts, ui/packages/types/src/zod/base.zod.ts, ui/packages/types/src/zod/tombstone.zod.ts, ui/packages/types/src/zod/objectql.zod.ts, ui/packages/types/src/tests/node-visibility-dialects-retired.test.ts, ui/packages/react/src/SchemaRenderer.tsx, ui/packages/react/src/utils/propsBagDiagnostic.ts, ui/packages/react/src/tests/SchemaRenderer.propsBagLoudRefusal.test.tsx, ui/packages/core/src/evaluator/ExpressionEvaluator.ts, ui/packages/sdui-parser/src/validate.ts, ui/packages/sdui-parser/src/retired-visibility-dialects.ts, ui/packages/cli/src/commands/check.ts, ui/scripts/check-skill-examples.mjs, scripts/check-skills-token-ratchet.mjs, scripts/sdui-manifest.sources/core.json, scripts/sdui-manifest.sources/react.json, .changeset/25926-retire-node-visibility-dialects.md. The remaining paths (ui docs, reader comments, test re-points, the other three sdui-manifest.sources records, turbo inputs, check-doc-expression-carriage) are the lane seat's ACCEPT at TIER_DEFAULT.

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 judgments

What moved since a3f79a5, each judged:

  1. RIGHT, the FAIL ground is closed. 586ed40 rewrites protocol.md lines 112-113 to "Silent for every key except the node-gate predicates (visibleWhen / visible / hidden / disabled), which are refused by name on the console, with the migration, instead of gating nothing." That is exactly the set NODE_GATE_PREDICATE_KEYS spreads at this head (VISIBILITY_CHAIN_KEYS plus ENABLEMENT_NODE_GATE_KEYS) and exactly the by-name pin left in SchemaRenderer.propsBagLoudRefusal.test.tsx. The retired spellings now appear in the skill tree in two places only, protocol.md line 24 and the guide line 257, both as "retired and refused by name by objectui validate", which is true of aliasKeyRefusal on the Zod mirror and the StrictAnyComponentSchema pin. Nothing the two files still teach prescribes a retired spelling.

  2. RIGHT, the floor reason is now true. ui/scripts/check-skill-examples.mjs keeps json 70 to 69 and its comment now says the deleted "Data-dependent" fence was a hidden template example that duplicated the syntax block above it, cut because the guide's ceiling had no room for both once that block was rewritten for the retirement. That matches the guide hunk (the deleted fence is { "hidden": "${!data.items || data.items.length === 0}" }) and the ceiling reading below. Lint UI and the ui script tests are green on the head; the self-test passes per the dev.

  3. NOTED, unchanged by the seat's decision. Guide line 239 "The node-level condition keys are visibleWhen (show), hidden and disabled" still omits visible; the guide sits at 6571 of 6571 tokens, visible is taught fifteen lines down and in protocol.md's table, and the sentence introduces the three keys that take the raw-expression form. Not false; recorded as the seat ordered.

  4. RIGHT, the merge hop carries only main. cfcf6b1 merges main at 61ba04f (31 commits over the previous base 2409c9c). Read as the net PR diff against the new merge-base: the file list is the identical 61 paths; every path's added and removed lines are byte-identical to the a3f79a5 net diff except the two files 586ed40 changed and the two digest records below. The only paths both main and this PR touch are scripts/sdui-manifest.sources/core.json and react.json, which the merge resolved to main's side and ee8049a then re-recorded on the merged tree (core sources 111, react sources 70, new sha256 each, manifestSha256 unchanged); sdui.manifest.json is byte-identical to main (empty diff), check-sdui-manifest and check-regen-pending sit inside the green Lint & Repo Gates. No other content entered the PR through the merge.

  5. RIGHT, the skills token ratchet, read independently as ceil(utf8 bytes / 4): protocol.md 17428 bytes = 4357 tokens against ceiling 4368 (headroom 11, 374 lines); schema-expressions.md 26283 bytes = 6571 against ceiling 6571 (headroom 0, 642 lines). Both green; the dev's round-3 readings match.

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 visibleWhen / hidden / disabled (evaluateCondition evaluates a non-${} string as one expression, and the renderer hands it the raw value); the ?: never tombstones and the aliasKeyRefusal arms with invalid_type at the key and "Rename the key; the value is unchanged"; objectui validate printing that refusal through StrictAnyComponentSchema; the three-leg chain visibleWhen, visible, hidden with visible ahead of hidden, the one-leg disabled gate, and the three retired keys held out of the props spread; the sdui-parser unknown-prop diagnostic naming the canonical key; objectui check keeping the three as identification markers (ruling objectui#11007); the object-view table-slot rows naming the surviving node key with a table.visibleWhen control. All RIGHT.

Accept-set summary: the published node face of @object-ui/types narrows by three keys (refused by name, no alias window); @object-ui/react stops reading them; @object-ui/sdui-parser stops accepting them as base props; @object-ui/app-shell stops reading an undeclared nav key. No packages/spec key, export or registry moves. The card's order as amended by the seat.

② Semver level

Unchanged since the previous record and re-read at this head: .changeset/25926-retire-node-visibility-dialects.md grades @object-ui/types, @object-ui/react and @object-ui/sdui-parser minor and @object-ui/app-shell patch, carries the BREAKING banner with Before / Now / Fix (FROM to TO rename, value unchanged, fields[].visibleOn named as a different slot) and exactly one ADR-0087 marker, not-required (no-migration-prescription), with the category closure spelled out. Both declaration lines sit on the PR body and the changeset and match the claim as amended: Clause-②: no (narrowing) and Metadata-protocol: narrowed. narrowed takes at least one moved package minor or above, and three of the four moved publishers are minor; app-shell patch is right because what narrows there is a consumer-side read of a key NavigationItem never declared. The fix commit touches a governed skill file and a ui script, neither a published package, so it moves no level. Check Changeset, check-changeset-no-major and check-adr-0087-registration are green on ee8049a. skip-changeset is not used and would be wrong. Clause-②: no (narrowing) is the correct arm.

③ Boundary flags

Round 3 report 6428550273: open_questions empty, out_of_scope_findings empty. The two findings of record 6426970806 are fixed as described and verified above. Earlier flags stand as answered: Q1 keep visible (A), Q2 narrowed (A), Q3 check names nothing itself (A), Q4 three extra docs (A); finding (b) closed in-PR; finding (c) is #26276; round-2 finding (c), the ui/scripts tests outside the derived gate families, stays carried on the seat as a tooling card to file or decline.
PR body, the seat's pen before the maintainer reads it: the Skills readings row for protocol.md still says 377 to 375 lines and 4368 to 4365 tokens (now 374 lines, 4357 tokens); Acceptance note 4 still calls the table-slot refusals out of scope (done in-PR); Verification still carries round-1 readings (dual-build exit 99, 68/66 families, dogfood head 6f044d4) where round 3 has 65/65 gate families green, dual-build green, dogfood at ee8049a 297/2527. The 维护者速读 draft's factual claims hold at this head.
Nothing escalated.

Implemented-by: claude/project-thread-flw1js
Reviewed-by: session_01RyMY3prh9GRva1Tt37Di3i

VERDICT: PASS

@os-zhuang
os-zhuang enabled auto-merge October 26, 2026 22:44
@os-zhuang
os-zhuang added this pull request to the merge queue Oct 26, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Oct 26, 2026
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
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Director seat reading · 2026-10-26T23:04Z · the review label leaves; landing is the owning seat's

Read by the director seat (summon #51, session_01KrKXQVvgYk2AJ4VkqMobyb): os-zhuang APPROVED this Tier H PR (skills/objectui/** in the diff) at 2026-10-26T22:44Z; the eight required contexts are green on head ee8049ac; contract reviews 6426970806 / 6428605336 and the 维护者速读 6428633840 are on the thread. The authorized approval is spent once per PR and does not expire on a later push (AGENTS.md Prime Directive 14), so needs-user-decision is removed in this act. One thing stands between the PR and the queue: GitHub reports mergeable_state: dirty — the branch conflicts with main. The owning seat merges origin/main into the branch (a merge commit, never a rebase or force-push), regenerates whatever the merge touched (scripts/pm/os-regen-merge.sh for generated artifacts; sdui-manifest.sources is in the diff), lets the checks re-run green, then readies and arms auto-merge through the relay (pr_ready → automerge_enable). The click was the maintainer's; the landing is the seat's. No review submitted by this seat.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 195b30322b308d8f1ca1a8fbabb577fb7a74f860
Local-runs: none
Scope: skills/objectui/guides/schema-expressions.md, skills/objectui/rules/protocol.md + scripts/sdui-manifest.sources/components.json, scripts/sdui-manifest.sources/core.json, scripts/sdui-manifest.sources/react.json, and the cited code of record 6428605336 (ui/packages/types base.ts, zod/base.zod.ts, zod/tombstone.zod.ts, zod/objectql.zod.ts and the node-visibility-dialects-retired test; ui/packages/react SchemaRenderer.tsx, utils/propsBagDiagnostic.ts and the propsBagLoudRefusal test; ui/packages/core evaluator/ExpressionEvaluator.ts; ui/packages/sdui-parser validate.ts and retired-visibility-dialects.ts; ui/packages/cli commands/check.ts; ui/scripts/check-skill-examples.mjs; scripts/check-skills-token-ratchet.mjs; the changeset), none of which moved in this hop. The remaining paths are the lane seat's ACCEPT at TIER_DEFAULT.

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

  1. RIGHT, the hop carries only main plus the three re-recorded digests. Read as the net PR diff against the new merge-base 7435353 (20 main commits over 61ba04f): the file list is the identical 61 paths of the ee8049a net diff, and every path outside scripts/sdui-manifest.sources/ has added and removed lines byte-identical to that diff, the two governed skill files included (protocol.md 4357 of 4368 tokens, schema-expressions.md 6571 of 6571, unchanged). The only paths main and this PR both touch are the components.json, core.json and react.json digest records; c74ae05 resolved them to main's side and 195b303 re-recorded them on the merged tree (one sha256 line each). The data-objectstack.json removal and the plugin-grid.json change visible between ee8049a and this head are main's own (refactor(react): AdapterCtx and useAdapter are typed by DataSource; react drops its data-objectstack dependency #26292), carried through the merge and absent from the PR's net diff. sdui.manifest.json is byte-identical to main (empty diff); check-sdui-manifest and check-regen-pending sit inside Lint & Repo Gates.

  2. RIGHT, everything record 6428605336 certified stands: no file in that record's scope moved between ee8049a and 195b303, so the skill sentences (raw-expression form of visibleWhen / hidden / disabled, the by-name refusal through objectui validate, visible ahead of hidden, the props-envelope set of exactly four surviving keys) remain true of this head's code, and the check-skill-examples floor reason remains true. Advisory item on guide line 239 (omits visible) stays NOTED.

② Semver level

Unchanged: @object-ui/types, @object-ui/react, @object-ui/sdui-parser minor, @object-ui/app-shell patch; BREAKING with FROM to TO migration; one ADR-0087 marker not-required (no-migration-prescription); Clause-②: no (narrowing) and Metadata-protocol: narrowed on body and changeset. The hop moves no package and no declaration. Check Changeset is green on this head.

③ Boundary flags

No 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: claude/project-thread-flw1js
Reviewed-by: session_01RyMY3prh9GRva1Tt37Di3i

VERDICT: PASS

@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 26, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Oct 26, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Oct 26, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Oct 26, 2026
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
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 26, 2026 23:35
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Seat note · domain:spec seat 2 · session session_01RyMY3prh9GRva1Tt37Di3i · 2026-10-26T23:35Z

Merge-forward after the second queue ejection (MERGE_CONFLICT on scripts/sdui-manifest.sources/types.json). New head 4be7b842c: merge 2f3f538 of main, with main's side of the record, then a re-record.

Seat's reading, git read commands only:

  • Against its merge-base with main, the PR's net diff is still 61 files, +870/−443.
  • Outside scripts/sdui-manifest.sources/, every added and removed line is identical to the net diff of 195b3032 judged in the PASS record 6429685531. That includes both governed skill files.
  • The five digest records (components, core, react, sdui-parser, types) each move one sha line, as on every earlier hop. check-sdui-manifest and check-regen-pending exit 0 per the dev.

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 CONTRACT_REVIEW_TIER record on this head could not be produced now because the reviewer tier's usage limit was reached. This note records the hop instead. Auto-merge is re-armed and holds for green CI.

@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 26, 2026
Merged via the queue into main with commit e843b5b Oct 27, 2026
61 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-flw1js branch October 27, 2026 00:07
os-tesla pushed a commit that referenced this pull request Oct 27, 2026
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
os-tesla pushed a commit that referenced this pull request Oct 27, 2026
#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>
os-tesla pushed a commit that referenced this pull request Oct 27, 2026
…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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/xl tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

types,react,skills,docs: retire the node-level visibility dialects visibleOn / hiddenOn / disabledOn / visible and teach only visibleWhen

3 participants