Skip to content

feat(spec)!: strict view/page schemas — mis-layered visibility keys are loud errors (ADR-0089 D3a)#2943

Merged
os-zhuang merged 2 commits into
mainfrom
claude/adr-0089-visibility-schema-q7gz10
Jul 15, 2026
Merged

feat(spec)!: strict view/page schemas — mis-layered visibility keys are loud errors (ADR-0089 D3a)#2943
os-zhuang merged 2 commits into
mainfrom
claude/adr-0089-visibility-schema-q7gz10

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Implements ADR-0089 D3a (#2902) — the final enforcement leg of the visibility-predicate unification.

What & why

FormFieldSchema, FormSectionSchema (view.zod.ts) and PageComponentSchema (page.zod.ts) run zod's default strip mode, so a key they don't declare — a visibleWhen typo, a page-only visibility pasted onto a view field (or vice-versa), or a key surviving past its deprecation window — is silently dropped, shipping inert metadata with no diagnostic. For an AI author that's the worst failure mode: no signal to self-correct from (ADR-0049 enforce-or-remove, ADR-0078 no-silently-inert).

This flips those three schemas to .strict(), so a mis-layered / stale key is a loud parse error.

Changes

  • .strict() on the three view/page schemas (before the existing normalizeVisibleWhen transform).
  • strictVisibilityError — a new zod error map (shared/visibility) wired as each schema's error. It names the offending key(s) and, when a key looks like the conditional-visibility predicate, points the author at the canonical visibleWhen (noting visibleOn / visibility remain accepted aliases). Mirrors the UNKNOWN_KEY_GUIDANCE tombstone precedent in object.zod.ts.
  • Back-compat preserved: the deprecated visibleOn / visibility aliases are declared keys, so .strict() keeps accepting them and they still normalize to visibleWhen (D2 unchanged).

Blast-radius sweep (ADR requires sweep-before-flip)

Built the full monorepo and ran every metadata-parsing consumer suite under strict. The entire framework + examples surfaced exactly one offender:

  • objectql protocol-meta.test.ts fixture lead_form_only used { id, title } on a form section instead of the canonical { name, label } — those keys were being silently stripped (the test only asserted success). Fixed to name/label.

Everything else — app-showcase (4 views / 28 pages), app-crm, platform-objects, metadata-core, rest, cli, metadata-protocol, sdui-parser, mcp, core, dogfood — is green under strict, and the showcase compiles to objectstack.json cleanly.

Tests

New ADR-0089 D3a describe blocks in view.test.ts and page.test.ts: strict rejects an unknown key; a visibility-ish typo is rejected with a message pointing at visibleWhen; a non-visibility unknown key is rejected without the hint; the deprecated aliases still parse; cross-layer aliases (visibility on a view field, visibleOn on a page component) are rejected. Full spec suite green (6787 passed); tsc --noEmit clean.

Release

Ships as a major@objectstack/spec 15.0.0 (MAJOR changeset included). ADR-0089 status flipped to reflect D3a done; layout-dsl.mdx documents the strict behavior.

Follow-up (separate)

The sibling objectui repo consumes @objectstack/spec; it should pick up the 15.0.0 bump on its own release cycle. objectui parses the pre-compiled objectstack.json artifact (already validated) rather than re-parsing through these schemas at runtime, so no break is expected — but the bump should be verified there when it lands.

Parent: #2642 · builds on #2900 (D1/D2/D3b) · sibling #2904 (renderers, merged via objectstack-ai/objectui#2490).

🤖 Generated with Claude Code

https://claude.ai/code/session_01R7oGohmS4pT9H73zoV6Jdb


Generated by Claude Code

…re loud errors (ADR-0089 D3a)

BREAKING CHANGE: FormFieldSchema, FormSectionSchema (view.zod.ts) and
PageComponentSchema (page.zod.ts) are now `.strict()`. A key these schemas
do not declare — a `visibleWhen` typo, a page-only `visibility` on a view
field (or vice-versa), or a stale key past its deprecation window — was
silently dropped by zod's default strip mode, shipping inert metadata with
no diagnostic (ADR-0049 enforce-or-remove, ADR-0078 no-silently-inert). It
is now a loud parse error.

- Deprecated `visibleOn` / `visibility` aliases are declared keys, so they
  keep parsing and normalizing to `visibleWhen` (D2 unchanged).
- New `strictVisibilityError` zod error map (shared/visibility) names the
  offending key(s) and, when one looks like the visibility predicate, points
  the author at the canonical `visibleWhen`.
- Monorepo + examples sweep found one offender: an objectql test fixture
  using `id`/`title` on a form section instead of `name`/`label` — fixed.

Ships as a major (`@objectstack/spec` 15.0.0). Full spec suite green
(6787), plus showcase/crm/platform-objects/objectql/metadata/rest/cli/
dogfood suites green under strict.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R7oGohmS4pT9H73zoV6Jdb
@vercel

vercel Bot commented Jul 15, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
spec Ready Ready Preview, Comment Jul 15, 2026 3:45am

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests protocol:ui tooling size/m labels Jul 15, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

102 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 @objectstack/objectql, 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 packages/objectql, @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/migration-from-objectql.mdx (via @objectstack/objectql)
  • content/docs/deployment/troubleshooting.mdx (via @objectstack/spec)
  • content/docs/deployment/vercel.mdx (via @objectstack/objectql)
  • 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/objectql, @objectstack/spec)
  • content/docs/kernel/services.mdx (via @objectstack/objectql)
  • content/docs/permissions/authentication.mdx (via @objectstack/objectql)
  • 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/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/objectql, @objectstack/spec)
  • content/docs/plugins/packages.mdx (via @objectstack/objectql, @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx (via @objectstack/spec)
  • content/docs/protocol/diagram.mdx (via packages/spec)
  • content/docs/protocol/knowledge.mdx (via @objectstack/spec)
  • content/docs/protocol/objectos/config-resolution.mdx (via @objectstack/spec)
  • content/docs/protocol/objectos/i18n-standard.mdx (via @objectstack/spec)
  • content/docs/protocol/objectos/index.mdx (via @objectstack/objectql)
  • content/docs/protocol/objectos/lifecycle.mdx (via @objectstack/spec)
  • content/docs/protocol/objectos/plugin-spec.mdx (via @objectstack/spec)
  • content/docs/protocol/objectos/runtime-capabilities.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/objectql, @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/objectql, @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/objectql, @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.

Additive-only public API change (0 breaking, 1 added) from the new
`strictVisibilityError` export (ADR-0089 D3a). Regenerated via
`pnpm --filter @objectstack/spec gen:api-surface`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R7oGohmS4pT9H73zoV6Jdb
@os-zhuang os-zhuang added the allow-major label Jul 15, 2026 — with Claude
@os-zhuang os-zhuang marked this pull request as ready for review July 15, 2026 04:47
@os-zhuang os-zhuang merged commit 28b7c28 into main Jul 15, 2026
17 checks passed
@os-zhuang os-zhuang deleted the claude/adr-0089-visibility-schema-q7gz10 branch July 15, 2026 04:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants