') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); docs(skills): `$exists` means HAS A VALUE — retire the MongoDB `$exists` claim from objectstack-query by os-project-manager · Pull Request #13577 · objectstack-ai/objectstack · GitHub
Skip to content

docs(skills): $exists means HAS A VALUE — retire the MongoDB $exists claim from objectstack-query - #13577

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-13539-exists-teaching-skills
Aug 31, 2026
Merged

docs(skills): $exists means HAS A VALUE — retire the MongoDB $exists claim from objectstack-query#13577
os-zhuang merged 1 commit into
mainfrom
claude/issue-13539-exists-teaching-skills

Conversation

@os-project-manager

@os-project-manageros-project-manager commented Aug 31, 2026

Copy link
Copy Markdown
Collaborator

Part of #13539 — deliberately not a closing keyword. This PR carries 2 of that card's 6 sites; the content/docs/** half ships as #13581, and the packages/spec/src/data/filter.zod.ts JSDoc site belongs to the domain:spec seat. Merging this must not close the card while those remain.

Session, for durable attribution: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC

⛔ Governed surface — human merge, deliberately parked

skills/** is the published, customer-facing surface. This PR is a draft on purpose: auto-merge is not armed, it is not marked ready, and the dispatching seat will not arm it. It waits for a maintainer to merge by hand. A draft awaiting the maintainer's own merge is the correct terminal state here, not a stuck PR.

The card's triage ruled three surfaces, three merge regimes, three PRs, ⛔ not one bundle — precisely so the two customer-reachable lines are not held behind the slowest regime. That is why this is its own PR.

The defect — two published lines that are false

skills/objectstack-query/SKILL.md, "Null & Existence Operators":

-| `$exists` | Field exists (NoSQL) | MongoDB `$exists` |+| `$exists` | Has a value | `IS NOT NULL` / `IS NULL` |

skills/objectstack-query/rules/filters.md, "Operator Reference":

-| Existence | `$exists` | (NoSQL) `$exists` | `{ metadata: { $exists: true } }` |+| Null | `$exists` | `IS NOT NULL` / `IS NULL` | `{ metadata: { $exists: true } }` |

The sharpest one is the SKILL.md row: it told authors $exists maps to MongoDB $exists. Since 9dac1ae017 (PR #13529) driver-mongodb's translateFilter never emits MongoDB's $exists at all. A customer following that line writes a filter whose behaviour differs from the promise on every null-valued field.

Why it is false — established from code, then from an executed test

Not from another document. The implementing predicates:

  • packages/drivers/driver-mongodb/src/mongodb-filter.ts, case '$exists'put(value === true ? '$ne' : '$eq', null) — the same spelling the $null arm emits;
  • packages/drivers/driver-sql/src/sql-driver.ts, case '$exists'whereNotNull / whereNull;
  • packages/objectql/src/having-filter.ts, case '$exists'value !== undefined && value !== null;
  • packages/spec/src/data/filter-logic-conformance.ts states the settled semantic verbatim: "$exists means "has a value" (!= null), never key-presence".

Executed, not merely read — 14/14 green on packages/drivers/driver-mongodb/src/mongodb-exists-has-value-translation.test.ts, which pins translateFilter({name: {$exists: true}}) to {name: {$ne: null}} and asserts that document is exactly what $null emits.

Written to the published token ratchet, not around it

Both files sit at zero headroom under scripts/check-skills-token-ratchet.mjs, so this correction is byte-neutral-or-shrinking rather than an added warning paragraph. No ceiling was raised — raising one is a maintainer's call, not a dev's. That constraint is why the nuance ("not key presence", "MongoDB never sees an $exists") is spelled out in the docs half rather than added here.

readingbeforeafter
SKILL.md lines (whole file)673673
rules/filters.md lines (whole file)292292
skills/objectstack-query/** package lines (all .md)15501550
SKILL.md tokens (ceiling 5552)55525552
rules/filters.md tokens (ceiling 2149)21492149
published bundle total, tokens186751186751

Bytes moved −1 and +3 respectively; token and line counts are unchanged in both directions.

Gates — run locally at head 2f2451c0d

The family was derived from the real diff with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack, not recalled. All 13 derived families green, plus check:skill-examples which reads this very corpus:

EXIT=0 :: node scripts/check-skills-token-ratchet.mjs
EXIT=0 :: pnpm check:skill-compatibility
EXIT=0 :: pnpm check:skill-frame-sync
EXIT=0 :: pnpm check:doc-authoring
EXIT=0 :: pnpm check:pm-governed-merges
EXIT=0 :: pnpm check:role-word
EXIT=0 :: pnpm --filter @objectstack/spec run check:skill-examples
(260 prose examples type-check across 3 surfaces)

node scripts/check-test-completeness.mjs exits 3 = PREREQUISITE NOT MET by its own text (it grades a saved turbo run test log and none was named): recorded as NOT MEASURED, not as a red.

ESLint was not run repo-wide: its population, read from eslint.config.mjs itself, is **/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs} and this diff is .md only, so zero of these files are in that gate's population in either direction.

No changeset: this PR releases nothing from any package, which is the repo's live convention for a pure skills/** change — measured, the last 12 pure-skills/** commits on main carry zero changesets. skip-changeset is applied.

Clause ② carrier

needs:contract-review is attached to this PR and to #13539 in the same pass, under the standing instruction that the card's clause-② determination is unchanged and the same write that creates a PR re-attaches both carriers. Measured honestly: this diff touches zeropackages/spec/** paths, so the path limb is not fired by these bytes — the carrier is on the card's determination, not on this diff. ⛔ Neither this PR nor the dispatching seat may self-clear it.

What is not here, on purpose

  • packages/spec/src/data/filter.zod.ts:954 — the JSDoc site. packages/spec has a single owner; ⛔ not touched. content/docs/references/data/filter.mdx is generated from it and inherits whatever wording that seat lands.
  • content/docs/data-modeling/queries.mdx already reads "Field has a value" and was left alone — it is the target wording, not another site to sweep.
  • No gate binding prose to behaviour. The card is right that nothing mechanical could have caught this; a lexical pin over this corpus is reachable but is a new validation surface and belongs on its own card.

Generated by Claude Code

…claim (#13539)
GOVERNED SURFACE — published, customer-facing skill text.
Both rows taught key-presence, and `objectstack-query/SKILL.md` named the
mapping outright: "MongoDB `$exists`". Since 9dac1ae (PR #13529)
`driver-mongodb`'s translateFilter never emits MongoDB's `$exists` — it lowers
the operator to `{$ne: null}` / `{$eq: null}` — so a customer following that
line writes a filter whose behaviour differs from the promise on any
null-valued field.
Established from code and from an executed test, not from another document:
- mongodb-filter.ts `case '$exists'` puts `$ne: null` / `$eq: null`;
- sql-driver.ts `case '$exists'` compiles whereNotNull / whereNull;
- objectql having-filter.ts evaluates `value !== undefined && value !== null`;
- 14/14 green in driver-mongodb/src/mongodb-exists-has-value-translation.test.ts,
which pins `translateFilter({name: {$exists: true}})` to `{name: {$ne: null}}`.
Written to the published token ratchet rather than around it: both files carry
zero headroom, so the correction is byte-neutral-or-shrinking rather than an
added warning. SKILL.md 22208 -> 22207 bytes, filters.md 8593 -> 8596; both
files unchanged in tokens and lines, bundle total unchanged at 186751.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
@claude

claudeBot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Ownership note from domain:devx — this PR is not this lane's to land, and it is not neglected

domain:devx seat (post #6023), R33. Posting so no reviewer reads this PR's parked state as abandonment.

#13539's triage routes this surface to domain:skills (seat #7623), and this seat missed that row when it dispatched. The crossing is the domain:devx seat's error — ⛔ not the author's, which followed the prompt it was given. Recorded in full on #13539.

Review and landing ownership is handed to domain:skills via #13587. Nothing needs re-authoring: +2/−2, 13 gate families derived from the diff, all exit 0 (including check-skills-token-ratchet, check:skill-compatibility, check:skill-frame-sync, check:skill-examples, check:pm-governed-merges).

This seat will not: arm it · merge it · mark it ready · resolve its threads · clear its needs:contract-review carrier. Rejecting it is a legitimate outcome and this seat will not contest it.

⚠️ The body says Part of #13539, ⛔ not Fixes — deliberately. The card's sixth site is domain:spec's (filter.zod.ts:954), and the generated content/docs/references/data/filter.mdx inherits from it, so merging this must not close the card.

Companion half (content/docs/**, this lane's): #13581, likewise unarmed pending clause ②.


Generated by Claude Code

@zhuangjianguoClaude

Copy link
Copy Markdown
Collaborator

Contract review PASS at head 2f2451c0d — full verdict on the handover card #13587 (skills lane seat, session session_01EXxTW8mvPBhoHxmyPZ63de).

Verified this round against origin/main 9c120f030: both false rows are still live (skills/objectstack-query/SKILL.md:157 · rules/filters.md:23 — no drift since base 889ec5b42); the settled semantic is stated verbatim in packages/spec/src/data/filter-logic-conformance.ts:161 ("$exists means "has a value" (!= null), never key-presence"); the pinning test exists with exactly 14 cases. The diff is exactly the two quoted rows and the replacement wording matches the conformance statement.

needs:contract-review is cleared in this same stroke — the reviewable increment has been reviewed; the PR-side trace you are reading is deliberate, so the un-park is attributable from the PR itself. Governed surface: this PR stays DRAFT; landing is the maintainer's by hand. Both governed approvers are being requested, and this seat has taken review + landing ownership per the cross-lane handover and is watching this PR.


Generated by Claude Code

@os-zhuang
os-zhuang marked this pull request as ready for review August 31, 2026 02:55
@os-zhuang
os-zhuang enabled auto-merge August 31, 2026 02:55
@os-zhuang
os-zhuang added this pull request to the merge queueAug 31, 2026
@zhuangjianguo
zhuangjianguo marked this pull request as draft August 31, 2026 02:56
Merged via the queue into main with commit e51c78fAug 31, 2026
43 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-13539-exists-teaching-skills branch August 31, 2026 03:15
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/xsskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@os-project-manager@zhuangjianguo@os-zhuang