Repository navigation
census: 谁在写 $exists?——可写面消费者普查,作 #13195 选项 C(词表合并退役 $exists)的前置输入 #13492
Description
Activity
zhuangjianguo commented
on Aug 30, 2026 CollaboratorAuthorMore actionsClaim: PM loop round R6
Session:session_01F3jdziLbAPGeceVNmSox5L
Branch:claude/issue-13492-exists-consumer-census
Worktree:objectstack-issue-13492
Domain:domain:engine
Container & model:M,mode:subagent,model: opus.
Clause ②: no — ⛔ this card changes no code at all, so neither limb can fire.⚠️ One fact has changed since this card was written, and it sharpens the stop conditionThe card says option A "已入队执行". A has now LANDED — PR #13529 merged at 17:32 today, verified by content on
origin/main(the.changeset/exists-has-value-three-exits.mdprobe went ABSENT → PRESENT, and#13195now has 5 citations inmongodb-filter.ts). All three exits read$existsas has-value onmainright now.⇒ The card's stop condition — "发现依赖 key-presence 的活用法 ⇒ 停手报告" — is no longer about a future regression risk. Any authored usage that depends on key-presence is broken on
maintoday. The dispatch carries it as a live-regression stop, not a hypothetical one, and it is the single highest-value thing this census can find.Grading gap, stated rather than filled
This card carries no
priority:label and has no triage comment — it was filed straight intopm:queueby the ruling seat in the same stroke as 批 #7. ⛔ I am not grading it;pm:queueis a dispatchable state and priority affects ordering, not eligibility. Flagging it so triage can grade it when it next sweeps.Serial constraints
⛔ No code changes, so no single-writer path is claimed and nothing can contend. The census reads the corpus only.
Generated by Claude Code
Census: who writes
$exists?Measured on
origin/main@430a278371, read-only worktree, nothing edited (git status --porcelainempty at start and end). No PR, no code change, per the card's ⛔.⚠️ Read this first: the stop condition, and what changed under the cardThe card was written while option A was "已入队执行". A has landed — PR #13529, commit
9dac1ae017. So the card's stop condition (发现依赖 key-presence 的活用法 ⇒ 停手报告) is no longer about a future risk: any key-presence-dependent usage would be broken onmaintoday.Axis 4 answer: ZERO key-presence-dependent usages — and the control fired
There are no authored usages that depend on key-presence. Stated first because a zero here is the expected answer and therefore the dangerous one, so here is the control that fired rather than the bare count.
Control (fires pre-A, silent post-A).
memory-own-key-undefined.test.tsbuilds the only fixture in the repo that can express the distinction — four rows where the key-absent and value-null states are separated:row state a1status: undefined— own key absenta2key never written — absent a3status: null— key present, value nulla4status: 'draft'— a value- Pre-A (
f087c376ff) that test asserted$exists: true→['a3','a4']and$exists: false→['a1','a2'].a3was included solely because the key was present. That is a key-presence-dependent authored usage, and my detector sees it. - Post-A the same assertions read
['a4']/['a1','a2','a3'], and A added two identity assertions in place.
So the detector demonstrably fires on a tree where such a usage exists, and returns zero on
main. The zero is a reading.Why it is zero by construction, not just by sampling. Post-A, every exit lowers
$existsto a null test, so key-absent and value-null are no longer distinguishable by any$existsfilter — the dependence is not merely absent, it is inexpressible:exit lowering driver-memory live mingo path $ne: null/$eq: nulldriver-memory reference matcher value !== undefined && value !== nulldriver-memory analytics face ( set)$ne: null/$eq: nulldriver-mongodb translateFilter$ne: null/$eq: nulldriver-sql whereNull/whereNotNulldriver-turso remote transport IS NULL/IS NOT NULLformula matches-filteractual != nullobjectql having-filtervalue !== undefined && value !== nullservice-analytics read-scope-sqlIS NOT NULL/IS NULLConfirmed independently in
memory-exists-has-value-faces.test.ts, which runs the same queries against a value-null fixture and a key-absent fixture and gets identical answers on all exits.One place the distinction was a fair question and is now closed: on JSON columns,
driver-sqldoes not refuse$exists— it answers a whole-column null test. There is no JSON-key-existence capability, so nothing depended on one.The finding that replaces the expected one
Axis 4 is clean in the executable corpus. It is not clean in the teaching corpus, and that population is invisible to CI because none of it executes. A landed in 11 files, all driver code and driver tests — it touched no docs, no spec, no skill, no example. Six teaching sites still describe key-presence:
site what it says post-A packages/spec/src/data/filter.zod.ts:954"Field exists check (primarily for NoSQL) - MongoDB: $exists"⛔ stale content/docs/protocol/objectql/query-syntax.mdx:264"Field exists (NoSQL)" ⛔ stale content/docs/protocol/objectql/query-syntax.mdx:518code block commented // Field exists (NoSQL)⛔ stale content/docs/protocol/kernel/http-protocol.mdx:397"Field existence check" ⛔ stale skills/objectstack-query/SKILL.md:157"Field exists (NoSQL) / MongoDB $exists"⛔ stale, published skill skills/objectstack-query/rules/filters.md:23"Existence / (NoSQL) $exists"⛔ stale, published skill content/docs/data-modeling/queries.mdx:71"Field has a value" ✅ correct The sharpest one is the published skill's claim that
$existsmaps to MongoDB$exists. Post-A that is factually false:translateFilteremits$ne: null. It is one line, and it is the line an AI author reads before writing a filter.⛔ Not fixed here — the card forbids code changes, and this is #13195's regression input, the maintainer's call.
Axis 1 — authored sites in the corpus
Corpus = tracked files (
git grep), nonode_modules/dist. 628 occurrences of$existsin 77 files; 321 in operator position; 156 lines carrying 168 occurrences are authored filter sites after excluding comments, backticked prose,caselabels and the Zod declaration.zone authored sites note examples/**0 control fired — see below test fixtures 154 lines, 33 files docs code blocks 4 listed below non-test platform source 0 the one hit, memory-analytics.ts:66, is an inbound map keyControl for the
examples/**zero — it fired. The same sweep over the same zone sees authored filters written by the real example apps:$ne×15,$gt×10,$in×4,$or×1, e.g.examples/app-showcase/src/automation/flows/index.ts:1636filter: { status: { $ne: 'done' } }andexamples/app-crm/src/dashboards/pipeline.dashboard.ts:93close_date: { $gte: '{1_years_ago}', $lte: '{today}' }. The two shipped example apps author filters extensively and never once write$exists.apps/is reported as not measured, not as a zero: it holds only the Next.js docs-site shell (14 tsx / 14 ts, no data layer) and contains no authored filters of any operator, so the reach control cannot fire there.Shapes
Across the 168 authored occurrences:
shape count $exists: true88 $exists: false54 inside a $not27 lines non-boolean comparand 19 The 19 non-boolean comparands (
'yes','false',undefined,1,'a') are not authored intent — they are inputs to out-of-contract refusal suites (sql-driver-out-of-contract-filter-input,remote-transport-null-comparand-refusal,read-scope-boolean-flag-comparand), which assert the operator rejects or coerces them.The 4 doc-block examples
content/docs/data-modeling/queries.mdx:71—{ phone: { $exists: true } }content/docs/protocol/objectql/query-syntax.mdx:264—{ metadata: { $exists: true } }content/docs/protocol/objectql/query-syntax.mdx:518—where: { metadata: { $exists: true } }(fenced, carries anos:checkmarker)skills/objectstack-query/rules/filters.md:23—{ metadata: { $exists: true } }
Three of the four use a
metadatafield, i.e. exactly the field where an author is most likely to mean "the key is there".
Axis 2 — platform code that GENERATES
$existsZero generation sites on
main.Control fired twice on the pre-A tree. The identical sweep run against
f087c376fffinds both emission sites that A removed:packages/drivers/driver-memory/src/memory-analytics.ts:267—set: ({ raw }) => ({ $exists: ... }), built its own operator and handed it to mingo. Now emits$ne: null/$eq: null.packages/drivers/driver-mongodb/src/mongodb-filter.ts:730—case '$exists': result[op] = value;, passing the operator through to the MongoDB wire. Now lowers to$ne: null/$eq: null.
Blind-spot check, because a literal sweep cannot see a computed-key emission. The pre-A MongoDB site was
result[op] = value— no literal$existson the line, caught only via itscaselabel. So I checked every filter translator for a pass-through arm that could emit$existswithout naming it: all of them terminate in adefault:arm that refuses unknown operators, and the one shared assignment (mongodb-filter.ts:737) covers only$eq $ne $gt $gte $lt $in $nin. No silent pass-through exists.Every remaining
$existsin platform source is a consumer (case '$exists':), not a generator.
Axis 3 — where authors are taught to write
$exists17 declaration and teaching sites.
Spec / machine-readable (5)
packages/spec/src/data/filter.zod.ts:955—SpecialOperatorSchema.$exists: z.boolean().optional()packages/spec/src/data/filter.zod.ts:1017—FieldOperatorsSchema.$existspackages/spec/src/data/filter.zod.ts:1367— theFilterTS typepackages/spec/src/data/filter.zod.ts:2222— theFILTER_OPERATORSvocabulary arraypackages/spec/authorable-surface/data.json+authorable-surface.base.json— enrolled asdata/SpecialOperator:$exists
Generated reference (1)
content/docs/references/data/filter.mdx:139— auto-generated from the Zod schema; the Description column is empty, so the generated reference teaches the key with no semantics at all
Prose docs (7) —
queries.mdx:71,query-syntax.mdx:264and:518,http-protocol.mdx:397,wire-format.mdx:179,data-engine.mdx:186,types.mdx:1071,troubleshooting.mdx:128(the last four are vocabulary lists with no semantics)Published skills (2) —
skills/objectstack-query/SKILL.md:157,skills/objectstack-query/rules/filters.md:23
Input for the C decision (measurement only — C is not mine to argue)
Four things the census turned up that bear directly on whether C is worth an ADR-0087-grade retirement:
$existsis now provably a synonym of inverted$null, and this is pinned executably — 11 assertions across 5 packages (driver-memory, driver-sql, formula, service-analytics) assert the two spellings return the same rows or compile to the same SQL, e.g.expect(await ids({ status: { $exists: true } })).toEqual(await ids({ status: { $null: false } })). Four backends carry the comment "$null: trueand$exists: falseare the same question". A retirement would therefore lose no expressive power.- The metadata authoring surface never offered it.
VIEW_FILTER_OPERATORS(saved views, list views) spells thisis_null/is_not_nulland has noexistsspelling at all — that surface already unified on the null spelling. - There is a precedent and a template.
$regex/$optionswere retired viaRETIRED_FILTER_OPERATORS(12 non-test consumers) plus a semantic migration entry,packages/spec/src/migrations/entries/semantic/17.filter-regex-options-retired.ts.⚠️ But that entry records the load-bearing caveat:FilterConditionSchemais an open record, so a stored{ field: { $exists: true } }parses green and always will — aretiredKey()tombstone cannot exist on an open record. C's refusal would have to live at the runtime refusal layer, exactly as$regex's does. $existsis declared and lowered on all nine exits but is enrolled in no cross-backend conformance case — zero entries inFILTER_LOGIC_CASES(the 5 mentions in that file are header prose above the case array).
The in-repo stored-metadata population that a C migration would have to rewrite is zero: no
$existsinexamples/**, and none in any JSON or YAML fixture (the only JSON hits are the authorable-surface ledger itself).
Method, and what I did NOT measure
Where the authored/generated line blurred (it did). Three cases resisted the two buckets, and I report the blur rather than forcing them:
memory-analytics.ts:66$exists: 'set'is an inbound map key — it matches an authored-site regex but is a consumer. Counted as neither authored nor generated.case '$exists':labels match operator position but are consumption. Excluded, explicitly.- Test fixtures are authored filters that exist only to drive a translator, so they are arguably both. Counted as authored (they are written by hand), and they are 154 of the 156 sites — so the headline "158 authored sites" is overwhelmingly the platform testing itself, not applications using the operator.
Not measured, stated as not-measured:
apps/— dead zone for filters, reach control cannot fire (above).- Sibling repos
objectuiandcloudwere not swept; this census isobjectstackonly. If authors write$existsthere, this count does not see it. That is a real gap for C, since C's migration population would span them. - No runtime/telemetry data: this is a static corpus census, so it cannot say what real deployments send over the wire. "Nobody in the repo writes it" is not "nobody writes it".
- Git history was read only at the two commits named (
430a278371,f087c376ff); I did not census$existsusage across the full history. - I ran no build and no test suite — the card forbids code changes and the census needs neither. Every claim above is from static reads plus the pre-A/post-A source comparison, not from executing the suites.
Controls, all shown firing: regex-sees-literal-members (anchor line 170, hand-read with
sedfirst); corpus-reach per zone; discriminator-retains-known-site; pre-A sweep for axis 2; pre-A fixture for axis 4.⚠️ One correction worth recording: my first repo-wide operator-position count returned 0 while the same regex fired on a single file — a shell-quoting artifact, not a reading. It was caught only because the control disagreed with the count. The sweep was moved into a script file and re-run; every number above comes from the re-run.Generated by Claude Code
Generated by Claude Code
- Pre-A (
os-dev-report
{ "issue": 13492, "status": "done", "branch": "claude/issue-13492-exists-consumer-census (not created — no code change, see summary)", "pr": null, "premise_still_valid": true, "summary": "Census delivered as comment 5470462097 on #13492. Nothing was edited: no worktree branch, no PR, no changeset; git status --porcelain empty at start and end, read-only worktree pinned at origin/main 430a278371. AXIS 4 FIRST: zero key-presence-dependent authored usages, and the control fired — memory-own-key-undefined.test.ts asserted $exists:true -> ['a3','a4'] on the pre-A tree f087c376ff (a3 stores an explicit null, included solely because the KEY was present) and reads ['a4'] post-A. The dependence is now inexpressible, not merely absent: all nine exits lower $exists to a null test, and memory-exists-has-value-faces.test.ts gets identical answers from a value-null fixture and a key-absent fixture. The card's own premise held, though its statement that A was '已入队执行' was stale — A landed as 9dac1ae017 (PR #13529), which sharpened the stop condition rather than invalidating the card. THE FINDING THAT REPLACES THE EXPECTED ONE: axis 4 is clean in the executable corpus but not in the teaching corpus, which CI cannot see because none of it executes. A touched 11 files, all driver code and driver tests, and no docs/spec/skill/example — so six teaching sites still describe key-presence, including two PUBLISHED skills; skills/objectstack-query/SKILL.md:157 still tells authors $exists maps to MongoDB $exists, which is false post-A since translateFilter now emits $ne:null. Not fixed, per Zone 1.1 and 1.3.", "tests": "No build and no test suite was run — the card forbids code changes and a static census needs neither; every number is from static reads plus a pre-A/post-A source comparison. Stated as not-measured rather than folded into a count. FIVE CONTROLS, ALL SHOWN FIRING: (1) regex-sees-literal-members — anchor read by hand with sed first (memory-exists-has-value-faces.test.ts:170), then shown in sweep output; (2) corpus-reach per zone — examples/ sees $ne x15, $gt x10, $in x4, $or x1 as real authored filters, so its $exists zero is a reading; content/docs/skills/packages all fire; apps/ does NOT fire and is reported not-measured, not zero (Next.js docs shell, no data layer); (3) discriminator-retains-known-site; (4) axis-2 control — the identical sweep on pre-A f087c376ff finds BOTH emission sites A removed (memory-analytics.ts:267 built {$exists:...} for mingo; mongodb-filter.ts:730 case '$exists': result[op]=value passed it to the wire); (5) axis-4 control as described above. BLIND-SPOT CHECK: the pre-A MongoDB emission had no literal $exists on its line (computed key result[op]=value), so a literal sweep could miss that class; I checked every filter translator for pass-through arms — all terminate in a default: that REFUSES unknown operators, and the one shared assignment covers only $eq $ne $gt $gte $lt $in $nin. COUNTS: 628 occurrences in 77 files; 321 operator-position; 156 authored lines carrying 168 occurrences (154 test-fixture lines in 33 files, 4 doc-block examples, 0 in examples/**, 0 in non-test platform source); shapes true x88, false x54, 27 lines inside $not, 19 non-boolean comparands that are refusal-suite inputs rather than authored intent. Axis 2 = 0 generation sites. Axis 3 = 17 declaration/teaching sites. ⚠️ ONE CORRECTION RECORDED IN THE CENSUS: my first repo-wide operator-position count returned 0 while the same regex fired on a single file — a shell-quoting artifact, caught only because the control disagreed with the count. The sweep was moved into a script file and re-run; all reported numbers are from the re-run.", "mcp_calls": "1 — CHANNEL SWITCH DECLARED: the census itself used 0 (repo-scoped REST probe was 200, so card read, comment reads, all comment writes, the new issue POST and every readback went over git + REST). The dedup search needed 1 MCP search_issues because repo-scoped REST is open but the GLOBAL /search/* path is 403 in this session ('sessions are bound to their configured repositories'), and the zero-quota payload channel does not cover issue search. That one call returned 5 results, so the channel was live rather than silently empty — the empty-result trap did not apply.", "open_questions": [], "out_of_scope_findings": [ "FILED as #13539 (unassigned, unlabelled) after the PM seat authorised it: six teaching sites still describe $exists as key-presence after A landed (packages/spec/src/data/filter.zod.ts:954; content/docs/protocol/objectql/query-syntax.mdx:264 and :518; content/docs/protocol/kernel/http-protocol.mdx:397; skills/objectstack-query/SKILL.md:157; skills/objectstack-query/rules/filters.md:23), against one that is CORRECT and must not be swept (content/docs/data-modeling/queries.mdx:71 'Field has a value'). The card carries: the structural reason no gate could have caught it (PR #13529 touched 11 files, driver code and tests only — the teaching corpus does not execute, so there is no arrangement of current gates under which this goes red); that the two skills/ lines are published and customer-facing and should be graded on that basis; independence from option C (if C is rejected the docs are still wrong); a line-number rot warning; and a FENCE NOTE that packages/spec/** and skills/** are governed surfaces requiring a maintainer-authorised path. Nothing edited, no PR, not graded.", "NOT filed — already covered: my conformance observation ($exists lowered on all nine exits but enrolled in ZERO FILTER_LOGIC_CASES) is already tracked as #13531, found by the dedup search. #13524 covers the key-clobbering class. Neither duplicates #13539.", "Sibling repos objectui and cloud were NOT swept — this census is objectstack only. If C proceeds, its migration population spans them and this count does not see it." ] }Generated by Claude Code
Generated by Claude Code
zhuangjianguo commented
on Aug 30, 2026 CollaboratorAuthorMore actionsPM review — ACCEPT. The census is delivered (
5470462097) and ⛔ nothing was edited: no branch, no PR, no changeset,git status --porcelainempty at both ends, worktree pinned read-only and removed without--force.Axis 4 — the answer, and why the zero is a reading
Zero key-presence-dependent authored usages, and the control that licenses that zero is the strongest I have seen this round:
memory-own-key-undefined.test.tsanswered$exists: true → ['a3','a4']on the pre-A tree —a3stores an explicitnulland was included solely because the key was present — and answers['a4']now. So the sweep demonstrably can see a key-presence-dependent usage before concluding there are none.⭐ And it goes past a count to an argument: the dependence is now inexpressible, not merely absent. All nine exits lower
$existsto a null test, andmemory-exists-has-value-faces.test.tsgets identical answers from a value-null fixture and a key-absent fixture.⭐ But the expected finding was replaced by a better one
Axis 4 is clean in the executable corpus and not in the teaching corpus — and CI structurally could not have caught the difference, because none of the teaching corpus executes. A touched 11 files, all driver code and driver tests: no docs, no spec, no skill, no example. So six teaching sites still describe key-presence:
packages/spec/src/data/filter.zod.ts:954·content/docs/protocol/objectql/query-syntax.mdx:264and:518·content/docs/protocol/kernel/http-protocol.mdx:397·skills/objectstack-query/SKILL.md:157·skills/objectstack-query/rules/filters.md:23— against one that is correct (content/docs/data-modeling/queries.mdx:71).The last two are published and customer-facing, and
SKILL.md:157still tells authors$existsmaps to MongoDB's$exists, whichtranslateFilterstopped doing when A landed. ⇒ I have asked for this as its own card, unassigned and unlabelled. It does not wait on C: the executing seat's own point is decisive — if C is rejected the docs are still wrong.⚠️ packages/spec/**andskills/**are governed surfaces, so the card flags that the repair is not a straightforward edit.⭐ A false zero caught by its own control
The first repo-wide operator-position count returned 0 while the same regex fired on a single file — a shell-quoting artifact, visible only because the control disagreed with the count. It was reported in the census rather than quietly re-run. This seat has caught itself using four non-discriminating probes today; this is the case where the discipline actually fired and saved the number.
The counts, and what they mean for C
628 occurrences / 77 files; 321 operator-position; 156 authored lines carrying 168 occurrences — but 154 of those 156 are the platform testing itself, 0 in
examples/**, 0 in non-test platform source. ⇒ "158 authored sites" is not application demand, and the executing seat said so rather than letting the number speak for itself. Axis 2 = 0 generation sites (A removed both; the pre-A control found them). Axis 3 = 17 declaration/teaching sites.Three inputs the maintainer will want for the C decision, none of which I asked for:
$existsis now provably inverted$null, pinned executably — 11 assertions across driver-memory, driver-sql, formula and service-analytics assert the two spellings return the same rows or compile to the same SQL. A retirement would lose no expressive power.- The metadata authoring surface never offered it —
VIEW_FILTER_OPERATORSspells thisis_null/is_not_null, with noexistsspelling at all. - The precedent carries a caveat that decides C's shape —
$regex/$optionswere retired viaRETIRED_FILTER_OPERATORSplus a semantic migration entry, and that entry records thatFilterConditionSchemais an open record. So a stored$existsparses green forever and aretiredKey()tombstone cannot exist ⇒ C's refusal would have to live at the runtime refusal layer, not the schema.
Zone 2 — two of mine broke
C broke: the authored/generated line blurred three ways, reported rather than forced into buckets. D partly broke: the
$inreach control does not transfer toapps/, which has no authored filters of any operator — correctly reported as a dead zone, not a zero. A confirmed; B confirmed but historical (the sites were where I guessed, and A had already removed both).⚠️ Declared limit: sibling reposobjectuiandcloudwere not swept. If C proceeds, its migration population spans them and this count does not see it.pm:dispatchedstripped; the deliverable is delivered, so the card closes. ⛔ Grading was left to triage throughout — this card still carries nopriority:.
Generated by Claude Code
Correction to the census above, for the record: the stale-teaching finding has now been filed — it is #13539, unassigned and unlabelled, for triage to grade and route.
My census comment listed it as "not filed, deliberately", which is no longer accurate. The PM seat's ruling on the point: the six sites are a different class from what #13195 carries as its regression input. That is authored usages that depend on key-presence — code that would break, of which the census found zero. #13539 is documentation that has become false on a surface that ships, and it does not depend on the option-C decision: if C is rejected the docs are still wrong.
⛔ Nothing was edited for it either — no PR, no grade. Also recorded there: #13531 already covers the
FILTER_LOGIC_CASESenrolment gap I listed as my second observation, so that one needed no new card.Generated by Claude Code
Generated by Claude Code
由维护者 2026-08-30 决裁批 #7 对 #13195 的裁定(verbatim「批 #7 同意」)同笔立卡:A(三出口对齐 has-value)已入队执行;C(退役可写
$exists、统一用$null)未裁,其唯一前置输入是本普查——没人取过这个数。任务(机械普查,无裁决内容)
清点谁在写
$exists:$exists的站点数与形状($exists: true/$exists: false/$not组合);$exists查询的站点(translator/builder 侧);$exists的 schema/文档位置(教作者写它的地方);产出:计数 + 逐站点清单落卡评论,C 的 ADR-0087 级退役是否值得走由维护者据此裁。
⛔ 本卡不改任何代码;发现依赖 key-presence 的活用法 ⇒ 停手报告(那是 A 执行卡 #13195 的回归输入)。
Refs: #13195(裁定与三倍严重度测量)· PR #13420(已合并的判向预写钉)。