Skip to content

fix(spec): alias visible/showWhen onto visibleWhen on page:tabs items and screen fields - #8415

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-8382-tabs-screenfield-visible-aliases
Aug 13, 2026
Merged

fix(spec): alias visible/showWhen onto visibleWhen on page:tabs items and screen fields#8415
os-zhuang merged 1 commit into
mainfrom
claude/issue-8382-tabs-screenfield-visible-aliases

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Fixes#8382

What changed

#7832 / PR #7884 curated the visible / showWhen action-side spellings onto
visibleWhen across six shapes, pinned in
packages/spec/src/shared/visible-when-alias-guidance.test.ts. Two more
visibleWhen shapes were outside that inventory and still rejected visible
/ showWhen without naming the key to write instead:

  1. packages/spec/src/ui/component.zod.ts — the page:tabsitem
    (surface: 'this \page:tabs` item', visibleWheninPageTabsProps's items` array).
  2. packages/spec/src/automation/builtin-node-config.zod.ts
    ScreenFieldConfigSchema (surface: 'this screen field').

Both have exactly one landing key for the visibility intent and no boolean
sibling, so both get the simple alias treatment per the inventory file's
own rule: visible → visibleWhen, showWhen → visibleWhen.

Before / after

page:tabs item

BEFORE { visible: true }
Unrecognized key(s) on this `page:tabs` item: `visible`. …
AFTER { visible: true }
Unrecognized key(s) on this `page:tabs` item: `visible`. Did you mean
`visible` → `visibleWhen`? …

Same shape for showWhen.

ScreenFieldConfigSchema

BEFORE { name: 'f', visible: true }
Unrecognized key(s) on this screen field: `visible`. …
AFTER { name: 'f', visible: true }
Unrecognized key(s) on this screen field: `visible`. Did you mean
`visible` → `visibleWhen`? …

Same shape for showWhen. The pre-existing visibleIf prescription (an exact
guidance entry, four edits from the right key) is untouched — an exact entry
wins over the alias table, pinned in the new test section.

Acceptance invariance

Every key probed is rejected before this change and rejected after — only the
message changed. Pinned directly:

  • visible.safeParse / showWhen.safeParse on both shapes still fail.
  • The canonical visibleWhen still parses on both shapes, and the parsed
    value is unchanged (page:tabs items route it through
    ExpressionInputSchema, so the pin checks the normalized { dialect: 'cel', source } shape rather than the raw string — that normalization predates
    this change).

Reachability — no guidance set shadows the new rows

Both shapes are hand-rolled strictObject calls with their own options.
Neither spreads VISIBILITY_STRICT_OPTIONS (confirmed by reading both call
sites on origin/main before writing the rows, per the card's instruction —
the tab item carries only its own key → value alias row, the screen field
carries only its visibleIf exact-guidance entry). So no shared guidance set
consumes visible / showWhen ahead of the new alias rows, and
alias-integrity.test.ts's #7889 reachability check stays green — ran it
directly alongside the new pins (see Tests below). The new pins themselves
assert the rename message actually fires (Did you mean …), which is exactly
what a shadowed/dead row would fail to produce.

⛔ Neither row was hoisted onto VISIBILITY_STRICT_OPTIONS — both stay on
their own hand-rolled tables, per the card's red line.

The visibility / visibleOn judgment call (page:tabs item only)

The tab item's visibleWhen docblock states that the deprecated ADR-0089
visibility / visibleOn aliases are not accepted on tab items — unlike
the view/page shapes that fold them into visibleWhen via
normalizeVisibleWhen, this key is new and deliberately carries no legacy
baggage. That sentence is about acceptance; an alias row does not disturb
it.

Decision: yes, both also get a pointer at visibleWhen, alongside visible
/ showWhen.
Reasoning, on the three axes:

  • Real business need. An author who correctly used visibility (page
    component) or visibleOn (view form) under ADR-0089 and reaches for the
    same word on a page:tabs item — a page-level surface, like the component
    the spelling came from — is signalling the identical intent. The docblock
    itself anticipates exactly this confusion, which is why it calls the two
    spellings out by name.
  • Long-term soundness. Same one-landing-key, no-boolean-sibling shape as
    visible / showWhen; the file's own header rule makes this the alias
    case for all four spellings, not just two. Landing all four rows on the
    same shape/decision gives the surface one coherent answer instead of two
    spellings resolved and two left bare for no principled reason.
  • Hard to get wrong. The schema is unchanged — all four spellings still
    fail .strict() exactly as before. Only the message improves, which is
    the AI-authoring-safe direction: loud rejection, now with a fix instead of
    a dead end.

The docblock and the field's .describe() string are both updated so they
say the same thing as the new rows: visible / showWhen / visibility /
visibleOn are all rejected on page:tabs items (none folded in, unlike
view/page surfaces), and each rejection now names visibleWhen. Pinned in the
new test section so a future edit can't silently change either half (drop the
pointer, or silently start accepting one of the four).

