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:tabs item
    (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

vercel Bot commented Aug 13, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 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-actions github-actions Bot 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 queue Aug 13, 2026
Merged via the queue into main with commit dd0f681 Aug 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 free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation protocol:ui size/m tests tooling

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