Skip to content

$exists docs and two PUBLISHED skills still teach key-presence — false since the has-value alignment landed, and no gate can catch it because the teaching corpus does not execute #13539

Description

@claude

Six places in the teaching corpus still describe $exists as key-presence. That stopped being true when the has-value alignment landed at 9dac1ae017 (PR #13529), so these lines are not stale wording — they are false, and two of them ship to customers.

Found by the consumer census on #13492 (comment 5470462097). Measured on origin/main @ 430a278371.

The six false sites

site what it says today why it is false
packages/spec/src/data/filter.zod.ts:954 JSDoc: "Field exists check (primarily for NoSQL) - MongoDB: $exists" ⚠️ governed surface — see the fence note below
content/docs/protocol/objectql/query-syntax.mdx:264 table: "Field exists (NoSQL)" operator means has-value on every backend
content/docs/protocol/objectql/query-syntax.mdx:518 code block commented // Field exists (NoSQL) same, and it carries an os:check marker
content/docs/protocol/kernel/http-protocol.mdx:397 "Field existence check" same
skills/objectstack-query/SKILL.md:157 "Field exists (NoSQL) / MongoDB $exists" published; translateFilter no longer emits MongoDB $exists
skills/objectstack-query/rules/filters.md:23 "Existence / (NoSQL) $exists" published

⛔ One site is already CORRECT — do not sweep it

content/docs/data-modeling/queries.mdx:71 reads "Field has a value", which is right and matches the shipped behaviour. Naming only the wrong sites invites a blanket find-and-replace that would break the one line already telling the truth. Whoever takes this should treat that row as the target wording, not as another thing to change.

The sharpest one, and why this is urgent rather than tidy

skills/objectstack-query/SKILL.md:157 tells authors that $exists maps to MongoDB $exists. Since 9dac1ae017, driver-mongodb's translateFilter lowers it to {$ne: null} / {$eq: null} and never emits MongoDB's $exists at all. A customer following that line today writes a filter whose behaviour does not match what the skill promises — on a null-valued field the two readings return different rows. The skills/ directory is the published, customer-facing surface, and this should be graded on that basis rather than as an internal docs nit.

⭐ Why no gate caught this, and why none would have

This is the reusable part, and it is a statement about the gate's reach rather than about whoever landed the alignment.

PR #13529 touched 11 files: driver code and driver tests only — no docs, no spec, no skills, no examples. Its CI was green and correctly so: every executable assertion about $exists was updated in the same commit. The teaching corpus does not execute. No test imports a .mdx table row, and no gate reads a skill's prose for semantic agreement with the drivers it describes, so there is no arrangement of the current gates under which this would have gone red.

That means the drift is silent by construction and will stay wrong until someone reads it — which is how this class of defect survives for months. Worth considering, separately from the repair, whether any cheap check could bind the documented semantic to the implemented one.

Independence from #13195's option C

⚠️ This is not blocked behind the C decision (retire $exists, unify on $null), and should not inherit its gate: if C is rejected, the docs are still wrong, because they describe the pre-alignment semantic rather than a spelling that might later be retired. If C is accepted, these lines get rewritten anyway — but they are false in the meantime either way.

It is also a different class from what #13195 carries as its regression input. That is authored usages that depend on key-presence — code that would break. The census found zero of those. This is documentation that has become false on a surface that ships. The two are not the same thing.

⚠️ Fence — flagging, not acting

packages/spec/** and skills/** are governed surfaces. Whoever picks this up does not simply edit them; that is a maintainer-authorised path. Noting it here so the next seat does not walk into the fence mid-repair. The content/docs/** sites carry no such fence.

Note also that content/docs/references/data/filter.mdx:139 is auto-generated from the Zod schema and must not be hand-edited — its Description column is currently empty, so it would inherit whatever wording the filter.zod.ts:954 JSDoc ends up with.

⚠️ Line numbers rot

All line numbers above are as of 430a278371 and this is precisely an area with active concurrent work — #13529 landed today and moved several of these files' neighbourhoods. Re-locate by the quoted text, not by the line number.

Method

Census sweep over tracked files (git grep, no node_modules/dist), with a corpus-reach control shown firing per zone. Dedup: one targeted search (repo-scoped REST /search/* is 403 in that session, so this went via MCP search_issues); it returned 5 results, so the channel was live rather than silently empty. None covers the teaching corpus. Related but distinct: #13531 covers the FILTER_LOGIC_CASES enrolment gap for the same operator; #13524 covers the key-clobbering class. Neither is this.

Refs: #13492 (the census) · #13195 (the ruling) · PR #13529 (the alignment that made these lines false).

⛔ Nothing was edited and no PR was opened for this — filed as a finding for triage to grade and route.

Generated by Claude Code


Generated by Claude Code

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions