Skip to content

fix(spec,drivers): the view filter vocabulary and the AST vocabulary now agree (#3948) - #4039

Merged
os-zhuang merged 1 commit into
mainfrom
fix/3948-view-ast-vocabulary-parity
Jul 30, 2026
Merged

fix(spec,drivers): the view filter vocabulary and the AST vocabulary now agree (#3948)#4039
os-zhuang merged 1 commit into
mainfrom
fix/3948-view-ast-vocabulary-parity

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

#3948 item 3 — the root cause. Item 1 shipped in #4029 and turned this class of failure from silent into loud; this removes the cause.

The disagreement

VIEW_FILTER_OPERATORS (ui/view.zod.ts:74) is what an author may declare on a ViewFilterRule, and what ViewFilterRuleSchema validates against. VALID_AST_OPERATORS (data/filter.zod.ts) gates isFilterAST(), which decides whether a filter is parsed into a query at all.

They disagreed on 8 of 19 members:

equals   not_equals   greater_than   less_than
greater_than_or_equal   less_than_or_equal   before   after

equals is on that list. An author could declare any of them, the schema validated them, defineStack accepted them — and then isFilterAST() refused the filter, the protocol passed the array through unconverted, and the driver could not apply it.

Six of the eight were reachable only in theory, because ObjectUI's adapter alias table happened to translate them. The correctness of the query path was resting on a hand-written table in a different repository being complete, and for before/after it wasn't — which is how this surfaced (objectstack-ai/objectui#2974).

What changed

AST_OPERATOR_MAP is now the single source of truth. VALID_AST_OPERATORS is derived from its keys rather than restated:

export const VALID_AST_OPERATORS = new Set(Object.keys(AST_OPERATOR_MAP));

They were two independent hand-written lists that happened to agree, with nothing enforcing it — and the two failure directions are both invisible: a name in the Set with no lowering hits convertComparison's $${op} fallback and reaches the driver as an unknown $-operator; a name in the Map but not the Set makes isFilterAST() refuse the filter entirely. (Verified they were byte-identical before this change, so the derivation is behaviour-preserving.)

The map gained the eight canonical view spellings plus the squashed/short forms stored metadata carries (notequals, greaterthanorequal, eq, gt, …) — saveMeta persists the authored body verbatim, so those are live in rows, not historical.

New export canonicalAstOperator(op) folds every accepted spelling of one comparison onto a single infix form. Both drivers now call it instead of growing private alias lists — which is precisely what let them accept different vocabularies before #4029.

like/ilike are deliberately not folded onto contains: driver-sql passes them to SQL verbatim, so folding would silently wrap the value in %…% and change what the query means.

Widening only

No spelling was removed, so nothing that validated before stops validating, and there is no stored-data migration. A filter that previously threw (after #4029) or was silently dropped (before it) now compiles.

Verification

Suite Result
@objectstack/spec 6922 pass across 266 files
objectql 1171 pass across 80 files
driver-sql 487 pass
driver-memory 178 pass (was 153 — the gate grew and its parity test follows it)
check:api-surface / check:authorable-surface / check:spec-changes pass

The api-surface ratchet caught the new export exactly as designed (0 breaking, 1 added); regenerated and committed.

The new parity test was confirmed to fail without the fix — removing before/after from the map produces 5 failures naming them specifically.

A note on how this was found

driver-memory's parity test from #4029 iterates VALID_AST_OPERATORS directly, so widening the gate immediately failed it — the drivers' array paths lagged the gate by 20 spellings. That is the test doing its job, and it is why canonicalAstOperator exists rather than two more hand-written alias lists. driver-sql had the identical gap and no test to catch it.

…now agree (#3948)

`VIEW_FILTER_OPERATORS` (`ui/view.zod.ts`) is what an author may declare on a
`ViewFilterRule`; `VALID_AST_OPERATORS` (`data/filter.zod.ts`) gates
`isFilterAST()`, which decides whether a filter is parsed into a query at all.
They disagreed on **8 of 19** members — `equals`, `not_equals`, `greater_than`,
`less_than`, `greater_than_or_equal`, `less_than_or_equal`, `before`, `after`.

An author could declare any of them, `ViewFilterRuleSchema` validated them and
`defineStack` accepted them; then `isFilterAST()` refused the filter, the protocol
passed the array through unconverted, and the driver could not apply it. Six of
the eight were reachable only in theory because ObjectUI's adapter alias table
happened to translate them — so the query path's correctness was resting on a
hand-written table in another repository being complete, and for `before`/`after`
it wasn't.

`AST_OPERATOR_MAP` becomes the single source of truth: `VALID_AST_OPERATORS` is
derived from its keys instead of restated, so an operator can no longer pass the
gate without having a lowering. The two were independent hand-written lists that
happened to agree, with nothing enforcing it. The map gained the eight canonical
view spellings plus the squashed/short forms stored metadata carries.

New export `canonicalAstOperator(op)` folds every accepted spelling of one
comparison onto a single infix form; both drivers call it rather than growing
private alias lists, which is what let them accept different vocabularies.
`like`/`ilike` are deliberately NOT folded onto `contains` — driver-sql passes
them to SQL verbatim, so folding would silently wrap the value in `%…%`.

Widening only; no spelling was removed, so nothing stops validating. Regenerated
api-surface.json (0 breaking, 1 added — the ratchet caught it).

Tests: spec 6922, objectql 1171, driver-sql 487, driver-memory 178. The new
parity test was confirmed to fail without the fix (5 failures naming
`before`/`after`).

Refs #3948 item 3

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

vercel Bot commented Jul 30, 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 Jul 30, 2026 5:31am

Request Review

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

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 3 package(s): @objectstack/driver-memory, @objectstack/driver-sql, @objectstack/spec.

109 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 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/driver-memory, @objectstack/driver-sql, @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/troubleshooting.mdx (via @objectstack/spec)
  • content/docs/deployment/validating-metadata.mdx (via @objectstack/spec)
  • content/docs/deployment/vercel.mdx (via @objectstack/driver-memory)
  • 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/glossary.mdx (via @objectstack/driver-memory, @objectstack/driver-sql)
  • 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/driver-memory, @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/driver-memory, @objectstack/spec)
  • content/docs/permissions/authentication.mdx (via @objectstack/driver-memory)
  • 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/plugins/adding-a-metadata-type.mdx (via @objectstack/spec)
  • content/docs/plugins/anatomy.mdx (via @objectstack/driver-sql)
  • content/docs/plugins/development.mdx (via @objectstack/spec)
  • content/docs/plugins/index.mdx (via @objectstack/driver-memory, @objectstack/spec)
  • content/docs/plugins/packages.mdx (via @objectstack/driver-memory, @objectstack/driver-sql, @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/i18n-standard.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/driver-sql, @objectstack/spec)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/driver-sql, @objectstack/spec)
  • content/docs/protocol/kernel/plugin-spec.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/runtime-capabilities.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/driver-memory, @objectstack/driver-sql, @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/releases/implementation-status.mdx (via @objectstack/driver-memory, @objectstack/driver-sql, @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/ui/actions.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/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)

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.

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:data size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant