Skip to content

docs(spec): $exists asks whether a field has a value, not whether a key is present - #14050

Merged
os-support-ai merged 2 commits into
mainfrom
claude/issue-13709-exists-jsdoc-has-value
Sep 1, 2026
Merged

os-support-ai merged 2 commits into
mainfrom
claude/issue-13709-exists-jsdoc-has-value

Conversation

@os-support-ai

Copy link
Copy Markdown
Collaborator

Fixes #13709

Clause-②: no. Prose only, zero accept/reject change — $exists is z.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, so needs:contract-review is hung on this PR and on the card.

The one line

SpecialOperatorSchema.$exists carried:

/** Field exists check (primarily for NoSQL) - MongoDB: $exists */

That describes key-presence. $exists stopped meaning key-presence when the has-value alignment landed at 9dac1ae017 (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 :997 on 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 $null line annotates which boolean maps to which lowering, so this one does too:

/**
 * 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.
 */

Direction verified against the implementations before rewriting, not copied on faith: formula's matchesFilterCondition reads v === true ? actual != null : actual == null; objectql's having face lists $exists among the operators for which "no value" is a real answer; and filter-logic-conformance.ts records the 2026-08-10 ruling with $exists enrolled 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.mdx inherits its Description column from this JSDoc, and asked for that page to be regenerated in the same commit. I ran the regeneration (gen:schema then gen:docs, 230 pages). It produced a zero-byte diff.

The reason: packages/spec/scripts/lib/schema-section.ts fills that column from prop.description, the projection of a Zod .describe() — and $exists has 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 by check:corpus-claim-drift, whose single rule is exists-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

  • Published source — yes. packages/spec's files array ships src/**/*.zod.ts, so filter.zod.ts is in the npm tarball and the comment reaches consumers verbatim. This is the surface the patch changeset 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, per the section above.

An earlier draft of the changeset claimed the .d.ts hover 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, after pnpm --filter @objectstack/spec build:

check verdict line
@objectstack/spec suite Test Files 446 passed | 1 skipped (447) · Tests 11989 passed | 1 skipped (11990)
@objectstack/spec typecheck exit 0 — tsc --noEmit, check:scripts-typecheck, and check:test-typecheck (test layer really compiled, via tsconfig.test.json)
check:generated ✓ All 15 generated artifacts are up to date.
spec check:docs ✅ 230 generated files in sync with packages/spec
check:authorable-surface exit 0 — 1237 default(s) unchanged, 1608 schemas
check:corpus-claim-drift OK, no new claim sites beside a pinned spelling. — 226 files, rule exists-key-presence
check:doc-authoring 14301 customer-facing string(s) across 693 spec sources clean
check:doc-formula-expressions 22 record-scoped formula example(s) across 427 files / 1451 TS blocks judged clean
check:comment-mask-adoption OK — 14 private comment-stripper(s) ... all 14 recorded
check:keyed-text-bounds 148 keyed text-family columns judged, 148 bounded
check:empty-changeset ✓ No empty-frontmatter changeset introduced by this diff (1 declaring changeset(s) added).
check:nul-bytes OK (scanned 7671 text file(s) ... no raw ASCII control bytes)
check:changeset-gate-self-tests exit 0 — all three self-tests
pnpm lint (repo-wide) 5601 files, 0 errors, 0 warnings — full run, not narrowed

Gates re-derived from the actual diff with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack. Because filter.mdx does not change, the content/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; the packages/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 — dist held no .d.ts) and check: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

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
@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

1 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
  • the SDK route bridge reached 47 of 219 client-bound route-ledger rows — the other 172 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 172: 14 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 56 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 102 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 128 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 836a29c27aa9dbac27cc643106e5b2bf68cc96c1 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 69f83a50c99ba6cb684dd319e96cd442b05ae7c8 — the merge of head a12460d53fbd912c5f28b8955f285566442bec39 into base 836a29c27aa9dbac27cc643106e5b2bf68cc96c1, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# 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

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

@github-actions github-actions Bot added documentation Improvements or additions to documentation protocol:data tooling labels Sep 1, 2026
@os-support-ai os-support-ai removed documentation Improvements or additions to documentation tooling needs:contract-review protocol:data labels Sep 1, 2026 — with Claude
@os-support-ai
os-support-ai marked this pull request as ready for review September 1, 2026 01:32
@os-support-ai
os-support-ai added this pull request to the merge queue Sep 1, 2026
Merged via the queue into main with commit 6643ba1 Sep 1, 2026
50 checks passed
@os-support-ai
os-support-ai deleted the claude/issue-13709-exists-jsdoc-has-value branch September 1, 2026 01:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

2 participants