ScreenFieldConfigSchema's docblock never made an ADR-0089 claim (it's an
ADR-0032 CEL predicate, not a view/page-family key), so no visibility /
visibleOn pointer was added there — out of the card's stated scope for that
shape and not implied by anything in its existing prose.

Changeset

patch on @objectstack/spec. Measured against the stated criterion (does
the prose reach a consumer — reference page, dist/**/*.d.ts hover, or a
parse-reachable error string): the new "Did you mean" text is a
safeParse/parse error message string, reached the moment an author (human
or AI) writes any of the four spellings — parse-reachable by definition, same
as #7832's own changeset for the identical shape of change.
.changeset/tabs-screenfield-visible-when-aliases.md.

Tests

Added a fifth section to
packages/spec/src/shared/visible-when-alias-guidance.test.ts
(#8382 — the two shapes #7832 never enumerated), following the file's
existing conventions (unknownKeyMessage helper, minimal reach-the-surface
bodies). Per shape:

  • visible / showWhen each produce a "Did you mean" rename onto visibleWhen.
  • The canonical visibleWhen still parses and the parsed value is unchanged.
  • visible / showWhen stay rejected (safeParse().success === false).
  • page:tabs items additionally pin the visibility / visibleOn decision:
    pointed at visibleWhen, still rejected.
  • ScreenFieldConfigSchema additionally pins that the pre-existing
    visibleIf guidance is untouched (guidance wins over aliases).
$ pnpm --filter @objectstack/spec exec vitest run \
src/shared/visible-when-alias-guidance.test.ts src/shared/alias-integrity.test.ts --maxWorkers=2
Test Files 2 passed (2)
Tests 48 passed (48)
$ pnpm --filter @objectstack/spec test
Test Files 390 passed (390)
Tests 10341 passed (10341)
$ pnpm --filter @objectstack/spec typecheck
tsc --noEmit passed
check:scripts-typecheck passed
check:test-typecheck: OK (263 pre-existing debt entries, unchanged)

No exports added or removed, so check:api-surface was not required (the
package-scoped typecheck above already covers the touched surface); ran
typecheck anyway per the gate list.

Gates run

All green:

  • pnpm check:adr-anchors
  • pnpm check:changeset-gate-self-tests
  • pnpm check:cross-package-test-inputs
  • pnpm --filter @objectstack/lint run check:doc-formula-expressions
    (built @objectstack/formula first)
  • pnpm check:docs-audit-scope
  • pnpm check:i18n (built @objectstack/cli and its dependency closure
    first — pnpm --workspace-concurrency=2 --filter '@objectstack/cli^...' build,
    concurrency flag before--filter per the toolchain note)
  • pnpm check:merge-driver
  • pnpm check:release-body
  • pnpm check:spec-parsed-alias
  • pnpm check:type-source-resolution
  • pnpm check:nul-bytes
  • pnpm --filter @objectstack/spec test / typecheck

Not run (repo's own pre-existing red state, not this PR's to fix, per the
dispatch prompt): node scripts/check-dev-prereqs.mjs,
check:objectui-pin-fresh.

Scope fence self-certification

Diff touches exactly:

  • packages/spec/src/ui/component.zod.ts (the named page:tabs item)
  • packages/spec/src/automation/builtin-node-config.zod.ts (the named
    ScreenFieldConfigSchema)
  • packages/spec/src/shared/visible-when-alias-guidance.test.ts (the new
    pin section)
  • .changeset/tabs-screenfield-visible-when-aliases.md

Nothing in data/object.zod.ts, the shared strict-options plumbing, or
VISIBILITY_STRICT_OPTIONS itself — that surface stays #7816's. No accept/
reject behavior changed on either shape (the triage red line): both
implementation passes confirmed this is the message-only alias case, so
nothing was flagged back.

content/docs/releases/ untouched.


Generated by Claude Code

… items and screen fields
#7832 curated the visible/showWhen action-side spellings onto visibleWhen
across six shapes and pinned the inventory in
visible-when-alias-guidance.test.ts. Two more visibleWhen shapes were never
in that inventory: page:tabs items (ui/component.zod.ts) and the automation
screen node's ScreenFieldConfigSchema (builtin-node-config.zod.ts). Both are
hand-rolled strictObject calls with exactly one landing key and no boolean
sibling, so both get the simple alias-row treatment: visible -> visibleWhen,
showWhen -> visibleWhen. Acceptance is unchanged -- an alias row is a message
channel, not a parse-time rename.
The page:tabs item docblock also flags visibility/visibleOn (the ADR-0089
view/page spellings) as deliberately not accepted here; that is an
acceptance statement an alias row does not disturb, so both also get a
pointer at visibleWhen while staying rejected.
Adds a fifth section to visible-when-alias-guidance.test.ts pinning both
shapes' rename messages, canonical-still-parses, aliased-spellings-still-
rejected, and the visibility/visibleOn decision, per Fixes#8382.
Fixes#8382
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016YBUGvukaeVu9DjKdsHJa9
@vercel

vercelBot commented Aug 13, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
objectstackIgnoredIgnoredAug 13, 2026 12:07pm

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec.

106 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 @objectstack/spec)
  • content/docs/automation/connectors.mdx(via @objectstack/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 @objectstack/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/cli.mdx(via @objectstack/spec)
  • content/docs/deployment/tenancy-modes.mdx(via @objectstack/spec)
  • content/docs/deployment/troubleshooting.mdx(via @objectstack/spec)
  • content/docs/deployment/validating-metadata.mdx(via @objectstack/spec)
  • content/docs/getting-started/build-with-claude-code.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/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 @objectstack/spec)
  • content/docs/kernel/index.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/data-service.mdx(via @objectstack/spec)
  • content/docs/kernel/runtime-services/email-service.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/examples.mdx(via @objectstack/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 @objectstack/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx(via @objectstack/spec)
  • content/docs/kernel/services-checklist.mdx(via @objectstack/spec)
  • content/docs/kernel/services.mdx(via @objectstack/spec)
  • content/docs/permissions/authorization.mdx(via @objectstack/spec)
  • 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/rls.mdx(via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx(via @objectstack/spec)
  • content/docs/permissions/system-context.mdx(via packages/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/http-protocol.mdx(via @objectstack/spec)
  • content/docs/protocol/kernel/i18n-standard.mdx(via @objectstack/spec)
  • content/docs/protocol/kernel/index.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/knowledge.mdx(via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx(via @objectstack/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/ui/actions.mdx(via @objectstack/spec)
  • content/docs/ui/apps.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/field-grouping-and-order.mdx(via @objectstack/spec)
  • content/docs/ui/forms.mdx(via @objectstack/spec)
  • content/docs/ui/index.mdx(via @objectstack/spec)
  • content/docs/ui/public-data-collection.mdx(via @objectstack/spec)
  • content/docs/ui/setup-app.mdx(via @objectstack/spec)
  • content/docs/ui/translations.mdx(via @objectstack/spec)
  • content/docs/ui/views.mdx(via @objectstack/spec)

7 release-owned page(s) also reference the affected code. These are read-only:

  • 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/v16.mdx(via @objectstack/spec)
  • content/docs/releases/v17.mdx(via @objectstack/spec)
  • content/docs/releases/v9.mdx(via @objectstack/spec)

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

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.

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation tests protocol:ui tooling labels Aug 13, 2026
@os-zhuang
os-zhuang marked this pull request as ready for review August 13, 2026 12:42
@os-zhuang
os-zhuang added this pull request to the merge queueAug 13, 2026
Merged via the queue into main with commit dd0f681Aug 13, 2026
27 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-8382-tabs-screenfield-visible-aliases branch August 13, 2026 12:58
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationprotocol:uisize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Two visibleWhen shapes were outside #7832's sweep: page:tabs items and ScreenFieldConfig still reject visible / showWhen without naming the key

2 participants

@os-zhuang@claude