@@ -289,22 +289,33 @@ export type FilterCondition = {
289289 * Directive #10, and this sentence is kept as the record that the tracking
290290 * worked.
291291 *
292+ * ## Empty combinators are boolean identities (#5322, maintainer ruling 2026-08-04)
293+ *
294+ * `{ $and: [] }` is TRUE — the AND identity, no constraint. `{ $or: [] }` is
295+ * FALSE — the OR identity, zero rows. A `{}` disjunct is TRUE and ABSORBS its
296+ * `$or`; `{ $not: {} }` is `NOT TRUE` — FALSE. The ruling took the reduction
297+ * over the analytics compilers' fail-closed throw for two reasons: only a
298+ * reduction can evaluate a NESTED tree (a rejection must first reduce to
299+ * judge an empty combinator sitting inside a `$or` branch, which concedes the
300+ * point), and `{ $or: [] }` = zero rows is fail-closed exactly where it
301+ * matters — an RLS scope whose disjunct list loops to zero items hides every
302+ * row instead of exposing the table (#5134). An earlier revision of this
303+ * paragraph kept the identities OUT of the contract because two compilers
304+ * still refused them; that gap closed with PR #5365 (both
305+ * `service-analytics` compilers reduce, and the four cases are enrolled in
306+ * `filter-logic-conformance.ts` against every backend — the five drivers
307+ * already reduced: `driver-sql` #5243, `driver-mongodb` #5323). Loud
308+ * AUTHORING-time rejection of the literal spellings is a separate, optional
309+ * lint concern (#5330), not a runtime semantic.
310+ *
292311 * ## Deliberately NOT declared here
293312 *
294- * The boolean identities of the EMPTY combinators (`{ $and: [] }` = TRUE,
295- * `{ $or: [] }` = FALSE, `{ $not: {} }` = FALSE) are RULED — #5322
296- * (maintainer, 2026-08-04) took the identity over the analytics compilers'
297- * fail-closed throw — but not yet stated here as contract: on main today
298- * `read-scope-sql` and `filter-normalizer` still refuse an empty `$and`/`$or`,
299- * and the ruling's implementation PR #5365 (aligns both compilers, enrolls the
300- * four cases in `FILTER_LOGIC_CASES`) is sequenced to land after this one. The
301- * declaration flips to stated contract with that PR, not here — declaring it
302- * first would out-run enforcement. Likewise `{ field: {} }` (a field
303- * constrained by zero operators): #5240 ruled it REJECTED and #5327 gated
304- * driver-sql / driver-sqlite-wasm / driver-memory / formula; `driver-mongodb`
305- * still answers it (tracked by #5376), and the schema-side narrowing stays
306- * with the spec lane. Declaring either before it is enforced everywhere would
307- * be exactly the `declared ≠ enforced` shape this file exists to prevent.
313+ * `{ field: {} }` (a field constrained by zero operators): #5240 ruled it
314+ * REJECTED and #5327 gated driver-sql / driver-sqlite-wasm / driver-memory /
315+ * formula; `driver-mongodb` still answers it (tracked by #5376), and the
316+ * schema-side narrowing stays with the spec lane. Declaring it before it is
317+ * enforced everywhere would be exactly the `declared ≠ enforced` shape this
318+ * file exists to prevent.
308319 */
309320export const FilterConditionSchema : z . ZodType < FilterCondition , FilterCondition > = z . lazy ( ( ) =>
310321 z . record ( z . string ( ) , z . unknown ( ) ) . and (
0 commit comments