feat(spec)!: strict view/page schemas — mis-layered visibility keys are loud errors (ADR-0089 D3a)#2943
Merged
Merged
Conversation
…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
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
📓 Docs Drift CheckThis PR changes 2 package(s): 102 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements ADR-0089 D3a (#2902) — the final enforcement leg of the visibility-predicate unification.
What & why
FormFieldSchema,FormSectionSchema(view.zod.ts) andPageComponentSchema(page.zod.ts) run zod's default strip mode, so a key they don't declare — avisibleWhentypo, a page-onlyvisibilitypasted 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 existingnormalizeVisibleWhentransform).strictVisibilityError— a new zod error map (shared/visibility) wired as each schema'serror. It names the offending key(s) and, when a key looks like the conditional-visibility predicate, points the author at the canonicalvisibleWhen(notingvisibleOn/visibilityremain accepted aliases). Mirrors theUNKNOWN_KEY_GUIDANCEtombstone precedent inobject.zod.ts.visibleOn/visibilityaliases are declared keys, so.strict()keeps accepting them and they still normalize tovisibleWhen(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:
objectqlprotocol-meta.test.tsfixturelead_form_onlyused{ id, title }on a form section instead of the canonical{ name, label }— those keys were being silently stripped (the test only assertedsuccess). Fixed toname/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 toobjectstack.jsoncleanly.Tests
New
ADR-0089 D3adescribe blocks inview.test.tsandpage.test.ts: strict rejects an unknown key; a visibility-ish typo is rejected with a message pointing atvisibleWhen; a non-visibility unknown key is rejected without the hint; the deprecated aliases still parse; cross-layer aliases (visibilityon a view field,visibleOnon a page component) are rejected. Full spec suite green (6787 passed);tsc --noEmitclean.Release
Ships as a major —
@objectstack/spec15.0.0 (MAJOR changeset included). ADR-0089 status flipped to reflect D3a done;layout-dsl.mdxdocuments 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-compiledobjectstack.jsonartifact (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