From 97a808af27354510da7a768f8970aeb5cfccb77b Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 1 Sep 2026 00:29:40 +0000 Subject: [PATCH 1/2] docs(spec): $exists asks for a value, not a present key (#13709) The JSDoc on `SpecialOperatorSchema.$exists` read "Field exists check (primarily for NoSQL)", which describes key-presence. `$exists` stopped meaning key-presence when the has-value alignment landed (PR #13529 / 9dac1ae017); the line was false, not merely stale, and it was the last of six sites still carrying the old claim. Replaced with the wording recorded on #13539 and already shipped on the other five sites, adapted to this file's JSDoc idiom (the sibling `$null` line annotates which boolean maps to which lowering, so this one does too). Direction verified against all three evaluators before rewriting. Prose only: `$exists` is `z.boolean().optional()` before and after, and the comment is not a `.describe()`, so no generated artifact moves. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01Mciyv38maJ6HYVMiaM26T1 --- .changeset/spec-exists-jsdoc-has-value.md | 25 +++++++++++++++++++++++ packages/spec/src/data/filter.zod.ts | 6 +++++- 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 .changeset/spec-exists-jsdoc-has-value.md diff --git a/.changeset/spec-exists-jsdoc-has-value.md b/.changeset/spec-exists-jsdoc-has-value.md new file mode 100644 index 0000000000..5046c0e8d7 --- /dev/null +++ b/.changeset/spec-exists-jsdoc-has-value.md @@ -0,0 +1,25 @@ +--- +'@objectstack/spec': patch +--- + +docs(spec): `$exists` JSDoc said key-presence, which has been false since protocol alignment (#13709) + +`SpecialOperatorSchema.$exists` carried `Field exists check (primarily for NoSQL) - +MongoDB: $exists`. That describes key-presence, and key-presence stopped being what +`$exists` means when the has-value alignment landed (PR #13529 / `9dac1ae017`). The +line was not stale phrasing — it was false, and it is the last of the six sites that +carried the old claim; the other five were corrected by PR #13581 / PR #13577. + +The corrected wording is the one recorded on #13539 and already shipped on those five +sites: `$exists` asks whether the field HAS A VALUE (`!= null`), the exact inverse of +`$null`, never key presence — lowered to `IS NOT NULL` / `IS NULL` on SQL and to +`{$ne: null}` / `{$eq: null}` on MongoDB. Verified against all three evaluators before +rewriting: `formula`'s `matchesFilterCondition` (`v === true ? actual != null : actual == null`), +`objectql`'s `having` face, and the `filter-logic-conformance.ts` table, which enrolls +`$exists` in both directions. + +Prose only. No accept/reject change: `$exists` was and stays `z.boolean().optional()`, +and the JSDoc is a comment, not a `.describe()` — the schema, its JSON-Schema +projection and every generated artifact are byte-identical. It reaches consumers as +the hover text on `@objectstack/spec`'s shipped `.d.ts`, which is why this is a patch +rather than no changeset at all. diff --git a/packages/spec/src/data/filter.zod.ts b/packages/spec/src/data/filter.zod.ts index 1f71470c2d..5ff205b277 100644 --- a/packages/spec/src/data/filter.zod.ts +++ b/packages/spec/src/data/filter.zod.ts @@ -994,7 +994,11 @@ export const SpecialOperatorSchema = lazySchema(() => z.object({ /** Is null check - SQL: IS NULL (true) / IS NOT NULL (false) | MongoDB: field: null */ $null: z.boolean().optional(), - /** Field exists check (primarily for NoSQL) - MongoDB: $exists */ + /** + * Field HAS A VALUE (`!= null`) — the inverse of `$null`, never key presence. + * Lowered to `IS NOT NULL` (true) / `IS NULL` (false) on SQL and to + * `{$ne: null}` / `{$eq: null}` on MongoDB. + */ $exists: z.boolean().optional(), })); From a12460d53fbd912c5f28b8955f285566442bec39 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 1 Sep 2026 01:03:01 +0000 Subject: [PATCH 2/2] docs(changeset): state the measured surfaces, not the assumed one (#13709) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The first draft claimed the corrected JSDoc reaches consumers as `.d.ts` hover text. Measured on a freshly built dist: zero `.d.ts`/`.d.mts` carry it (the property's type is inferred from Zod, so no declaration comment is emitted) — it appears in `dist` only inside `.js.map` sourcemaps. The surface that does carry it is published SOURCE: this package's `files` array ships `src/**/*.zod.ts`, so `filter.zod.ts` itself is in the tarball. Recorded alongside the two negative results, since a PR correcting a false claim must not ship one. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01Mciyv38maJ6HYVMiaM26T1 --- .changeset/spec-exists-jsdoc-has-value.md | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/.changeset/spec-exists-jsdoc-has-value.md b/.changeset/spec-exists-jsdoc-has-value.md index 5046c0e8d7..43f2fe4770 100644 --- a/.changeset/spec-exists-jsdoc-has-value.md +++ b/.changeset/spec-exists-jsdoc-has-value.md @@ -20,6 +20,18 @@ rewriting: `formula`'s `matchesFilterCondition` (`v === true ? actual != null : Prose only. No accept/reject change: `$exists` was and stays `z.boolean().optional()`, and the JSDoc is a comment, not a `.describe()` — the schema, its JSON-Schema -projection and every generated artifact are byte-identical. It reaches consumers as -the hover text on `@objectstack/spec`'s shipped `.d.ts`, which is why this is a patch -rather than no changeset at all. +projection and all 15 generated artifacts are byte-identical (`check:generated` green +on a freshly built `dist`). + +Where the corrected line does and does not surface, measured rather than assumed: + +- **Published source — yes.** This package's `files` array ships `src/**/*.zod.ts`, so + `filter.zod.ts` is in the npm tarball and the comment reaches consumers verbatim. + That is the surface this patch is for. +- **Emitted `.d.ts` — no.** Zero of the emitted `.d.ts`/`.d.mts` carry the text; the + property's type is inferred from Zod, so no declaration comment is written. It + appears in `dist` only inside `.js.map` sourcemaps. +- **Generated reference page — no.** `content/docs/references/data/filter.mdx` renders + its Description column from `prop.description`, i.e. a Zod `.describe()`; `$exists` + carries none, so that cell is empty before and after. Regenerating all 230 pages + produces no diff. See #13709 for the follow-up on that empty cell.