Uh oh!
There was an error while loading. Please reload this page.
docs(spec): $exists asks whether a field has a value, not whether a key is present - #14050
Conversation
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 / 9dac1ae); 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 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Mciyv38maJ6HYVMiaM26T1
…3709) 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 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Mciyv38maJ6HYVMiaM26T1
📓 Docs Drift Check1 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to list — not a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run. What this run could not see
Coarse fallback — 128 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 69f83a50c99ba6cb684dd319e96cd442b05ae7c8 && git checkout 69f83a50c99ba6cb684dd319e96cd442b05ae7c8
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 836a29c27aa9dbac27cc643106e5b2bf68cc96c1 a12460d53fbd912c5f28b8955f285566442bec39 && git checkout -B drift-repro 836a29c27aa9dbac27cc643106e5b2bf68cc96c1 && git merge --no-ff a12460d53fbd912c5f28b8955f285566442bec39
node scripts/docs-audit/affected-docs.mjs --json 836a29c27aa9dbac27cc643106e5b2bf68cc96c1 |
Uh oh!
There was an error while loading. Please reload this page.
Fixes#13709
Clause-②: no. Prose only, zero accept/reject change —
$existsisz.boolean().optional()before and after, and the edited text is a JSDoc comment, not a.describe(). The path limb (packages/spec/src/**/*.zod.ts) still binds the enqueue gate, soneeds:contract-reviewis hung on this PR and on the card.The one line
SpecialOperatorSchema.$existscarried:/** Field exists check (primarily for NoSQL) - MongoDB: $exists */That describes key-presence.
$existsstopped meaning key-presence when the has-value alignment landed at9dac1ae017(PR #13529), so the line was false rather than merely stale — the last of the six sites carrying the old claim, the other five having been corrected by PR #13581 and PR #13577.Anchored by the string
primarily for NoSQL, never a line number: the card's own title said:954, its comments said:997, and it sat at:997on my base. The symptom grep returns exactly one hit repo-wide, and zero after this diff.Replaced with the wording recorded on #13539, adapted to this file's JSDoc idiom — the sibling
$nullline annotates which boolean maps to which lowering, so this one does too:Direction verified against the implementations before rewriting, not copied on faith:
formula'smatchesFilterConditionreadsv === true ? actual != null : actual == null;objectql'shavingface lists$existsamong the operators for which "no value" is a real answer; andfilter-logic-conformance.tsrecords the 2026-08-10 ruling with$existsenrolled in both directions.A sub-premise of the card is falsified — measured, not argued
The card and the dispatch both state that
content/docs/references/data/filter.mdxinherits its Description column from this JSDoc, and asked for that page to be regenerated in the same commit. I ran the regeneration (gen:schemathengen:docs, 230 pages). It produced a zero-byte diff.The reason:
packages/spec/scripts/lib/schema-section.tsfills that column fromprop.description, the projection of a Zod.describe()— and$existshas none, so its cell is empty before and after. The two surfaces were therefore never in the disagreement the card describes; the generated page is silent about$exists, not wrong about it.Recorded as #14048 (unassigned, for triage) rather than fixed here: adding
.describe()writes new prose onto a published page under a root watched bycheck:corpus-claim-drift, whose single rule isexists-key-presence— the exact shape that ratchet inspects. That earns its own diff and its own gate run instead of riding along on a fenced one-line card.Where the corrected line actually surfaces
packages/spec'sfilesarray shipssrc/**/*.zod.ts, sofilter.zod.tsis in the npm tarball and the comment reaches consumers verbatim. This is the surface the patch changeset is for..d.ts— no. Zero of the emitted.d.ts/.d.mtscarry the text (the property's type is inferred from Zod, so no declaration comment is written); it appears indistonly inside.js.mapsourcemaps.An earlier draft of the changeset claimed the
.d.tshover surface. That was assumed, not measured; the follow-up commit corrects it, because a PR that exists to remove a false claim must not ship one.Verification
All of the following on the final commit
a12460d5, clean tree, afterpnpm --filter @objectstack/spec build:@objectstack/specsuiteTest Files 446 passed | 1 skipped (447)·Tests 11989 passed | 1 skipped (11990)@objectstack/spectypechecktsc --noEmit,check:scripts-typecheck, andcheck:test-typecheck(test layer really compiled, viatsconfig.test.json)check:generated✓ All 15 generated artifacts are up to date.spec check:docs✅ 230 generated files in sync with packages/speccheck:authorable-surface1237 default(s) unchanged, 1608 schemascheck:corpus-claim-driftOK, no new claim sites beside a pinned spelling.— 226 files, ruleexists-key-presencecheck:doc-authoring14301 customer-facing string(s) across 693 spec sources cleancheck:doc-formula-expressions22 record-scoped formula example(s) across 427 files / 1451 TS blocks judged cleancheck:comment-mask-adoptionOK — 14 private comment-stripper(s) ... all 14 recordedcheck:keyed-text-bounds148 keyed text-family columns judged, 148 boundedcheck:empty-changeset✓ No empty-frontmatter changeset introduced by this diff (1 declaring changeset(s) added).check:nul-bytesOK (scanned 7671 text file(s) ... no raw ASCII control bytes)check:changeset-gate-self-testspnpm lint(repo-wide)Gates re-derived from the actual diff with
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack. Becausefilter.mdxdoes not change, thecontent/docs/**families named at dispatch (check:doc-anchors,check:docs-audit-scope,check:docs-redirects,check:docs-single-h1,check:doc-security-posture) do not match this diff; thepackages/spec/**families above do.Two gates first reported a prerequisite, not a finding, and were re-run after the missing builds:
check:generated(exit 1 —distheld no.d.ts) andcheck:doc-formula-expressions(exit 3 twice —@objectstack/formula, then@objectstack/lint, unbuilt). Both green above. Every exit code was captured before any pipe.Not touched, on purpose
content/docs/data-modeling/queries.mdx— its wording was already correct before any of this and is the target wording, not another site to change. No blanket sweep: the diff is one comment and one changeset.Generated by Claude Code