Skip to content

feat(spec,lint): reject userFilters on object list views (ADR-0053 phase 4)#2583

Merged
os-zhuang merged 2 commits into
mainfrom
feat/adr-0053-list-view-mode-guardrail
Jul 4, 2026
Merged

feat(spec,lint): reject userFilters on object list views (ADR-0053 phase 4)#2583
os-zhuang merged 2 commits into
mainfrom
feat/adr-0053-list-view-mode-guardrail

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Lands the ADR-0053 phase-4 guardrail: userFilters / quickFilters are reserved for page lists (InterfaceListPage, "filters" mode). On an object list view ("views" mode — where the ViewTabBar is the only nav control) they were silently dropped at every layer. The runtime suppression is correct, but the schema still accepted the field, so an author (human or AI) got a valid schema, a passing build, and a toolbar with nothing where the filter controls should be.

This is the framework/schema half of the fix. objectui #2219 tracks the issue; objectui #2220 shipped the interim runtime console.warn that this guardrail supersedes.

Design — layered defence (no runtime break)

The hard constraint from ADR-0053 is no mode discriminator: the mode is decided by context (object list → views; page list → filters), so a single schema can't tell which context it's in. The fix splits across three layers:

Layer Mechanism Effect
Type (author time) ObjectListViewSchema = ListViewSchema.omit({ userFilters }); object built-in listViews + defineView list/listViews use it userFilters on an object list view is a tsc error — the primary defence for AI-authored .ts
Runtime (back-compat) default strip (no .strict), so a stray userFilters is dropped at parse ObjectSchema.parse never throws — existing metadata keeps loading, no line-side break
Author / CI new @objectstack/lint validateListViewMode, wired into os validate reports the wrong-context field PRE-parse (before the schema strips it) with a fix hint, for the AI self-correction loop

The full ListViewSchema used by page lists ("filters" mode) is untouched — page userFilters stays valid.

Why lint runs pre-parse

ObjectStackDefinitionSchema.objects deep-parses through ObjectSchema, so once the schema strips userFilters, a post-parse stack no longer has it. The rule therefore runs on the normalizeStackInput output (pre-parse), where the field is still present. Confirmed normalizeStackInput preserves it.

Changes

  • spec/src/ui/view.zod.ts — new ObjectListViewSchema; ViewSchema.list / listViews use it.
  • spec/src/data/object.zod.ts — object built-in listViews uses it.
  • lint/src/validate-list-view-mode.ts (+ export) — new rule, scans objects[*].listViews, views[*].list, views[*].listViews.
  • cli/src/commands/validate.ts — wires the rule into os validate (on normalized, pre-parse).

Testing

  • spec: 6673 tests green (incl. 4 new — strip-not-throw, shape omits userFilters, page schema still accepts it). Zero regressions, so nothing depended on object listViews keeping userFilters.
  • lint: 122 tests green (incl. 6 new — object/view context hits, quickFilters, array + name-keyed-map forms, clean stack stays silent).
  • build: turbo run build --filter=@objectstack/cli... — 54 pkgs, tsc typecheck green.
  • e2e: normalizeStackInputvalidateListViewMode catches both array (objects) and map (views) forms.

Rollout

Needs a @objectstack/spec release before objectui can bump and consume the tightened schema; objectui's runtime warn (#2220) already covers the gap in the meantime. objectui's hand-written mirror (packages/types) is intentionally not switched to import spec here — the two ListViewSchema copies have drifted and that's a separate migration.

🤖 Generated with Claude Code

…ase 4)

ADR-0053 reserves userFilters/quickFilters for page lists ("filters" mode); on
an object list view ("views" mode, where the ViewTabBar is the only nav
control) they are silently dropped. Land the phase-4 guardrail as a layered
defence so the wrong-context authoring mistake is caught without breaking
existing metadata:

- Type (author time): new ObjectListViewSchema = ListViewSchema minus
  userFilters. Object built-in listViews + defineView list/listViews use it, so
  userFilters on an object list view is a tsc error. Full ListViewSchema (page
  filters mode) untouched.
- Runtime (back-compat): field STRIPPED at parse (default strip, no throw) —
  existing metadata keeps loading; ObjectSchema.parse never fails on a stray
  userFilters.
- Author/CI (actionable): new @objectstack/lint validateListViewMode, wired
  into `os validate`, reports the wrong-context field PRE-parse (before the
  schema strips it) with a fix hint.

Verified: spec 6673 tests + lint 122 tests green, turbo build (54 pkgs, tsc
typecheck) green, e2e normalize+lint chain catches both array/map forms.

Closes the schema half of objectui #2219; supersedes the interim runtime warn
in objectui #2220.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 4, 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 4, 2026 3:43pm

Request Review

@github-actions

github-actions Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 3 package(s): @objectstack/cli, @objectstack/lint, @objectstack/spec.

93 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 packages/cli, @objectstack/spec)
  • content/docs/ai/skills.mdx (via @objectstack/spec)
  • content/docs/api/client-sdk.mdx (via @objectstack/cli, @objectstack/spec)
  • content/docs/api/environment-routing.mdx (via @objectstack/cli, @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/cli, packages/spec)
  • content/docs/automation/hooks.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/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/cloud-artifact-api.mdx (via packages/cli, packages/spec)
  • content/docs/deployment/troubleshooting.mdx (via @objectstack/spec)
  • content/docs/getting-started/cli.mdx (via @objectstack/cli, @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/cli, @objectstack/spec)
  • content/docs/getting-started/validating-metadata.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/runtime-services/data-service.mdx (via packages/cli)
  • content/docs/kernel/runtime-services/email-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/index.mdx (via packages/cli, 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/storage-service.mdx (via packages/spec)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/spec)
  • content/docs/permissions/authentication.mdx (via @objectstack/cli)
  • content/docs/permissions/authorization.mdx (via packages/spec)
  • content/docs/permissions/permission-sets.mdx (via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx (via @objectstack/spec)
  • content/docs/permissions/profiles.mdx (via @objectstack/spec)
  • content/docs/permissions/roles.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/cli, @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/lifecycle.mdx (via @objectstack/spec)
  • content/docs/protocol/objectos/plugin-spec.mdx (via @objectstack/cli, @objectstack/spec)
  • content/docs/protocol/objectos/realtime-protocol.mdx (via @objectstack/cli)
  • 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/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 packages/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/cli, @objectstack/spec)
  • content/docs/releases/index.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/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.

New export from the ADR-0053 phase-4 guardrail; the api-surface check flagged it
as 1 added / 0 breaking. Regenerated the snapshot via
`pnpm --filter @objectstack/spec gen:api-surface`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@os-zhuang
os-zhuang merged commit e695fe0 into main Jul 4, 2026
17 checks passed
@os-zhuang
os-zhuang deleted the feat/adr-0053-list-view-mode-guardrail branch July 4, 2026 15:51
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.

1 participant