Skip to content

feat(spec): conditional tabs — page:tabs items accept a visibleWhen CEL predicate (#2606) - #2967

Merged
os-zhuang merged 2 commits into
mainfrom
claude/page-tabs-visibility-cel-0cazrj
Jul 15, 2026
Merged

feat(spec): conditional tabs — page:tabs items accept a visibleWhen CEL predicate (#2606)#2967
os-zhuang merged 2 commits into
mainfrom
claude/page-tabs-visibility-cel-0cazrj

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

背景:基于 ADR-0089 对 #2606 的重新评估

#2606(2026-07-05)提出给 page:tabs 的 item 加 visibility(CEL)。该提案早于 ADR-0089(2026-07-14 接受),重新评估结论:

  • 提案本身依然成立:Tier 2(page 层)能力、复用 ExpressionInputSchema(CEL)、不进对象层、加性可选字段 → minor。这些设计边界与 ADR-0085 / 详情页相关子表:降低对自定义 page 的依赖(relatedList 三态 + 列派生 + 确定性排序) #2579 一致,不受 ADR-0089 影响。
  • 唯一需要修正的是字段名:ADR-0089 D1 已把条件显隐谓词统一为规范名 visibleWhen,visibility / visibleOn 降级为 @deprecated alias(仅为存量元数据保留)。因此本字段从第一天起就叫 visibleWhen
  • 新表面不接受 alias:ADR-0089 D2 的 alias 机制服务于已有键的存量元数据;tab item 此前没有任何 visibility 键,不存在需要兼容的存量 → 只声明规范键,不加 visibility/visibleOn alias(Prime Directive Add comprehensive test suite for Zod schema validation #12:一个严格契约优于 N 种方言)。测试中显式固化了这一点。

改动

PageTabsProps.items[] 新增可选 visibleWhen: ExpressionInputSchema:

  • 语义:谓词求值为 FALSE → 整条 tab(头 + 面板)从 tab 条移除;区别于子组件自身的 visibleWhen(只隐藏面板内容、留下空 tab 头)。
  • 求值环境:与页面组件级 visibleWhen 一致 —— record + current_user,外加 page.<var>(page 变量变化时响应式重算)。
  • 加性、向后兼容:不带 visibleWhen 的 item 行为完全不变。

测试

  • 裸 CEL 字符串 → 规范化为 { dialect: 'cel', source } 信封;信封形式直接接受。
  • 不带谓词的 item 不受影响。
  • visibility alias 在 tab item 上被折叠(新表面只认规范键)。
  • spec 全量:251 files / 6790 tests 通过;@objectstack/spec build(含 DTS)通过。

配套

渲染器落地在 objectui 同名分支 PR(item 过滤 + 激活 tab 被隐藏时回落到第一个可见 tab)。

Closes#2606. Refs #2579, ADR-0089.

🤖 Generated with Claude Code

https://claude.ai/code/session_01FmuuXkuW3JH9LYXHHRnoQV


Generated by Claude Code

…EL predicate (#2606)
Re-evaluated #2606 under ADR-0089: the proposal predates the ADR, which made
visibleWhen the single canonical conditional-visibility key and demoted
visibility/visibleOn to deprecated aliases. The new item-level key therefore
ships as visibleWhen from day one; the aliases are NOT accepted on this new
surface (no legacy metadata to alias for — Prime Directive #12, one strict
contract over N dialects).
Semantics: when the predicate evaluates FALSE the whole tab (header + panel)
is omitted, unlike a child component's own visibleWhen which hides only the
panel content and leaves an empty tab header. Binds the same environment as
page-component visibleWhen: record + current_user + page.<var>.
Additive, back-compatible (optional field) → minor. Renderer lands in
objectui (item filtering + active-tab fallback to the first visible tab).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FmuuXkuW3JH9LYXHHRnoQV
@vercel

vercelBot commented Jul 15, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
specReadyReadyPreview, CommentJul 15, 2026 3:49pm

Request Review

@github-actions

github-actionsBot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/dogfood, @objectstack/spec.

98 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/agents.mdx(via @objectstack/spec)
  • content/docs/ai/skills-reference.mdx(via @objectstack/spec)
  • content/docs/ai/skills.mdx(via @objectstack/spec)
  • content/docs/api/client-sdk.mdx(via @objectstack/spec)
  • content/docs/api/environment-routing.mdx(via @objectstack/spec)
  • content/docs/api/error-catalog.mdx(via @objectstack/spec)
  • content/docs/api/error-handling-client.mdx(via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx(via @objectstack/spec)
  • content/docs/api/index.mdx(via @objectstack/spec)
  • content/docs/automation/approvals.mdx(via packages/spec)
  • content/docs/automation/flows.mdx(via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx(via packages/spec)
  • content/docs/automation/hooks.mdx(via @objectstack/spec)
  • content/docs/automation/index.mdx(via @objectstack/spec)
  • content/docs/automation/webhooks.mdx(via @objectstack/spec)
  • content/docs/automation/workflows.mdx(via @objectstack/spec)
  • content/docs/concepts/architecture.mdx(via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx(via packages/spec)
  • content/docs/concepts/index.mdx(via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx(via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx(via packages/spec)
  • content/docs/concepts/north-star.mdx(via packages/spec)
  • content/docs/data-modeling/analytics.mdx(via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx(via @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx(via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx(via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx(via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx(via @objectstack/spec)
  • content/docs/data-modeling/index.mdx(via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx(via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx(via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx(via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx(via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx(via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx(via @objectstack/spec)
  • content/docs/deployment/troubleshooting.mdx(via @objectstack/spec)
  • content/docs/getting-started/build-with-claude-code.mdx(via @objectstack/spec)
  • content/docs/getting-started/cli.mdx(via @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx(via @objectstack/spec)
  • content/docs/getting-started/examples.mdx(via @objectstack/spec)
  • content/docs/getting-started/quick-reference.mdx(via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx(via @objectstack/spec)
  • content/docs/getting-started/validating-metadata.mdx(via @objectstack/spec)
  • content/docs/getting-started/your-first-project.mdx(via @objectstack/spec)
  • content/docs/kernel/cluster.mdx(via @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx(via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx(via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx(via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx(via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx(via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx(via packages/spec)
  • content/docs/kernel/index.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/email-service.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/index.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/sharing-service.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx(via packages/spec)
  • content/docs/kernel/services-checklist.mdx(via @objectstack/spec)
  • content/docs/permissions/authorization.mdx(via packages/dogfood, @objectstack/spec)
  • content/docs/permissions/delegated-administration.mdx(via packages/dogfood)
  • content/docs/permissions/permission-sets.mdx(via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx(via @objectstack/spec)
  • content/docs/permissions/positions.mdx(via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx(via @objectstack/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx(via @objectstack/spec)
  • content/docs/plugins/development.mdx(via @objectstack/spec)
  • content/docs/plugins/index.mdx(via @objectstack/spec)
  • content/docs/plugins/packages.mdx(via @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx(via @objectstack/spec)
  • content/docs/protocol/diagram.mdx(via packages/spec)
  • content/docs/protocol/kernel/config-resolution.mdx(via @objectstack/spec)
  • content/docs/protocol/kernel/i18n-standard.mdx(via @objectstack/spec)
  • content/docs/protocol/kernel/lifecycle.mdx(via @objectstack/spec)
  • content/docs/protocol/kernel/plugin-spec.mdx(via @objectstack/spec)
  • content/docs/protocol/kernel/runtime-capabilities.mdx(via @objectstack/spec)
  • content/docs/protocol/knowledge.mdx(via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx(via packages/spec)
  • content/docs/protocol/objectql/query-syntax.mdx(via @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx(via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx(via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/record-alert.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx(via @objectstack/spec)
  • content/docs/releases/implementation-status.mdx(via @objectstack/spec)
  • content/docs/releases/index.mdx(via @objectstack/spec)
  • content/docs/releases/v12.mdx(via @objectstack/spec)
  • content/docs/releases/v13.mdx(via @objectstack/spec)
  • content/docs/releases/v9.mdx(via @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx(via @objectstack/spec)
  • content/docs/ui/dashboards.mdx(via @objectstack/spec)
  • content/docs/ui/forms.mdx(via @objectstack/spec)
  • content/docs/ui/index.mdx(via @objectstack/spec)
  • content/docs/ui/setup-app.mdx(via @objectstack/spec)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

…surface ledger (#2606)
The ADR-0058 D7 / ADR-0060 conformance ratchet re-discovers every
ExpressionInputSchema field in packages/spec/src and fails the build if any is
unclassified. The new tab-item visibleWhen surface (ui/component.zod.ts) needs
a ledger row: it belongs to cel-ui (interpret, fail-soft-log) alongside the
page/view visibility predicates — evaluated by the objectui page:tabs renderer.
Fixes the Test Core / Dogfood Regression Gate failures on this branch.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FmuuXkuW3JH9LYXHHRnoQV
@os-zhuang
os-zhuang marked this pull request as ready for review July 15, 2026 14:56
@os-zhuang
os-zhuang merged commit c64ee8c into mainJul 15, 2026
16 of 17 checks passed
@os-zhuang
os-zhuang deleted the claude/page-tabs-visibility-cel-0cazrj branch July 15, 2026 14:56
os-zhuang added a commit that referenced this pull request Jul 16, 2026
…estore PageTabsProps (#2978) (#3012)
* fix(spec): stop gen:schema silently unpublishing transform-bearing schemas (#2978)
PageTabsProps vanished from json-schema/ when #2967 added an
ExpressionInputSchema (.transform) field — zod's toJSONSchema cannot
represent transforms in the default output mode, and build-schemas.ts
silently skipped it, so the next gen:docs run would have deleted the
published PageTabsProps reference section.
Two-part fix:
1. io:'input' fallback — when output-mode conversion fails on a
transform, retry with io:'input'. These JSON Schemas describe what
authors WRITE, and the input side of a transform pipe is plain data,
so it is representable (for PageTabsProps.visibleWhen it emits the
correct `anyOf: [string, expression envelope]` authoring shape).
Rescued schemas are marked `x-io: "input"`. This restores
PageTabsProps and 149 other transform-blocked public contracts
(ObjectSchema, FieldSchema, FlowSchema, PageSchema, ActionSchema, …);
only 18 truly unrepresentable schemas (function/Date/BigInt/custom)
remain skipped.
2. Disappearance ratchet — json-schema/ is gitignored, so the committed
json-schema.manifest.json records every schema key ever emitted.
A key present in the manifest but absent from a build now fails
gen:schema loudly with remediation steps; deliberate retirements must
remove the key in the same PR. Silent skip remains only for types
that have never been representable.
Also escape literal `|` in the description cell of generated property
tables (build-docs.ts) — rescued schemas surfaced descriptions with
pipes that split GFM table rows.
Closes#2978
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fn6qMKtJeVbs2KzouWDHhB
* docs(references): regen from restored JSON Schemas (#2978)
gen:docs over the post-fix json-schema/ output. PageTabsProps keeps its
section (now including the visibleWhen items shape from #2967), and the
149 schemas rescued by the io:'input' fallback gain reference sections —
previously delivered-but-undeclared contracts (Prime Directive #10).
Existing table rows with literal pipes in descriptions are re-emitted
with GFM escaping.
Verified: `pnpm docs:build` compiles all regenerated MDX.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fn6qMKtJeVbs2KzouWDHhB
* fix(spec): address CodeQL alerts in gen:schema/gen:docs scripts (#3012)
- build-schemas.ts: read the ratchet manifest directly and treat ENOENT
as first-run bootstrap instead of existsSync-then-read (TOCTOU).
- build-docs.ts: escape backslashes before pipes in table-cell
descriptions — an existing `\|` would otherwise decay into an escaped
backslash followed by a live pipe, splitting the GFM cell.
No output changes: regenerated json-schema/ and references/ are
byte-identical.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fn6qMKtJeVbs2KzouWDHhB
---------
Co-authored-by: Claude <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationprotocol:uisize/steststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

条件 tab:给 page:tabs 的 item 加 visibility(CEL)—— #2579 follow-up

2 participants

@os-zhuang@claude