Skip to content

[finding] $exists with a non-boolean comparand ("yes", 1) is accepted at every door and inverted on driver-memory: it returns the rows with NO value; the spec declares $exists: z.boolean(), and its $null twin is refused #20897

Closed

Description

@objectstack-fleet

Filing gate: ① a defect with a named landing site. Finding class (b). reach: measured at the public engine.find door on the in-memory driver by #20822 group 1's dev (os-dev-report 5915148713 on #20822, out_of_scope_findings[0]).

Filed by the domain:engine execution seat 1 (session_01DEvba2nBuD4tWzfq8r8NFY, os-support-ai). ⛔ Filed bare: routing and grading belong to triage. ⛔ Not a claim.

What happens

The object px has a text field name. Row a has name = "won", and row b has name = null.

where driver-memory right answer
{ name: { $exists: true } } [a] [a]
{ name: { $exists: "yes" } } [b], the no-value row refused, as $null is
{ name: { $exists: 1 } } [b] refused

Seam: spec:FieldOperatorsSchema.$exists (boolean) → runtime:objectql engine filter doors (no refusal) → driver-memory normalizeFieldOperators $exists arm. Measure the other drivers.

Scope for whoever takes it (⛔ not a ruling)

  • A non-boolean $exists is refused at the engine's comparand doors, in the $null refusal's words, on every driver. The spec already declares the boolean.
  • Pins: "yes", 1 and "false" refused on memory and SQLite, plus a true / false control.

Dedupe

mcp__github__search_issues, repo-scoped, open and closed, in the act that filed this card:

Dedupe words: exists non-boolean comparand refused · $exists truthy string inverted driver-memory · 5347 null twin exists


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Sep 30, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade — bug · priority:p2 · domain:engine · area:api · pm:queue. Direction: a non-boolean $exists is refused at the comparand doors, in $null's words

    Triage seat (objectstack-wide, seat post #6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-09-30T18:00Z. ⛔ Not a claim, ⛔ not a dispatch.

    Triage: the engine's comparand doors (packages/objectql) ⇒ domain:engine. driver-memory's arm is fixed by the refusal reaching it first.

    Why p2. A silent inversion on memory: $exists: "yes" returns the rows with no value. The spec declares $exists boolean, and its twin $null already refuses a non-boolean.

    Direction.

  2. added
    area:apiThe API a customer can call, and integrations — REST, connectors, webhooks, jobs
    bugSomething isn't working
    and removed on Sep 30, 2026
  3. objectstack-fleet commented on Sep 30, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1
    Session: session_01Ujdtvqs7ree7WyQmEDwEnG
    Account: os-litant (the seat's linked user as GET /user answers it; always the card's assignee)
    Branch: claude/issue-20897-exists-non-boolean-refused
    Worktree: objectstack-issue-20897
    Domain: domain:engine
    Seat: domain:engine#2 (seat post #20966; the maintainer named this card for seat 2 in chat: 「你是第2个席位,你只处理 p2 的卡片,#20873、#20874、#20897」)
    File surface (triage's direction 5916839636: a non-boolean $exists is refused with INVALID_FILTER / 400, in $null's words, on every driver):

    • packages/drivers/driver-memory/src/filter-refusal.ts: the comparand-shape gate that refuses a non-boolean $null and $empty refuses a non-boolean $exists, in the words driver-sql already uses for it (nonBooleanExistsComparandError). ⛔ No second message.
    • packages/drivers/driver-mongodb/src/mongodb-filter.ts: the same, beside its $null gate, if measured accepting today.
    • packages/objectql (an engine-level door): only if the measurement shows the per-face gates cannot give one answer on every face. The report names the landing chosen and why.
    • pin flip: packages/drivers/driver-memory/src/memory-null-comparand-refusal.test.ts, the case "$exists is deliberately NOT tightened here", pins the inverted answer this card removes. It turns into a refusal pin, ⛔ not deleted.
    • pins: "yes", 1 and "false" refused on memory and SQLite (and MongoDB where its suite runs); true / false unchanged (the control).
    • .changeset/20897-*.md.

    Stop on breach and explain in the report.

  4. objectstack-fleet commented on Sep 30, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 20897,
    "status": "done",
    "branch": "claude/issue-20897-exists-non-boolean-refused",
    "pr": "#20979",
    "session": "session_01Ujdtvqs7ree7WyQmEDwEnG (subagent run; the id is the dispatching session's, as the commit trailers carry it)",
    "premise_still_valid": true,
    "summary": "driver-memory and driver-mongodb now refuse a non-boolean $exists comparand with INVALID_FILTER / 400, in driver-sql's nonBooleanExistsComparandError words. Each refusal sits beside the driver's existing $null refusal: memory's one shape gate (filter-refusal.ts assertFilterConditionShape, which covers find, findOne, count, aggregate, updateMany, deleteMany and the cube face's query() and generateSql()), and mongodb's validating walk (reduceFilterKey) plus a local check in the emitter arm. The premise held on driver-memory: $exists 'yes' / 1 returned the no-value row, the intent inverted, and its cube face answered the opposite rows by truthiness. 'Every door' did not hold: driver-sql, sqlite-wasm, both Turso transports and both service-analytics faces already refused (PM's H1). H1-H5 all held. No engine-level door was added: the only faces still accepting after this PR are the engine's in-process evaluators (objectql aggregation filter and having, in having-filter.ts) and formula matches-filter.ts, both fenced, and each measured and named. The pin 'is deliberately NOT tightened here' in memory-null-comparand-refusal.test.ts is flipped into a refusal pin (code + status + driver-sql's first sentence), not deleted.",
    "tests": "head da4feaf. [driver-memory] pnpm --filter @objectstack/driver-memory test: 68 files, 1500 passed. [driver-mongodb] pnpm --filter @objectstack/driver-mongodb test: 30 passed, 5 skipped (live-mongod suites, opt-in OS_TEST_MONGODB_MEMORY_SERVER_ENABLED); 671 passed, 172 skipped. The new mongodb pins run on translateFilter and do not need mongod. [typecheck] typecheck for both packages exit 0; the new memory test is in tsc --noEmit's program (--listFiles count 1), the mongodb one in tsconfig.test.json (count 1). [cross-face] turso remote-transport-null-comparand-refusal + turso-local-remote-null-parity: 90 passed; service-analytics where-boolean-flag-refusal + read-scope-boolean-flag-comparand: 104 passed. [measurement] A scratch script over built dist ran engine.find / count / aggregate(where) / aggregate(agg filter) / aggregate(having) on memory and SQLite, memory driver entries, the cube face, sqlite-wasm, mongodb translateFilter and formula, before (f6ccca4) and after. Memory: every entry went from ROWS [b] to REFUSED INVALID_FILTER/400. Cube face: from sum over row a ('yes' / 1 / 'false') to REFUSED. Mongodb: from {$eq: null} to REFUSED. true / false unchanged everywhere. [ablation] Run from the committed fix via scripts/ablation-replace.mjs (anchor 1 to 0 and blob change asserted on disk; restore proven as blob == HEAD with git diff HEAD empty). Tests import src/, so no dist leg. (A) memory gate removed: 55 failed / 24 passed of 79. (B) mongodb walk gate removed, emitter check kept: 1 failed / 9 passed; only the identity-settled case, as predicted. (C) walk + emitter removed: 9 failed / 1 passed; only the true/false control. B's first attempt was an empty operation: the replacement contained the anchor, the tool refused, nothing was measured; re-run with a new anchor. [eslint] Narrowed to the 5 changed .ts files, --no-inline-config: 5 files / 0 errors / 0 warnings (from --format json). Population: each file resolves --print-config. Invariance: eslint.config.mjs enables no type-aware linting (no parserOptions.project), so untouched files' verdicts cannot move. [conformance ledger] node scripts/check-driver-conformance.mjs: 50 covered / 0 DEBT / 0 exempt, both before and after.",
    "mcp_calls": "0",
    "api_writes": "3, each one repository_dispatch through the fleet-write relay, executed as objectstack-fleet[bot]: (1) pr_create, POST /repos/objectstack-ai/objectstack/pulls, giving #20979; read-back 11757 bytes identical. (2) label-write assign, POST /repos//issues/20979/assignees with os-litant; read-back matches; zero label writes, because the dispatch named no label names and skip-changeset does not apply with a changeset present. (3) this os-dev-report comment, POST /repos//issues/20897/comments. Plus git push of the branch, which is not REST.",
    "open_questions": [
    {
    "question": "The standing increment asks for the $exists refusal in shared conformance coverage. No spec case-set carries a driver-level refusal verdict for the flags: FILTER_LOGIC_CASES holds rows only, and FILTER_COMPARAND_TYPE_CASES' refusal verdict is the upstream parseFilterAST door. Enrol one?",
    "options": [
    "A: no new table; the refusal stays pinned per driver (sql, sqlite-wasm, turso parity, mongodb, memory), and memory's cross-face invariant (every entry refuses or equals find()) lands here",
    "B: add a refusal verdict for non-boolean $null / $exists / $empty to a packages/spec case-set that every driver consumes (a spec type change and five driver suites)"
    ],
    "recommendation": "A. Business need: every driver face now refuses and is pinned, and no producer is measured writing a non-boolean flag past the save door. Long-term: B is the sound shape once a second flag-shaped operator arrives. AI-error axis: both refuse loudly today. Startup scope: B widens a spec table and five suites for a rule that holds everywhere now. Revisit if a new driver arrives."
    }
    ],
    "out_of_scope_findings": [
    "class: a · reach: engine.aggregate, measured on driver-memory AND driver-sql better-sqlite3 (HTTP not measured) · evidence: objectql having-filter.ts checkCondition reads $exists as exists !== !!target, so the aggregation filter { name: { $exists: 'false' } } sums the VALUED rows (s=1) and having { name: { $exists: 'false' } } returns the valued group 'won'. 'yes' and 1 give the same, 0 / null the no-value side. It is not refused on any driver, SQLite included, because the engine evaluates it in-process after the driver. The $null twin on the same two faces DROPS the constraint: $null 'yes' / 1 / 'false' / 0 / null return every row (s=3) and both groups, which is the widening direction. assertConditionIsEvaluable already refuses a non-boolean $empty there, not $null / $exists. Same family as the fenced having-filter serial (#20822 F8, then #20873): route into that family card, not a single-point card · dedupe words: having non-boolean $exists truthiness · aggregation filter $null non-boolean constraint dropped · assertConditionIsEvaluable flags $null $exists",
    "carrier: #20869's in-flight PR on packages/formula/src/matches-filter.ts (or the #20822 F7 family) · noted, not filed: matchesFilterCondition reads $exists / $null by identity (v === true) and refuses neither, so $exists 'yes' / 1 / 'false' / 0 / null all match the no-value row. This face is total by contract, and no producer is measured emitting a non-boolean flag into it",
    "carrier: #20874 (seat 2, same round, owns memory-driver.ts) · noted, not filed: normalizeFieldOperators' $exists arm has no local totality check (the $null arm has one). After this PR's gate it is unreachable with a non-boolean, so it is dead-code asymmetry",
    "carrier: whoever lands the having-filter flag refusal (#20822 F8 / #20873) · noted, not filed: the filter.zod.ts save-door docblock ('Every query face refuses a non-boolean $null / $exists flag') and filter-save-door-refusals.ts ('refused on every query face') now hold for every driver, but still overstate objectql's aggregation filter / having and formula"
    ],
    "gates": {
    "head": "da4feaf8",
    "derived_by": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths; 6 paths vs merge base f6ccca4)",
    "derived": 60,
    "ran_exit_0": 58,
    "not_measured": [
    "pnpm check:dual-build-cjs-loads: exit 3, PREREQUISITE NOT MET (reads every package's dist; 62 packages unbuilt in this partial build); declared to CI",
    "pnpm check:type-check-debt: exit 3, PREREQUISITE NOT MET (re-measure needs the full build closure); declared to CI"
    ],
    "reconciliation": "node scripts/pm/dispatch-gates.mjs --ran ran.list: 60 derived famil(ies) accounted for, 58 run, 2 NOT-MEASURED (exit 0)",
    "note": "node scripts/check-plugin-teardown-shape.mjs --self-test first exited 3 (pinned fixture 621a487 outside the shallow clone). After git fetch --depth=1 of that one commit it exited 0; ran.list records the rerun.",
    "ci_at_report": "31 check runs on da4feaf: 14 completed with 0 failures, 17 in_progress. Not awaited."
    },
    "deviations": [
    "Shared conformance coverage (standing increment): not enrolled in a packages/spec case-set, because none carries a driver-refusal verdict and the spec tables are outside the claimed surface. The multi-face invariant is pinned in driver-memory (every entry refuses, and true/false equal find()); see open_questions[0].",
    "Zone 3 engine.find pins: written at driver level, where the refusal lives (as $null's pins are). driver-memory and driver-sql take no @objectstack/objectql dependency. The engine pass-through was measured by a scratch script on both drivers (engine.find refuses on memory and SQLite after this PR).",
    "SQLite pins: not duplicated. driver-sql is fenced, and its existing [#5369] block pins 'yes', 1, 0, null, undefined, {} and 'false' refused, with a true/false control.",
    "Commit trailers use AGENTS.md's model-free pair (Claude-Session + Co-authored-by: Claude), not the harness reminder's model-named Co-Authored-By; the PR footer is os-dev.md's session-URL form.",
    "git fetch --depth=1 origin 621a487 added one commit to the shared object store (additive), so a self-test could reach its pinned fixture.",
    "origin/main moved 9 commits after f6ccca4, none touching driver-memory, driver-mongodb or spec/src/data. Not merged before the PR; the CI merge ref validates."
    ],
    "files_changed": [
    ".changeset/20897-exists-non-boolean-refused.md",
    "packages/drivers/driver-memory/src/filter-refusal.ts",
    "packages/drivers/driver-memory/src/memory-exists-non-boolean-refusal.test.ts",
    "packages/drivers/driver-memory/src/memory-null-comparand-refusal.test.ts",
    "packages/drivers/driver-mongodb/src/mongodb-exists-non-boolean-refusal.test.ts",
    "packages/drivers/driver-mongodb/src/mongodb-filter.ts"
    ]
    }


    Generated by Claude Code

  5. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 20897,
    "status": "done",
    "branch": "claude/issue-20897-exists-non-boolean-refused",
    "pr": "#20979",
    "session": "session_01Ujdtvqs7ree7WyQmEDwEnG (subagent run; the id is the dispatching session's, as the commit trailers carry it)",
    "premise_still_valid": true,
    "summary": "Patch round 1, three commits on the same branch; final head 378effc. (1) CI red at da4feaf (Test Core 5/6, @objectstack/client envelope-caller-census): the new memory suite's two analytics.query( calls on its own MemoryAnalyticsService are real call sites in that repo-wide census. Its prescription is to classify them, so LEDGER gains a NOT_SDK row (receiver service, count 2), and the two exact-count controls move with it (service receivers 1 to 3 with the file list pinned; NOT_SDK split 1 to 3). Nothing is exempted. (1b) The census therefore reads that driver-memory file, and check:cross-package-test-inputs went red until it was declared. I chose option (a): scripts/cross-package-test-inputs.mjs names the one file in @objectstack/client's globs, mirrored into turbo.json's @objectstack/client#test inputs. Per-file, not packages/, for the price the census header records. Option (b) has no sanctioned path: the census header names no exclusion for driver-package tests (SKIP_DIRS is build output only), and its receiver split exists to classify producer calls as NOT_SDK, which is what the ledger row does. (2) The changeset is rewritten as an accept-set narrowing: both packages minor, 'fix(driver-memory, driver-mongodb)!:', 'Clause-②: no (narrowing)', a BREAKING paragraph that keeps 'write the boolean itself', and one ADR-0087 marker: not-required (already-registered filter-query-face-comparands-refused-at-save). That registered entry's reason already states every query face refuses a non-boolean $null / $exists flag, and its replacement is this change's migration. The body carries that prescription, which a no-migration-prescription claim would contradict. (3) Open question A noted; no action.",
    "tests": "final head 378effc. [census] pnpm --filter @objectstack/client exec vitest run --maxWorkers=2 src/envelope-caller-census.test.ts: 20 passed. Reverse leg: the census file restored from da4feaf (git restore --source, tree only), run against the new test: 2 failed / 18 passed, the same two cases CI named ('separates the producer-service receiver…', 'the mechanical enumeration and the hand ledger agree…'). Restore proven: blob == HEAD, git diff HEAD empty, status clean. [client] pnpm --filter @objectstack/client run typecheck && test: exit 0, 50 files / 641 passed. [cross-package] pnpm check:cross-package-test-inputs: exit 1 at 3682d8f (reproduced CI's 'named in packages/client/src/envelope-caller-census.test.ts'); after the glob alone, still exit 1 ('turbo.json @objectstack/client#test inputs are missing the declared glob'); after the turbo.json mirror, exit 0 ('29 package(s) read outside themselves, all declared'). node scripts/check-ci-filter-parity.mjs: exit 0. [changeset] node scripts/check-adr-0087-registration.mjs --base origin/main: exit 0 ('1 declared-breaking changeset … not-required (already-registered)'). node scripts/check-changeset-no-major.mjs --base origin/main: exit 0. node scripts/check-empty-changeset.mjs --base origin/main: exit 0. pnpm check:changeset-gate-self-tests: exit 0. Level axis driven offline with --event: the current body ('Clause-②: no') exit 0, and the proposed body ('Clause-②: no (narrowing)') exit 0 ('no package whose packages/
    /src/** it moves is graded patch'). [eslint] Narrowed to the 7 changed code files (6 .ts + scripts/cross-package-test-inputs.mjs), --no-inline-config: 7 files / 0 errors / 0 warnings from --format json; all 7 resolve --print-config; no type-aware linting in eslint.config.mjs, so untouched files' verdicts cannot move. turbo.json parses. [unchanged since da4feaf] driver-memory and driver-mongodb sources and tests: last round's 1500 / 671 passes and the A/B/C ablations stand. [conformance ledger] untouched this round (no conformance file changed): 50 covered / 0 DEBT / 0 exempt.",
    "mcp_calls": "0",
    "api_writes": "1: this os-dev-report comment, POST /repos//issues/20897/comments through the fleet-write relay as objectstack-fleet[bot]. Plus git push of three commits (9df5e06, 3682d8f, 378effc), which is not REST. No PR body write.",
    "open_questions": [],
    "out_of_scope_findings": [
    "carried unchanged from os-dev-report 5921891523: class a · objectql having-filter aggregation filter / having read $exists by truthiness and drop a non-boolean $null constraint, on every driver (engine.aggregate, memory and SQLite measured) · route into #20822 F8 / #20873",
    "carried unchanged: carrier #20869 (formula matches-filter identity read of $exists / $null) · carrier #20874 (memory-driver.ts $exists arm, no local totality check) · carrier: whoever lands the having-filter flag refusal (filter.zod.ts / filter-save-door-refusals.ts 'every query face refuses' prose) · noted, not filed"
    ],
    "pr_body_edits": [
    {
    "replace": "Clause-②: no",
    "with": "Clause-②: no (narrowing)",
    "where": "line 2, the declaration line (whole line)"
    },
    {
    "replace": "Why the declaration is no: FieldOperatorsSchema declares $exists: z.boolean(), so this pulls two faces back to the declared contract. It reuses the existing INVALID_FILTER code and driver-sql's existing wording, and it adds no key, code or accepted shape.",
    "with": "Why the declaration is no (narrowing): FieldOperatorsSchema declares $exists: z.boolean(), so this pulls two faces back to the declared contract. It reuses the existing INVALID_FILTER code and driver-sql's existing wording, and it adds no key, code or accepted shape. It does narrow what driver-memory and driver-mongodb accept: a filter they used to answer is now refused. The changeset declares it BREAKING, minor for both packages under the launch-window convention for accept-set narrowings, with the ADR-0087 disposition not-required (already-registered filter-query-face-comparands-refused-at-save). That registered entry's reason already states that every query face refuses a non-boolean flag, and its replacement is this change's migration.",
    "where": "line 6, reads false now (says the declaration is plain no)"
    },
    {
    "replace": "## Verification (head da4feaf8)",
    "with": "## Verification (driver changes at da4feaf8; final head 378effc8)",
    "where": "heading, line 52"
    },
    {
    "replace": "- Gates: node scripts/pm/dispatch-gates.mjs --commands after the last commit derived 60 families. --ran reconciles them as 60 accounted for: 58 run and exit 0, and 2 NOT MEASURED. The first NOT MEASURED is check:dual-build-cjs-loads, which reads every package's dist/. The second is check:type-check-debt, whose re-measure needs the whole build closure. Both exit 3 (PREREQUISITE NOT MET) on this partial build and are declared to CI. check-plugin-teardown-shape --self-test first exited 3 because its pinned fixture commit was outside the shallow clone. I fetched that one commit and it ran green.",
    "with": "- Gates: node scripts/pm/dispatch-gates.mjs --commands after the last commit (378effc8) derived 79 families. --ran reconciles them as 79 accounted for: 77 run and exit 0, and 2 NOT MEASURED. Those two are check:dual-build-cjs-loads, which reads every package's dist/, and check:type-check-debt, whose re-measure needs the whole build closure. Both exit 3 (PREREQUISITE NOT MET) on this partial build and are declared to CI. check:skill-examples first exited 3 for want of the client-react build, and ran green once that closure was built.",
    "where": "line 63, stale counts and head"
    },
    {
    "replace": "- ESLint, narrowed to the 5 changed .ts files with --no-inline-config: 5 files, 0 errors, 0 warnings (counted from --format json). Each file resolves a config (--print-config). eslint.config.mjs enables no type-aware linting (no parserOptions.project), so this diff cannot move the verdict on an untouched file.",
    "with": "- ESLint, narrowed to the 7 changed code files (the 6 .ts files and scripts/cross-package-test-inputs.mjs) with --no-inline-config: 7 files, 0 errors, 0 warnings (counted from --format json). Each file resolves a config (--print-config). eslint.config.mjs enables no type-aware linting (no parserOptions.project), so this diff cannot move the verdict on an untouched file.",
    "where": "line 64, stale file count"
    },
    {
    "insert_before": "## Acceptance notes",
    "text": "## Outside the drivers: the envelope caller census\n\nThe new memory suite calls analytics.query( twice on its own MemoryAnalyticsService. @objectstack/client's envelope-caller-census.test.ts walks the whole repo for that call shape, so CI went red at da4feaf8 (Test Core (5/6)). The census prescribes classifying every counted site, and its receiver split already has the class for a producer call. So LEDGER gains one NOT_SDK row (count 2, receiver service), and its two exact-count controls move with it: the service-receiver control now expects 3 sites and pins their files, and the verdict split reads 3 not-SDK. Nothing is exempted or loosened. The census as it stood at da4feaf8 goes red locally on the same two cases CI named. At 378effc8 it is 20/20 green, and the client package's tests (641) and typecheck pass.\n\nThe census reads that driver-memory file, so pnpm check:cross-package-test-inputs requires it declared. scripts/cross-package-test-inputs.mjs names the one file in @objectstack/client's globs, and turbo.json mirrors it into @objectstack/client#test inputs, so a change to that suite re-runs the census. It is per-file, not packages/**, for the price the census header records. check:ci-filter-parity passes, because core already covers the path.\n\n",
    "where": "new section; the body names no client, scripts or turbo.json change today"
    }
    ],
    "gates": {
    "head": "378effc8",
    "derived_by": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths; 9 paths vs merge base f6ccca4)",
    "derived": 79,
    "ran_exit_0": 77,
    "not_measured": [
    "pnpm check:dual-build-cjs-loads: exit 3, PREREQUISITE NOT MET (reads every package's dist); declared to CI",
    "pnpm check:type-check-debt: exit 3, PREREQUISITE NOT MET (re-measure needs the full build closure); declared to CI"
    ],
    "reconciliation": "node scripts/pm/dispatch-gates.mjs --ran ran3.list: 79 derived famil(ies) accounted for, 77 run, 2 NOT-MEASURED (exit 0)",
    "note": "pnpm --filter @objectstack/spec run check:skill-examples (newly derived this round) first exited 3 (no client-react / spec dist in the recreated worktree); after building the client-react closure it exited 0 (259 examples type-check); ran3.list records the rerun. 18 families are new since da4feaf (the scripts/** and turbo.json edits): check-declaration-mirrors, check-scripts-symbol-anchors, check-self-test-wired, check-self-test-workflow-commands, check-whole-set-label-write (each with --self-test), check:agent-test-spelling, check:bash32-floor, check:cli-command-ids, check:entry-guard, check:parse-guard, check:pnpm-filter-targets, check:ratchet-remedy-authority, check:turbo-task-graph; all exit 0.",
    "ci_at_report": "not read this round; not awaited"
    },
    "deviations": [
    "Files outside the claim's surface, named for the claim amendment: packages/client/src/envelope-caller-census.test.ts (one LEDGER row, two exact-count controls moved to the new exact numbers, one header sentence); scripts/cross-package-test-inputs.mjs (one per-file glob under @objectstack/client); turbo.json (its mirror in @objectstack/client#test inputs). The last two were required by check:cross-package-test-inputs once the census row named the file.",
    "ADR-0087 marker category: already-registered, not the no-migration-prescription the 4b4ee88 precedent used. The id filter-query-face-comparands-refused-at-save exists at the merge base, and the gate verifies it. It is the honest fit because this changeset keeps a migration prescription that the registered entry's replacement already records.",
    "Worktree recreated as instructed and rebuilt (objectql / driver-memory / driver-sql / driver-mongodb / client closures, then client-react) so the dist-reading gates measured rather than ran on an empty build.",
    "The PR body was not touched; pr_body_edits carries the exact replacements for the seat."
    ],
    "files_changed": [
    ".changeset/20897-exists-non-boolean-refused.md",
    "packages/client/src/envelope-caller-census.test.ts",
    "packages/drivers/driver-memory/src/filter-refusal.ts",
    "packages/drivers/driver-memory/src/memory-exists-non-boolean-refusal.test.ts",
    "packages/drivers/driver-memory/src/memory-null-comparand-refusal.test.ts",
    "packages/drivers/driver-mongodb/src/mongodb-exists-non-boolean-refusal.test.ts",
    "packages/drivers/driver-mongodb/src/mongodb-filter.ts",
    "scripts/cross-package-test-inputs.mjs",
    "turbo.json"
    ]
    }


    Generated by Claude Code

  6. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1 (amendment of claim 5921382209: same session, same branch; the file surface follows the patch-round report 5922406027)
    Session: session_01Ujdtvqs7ree7WyQmEDwEnG
    Account: os-litant (the seat's linked user as GET /user answers it; always the card's assignee)
    Branch: claude/issue-20897-exists-non-boolean-refused
    Worktree: objectstack-issue-20897
    Domain: domain:engine
    Seat: domain:engine#2 (seat post #20966; runs the lane queue by the maintainer's order recorded on #6367)
    File surface, as delivered in PR #20979 (head 378effc8): the original surface plus three declared additions.

  7. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    ContributorAuthor

    Correction to claim amendment 5922434301: an in-flight overlap on the census files, and the main sha

    domain:engine#2 · session_01Ujdtvqs7ree7WyQmEDwEnG · 2026-10-01T00:48Z.

  8. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    ContributorAuthor

    Id fix for 5922450072 (domain:engine#2 · session_01Ujdtvqs7ree7WyQmEDwEnG · 2026-10-01T00:49Z): the claim amendment it corrects is 5922434933. The 5922434301 in its title names no comment.

  9. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT — PR #20979 @ 378effc8 (a non-boolean $exists is refused on driver-memory and driver-mongodb, as on every other driver)

    domain:engine#2 (seat post #20966) · session_01Ujdtvqs7ree7WyQmEDwEnG · 2026-10-01T01:00Z. Judged against GitHub, not the reports (5921891523, 5922406027).

  10. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 20897,
    "status": "done",
    "branch": "claude/issue-20897-exists-non-boolean-refused",
    "pr": "#20979",
    "session": "session_01Ujdtvqs7ree7WyQmEDwEnG (subagent run; the id is the dispatching session's, as the commit trailers carry it)",
    "premise_still_valid": true,
    "summary": "Merge round: origin/main (f8178ff, 19 commits past 378effc, among them 8d329f0 / PR #20916 and f8178ff / PR #20984) merged into the branch as merge commit 94daed8 (two parents; no rebase, no force-push), pushed; head 94daed8. Three files conflicted, every hunk because 8d329f0 declared its own census row in the same three places. Each was resolved so both declarations survive (hunks listed under 'merge'). Nothing beyond the conflict resolution changed: the six non-conflicted branch files are byte-identical to 378effc, and against origin/main the three conflicted files differ only by this branch's additions. The merged ledger recomputes mechanically to PAYLOAD_DEPENDENT 18, RESULT_INSENSITIVE 10, NOT_SDK 8 (1 + 5 + 2), service receivers 8, total 36. #20984 (f8178ff) changed memory-driver.ts and memory-analytics.ts. The gate still runs before both $exists readers: convertToMongoQuery calls assertFilterConditionShape before normalizeFilterCondition (whose normalizeFieldOperators holds the $exists arm), every query entry still routes through convertToMongoQuery, and the cube face's normalizeFilters calls the gate before flattenFilterCondition (whose set lowering and SQL echo read the flag). driver-memory's full suite passes on the merge.",
    "merge": {
    "commit": "94daed863",
    "parents": "378effc8c (branch) + f8178ff (origin/main)",
    "conflicts": [
    "packages/client/src/envelope-caller-census.test.ts hunk 1 (LEDGER, after the analytics-automation-json-erasure NOT_SDK row): main added the rest row (packages/rest/src/analytics-nested-relation-filter.test.ts, analytics.query, service, count 5, NOT_SDK); this branch added the driver-memory row (count 2). Resolved: both rows, main's first, each with its own comment line, text unchanged.",
    "packages/client/src/envelope-caller-census.test.ts hunk 2 (§2 'separates the producer-service receiver from the SDK receiver'): main had length 6 and the sorted file set of 2 files; this branch had length 3 and a per-site file list. Resolved: length 8, main's form (sorted unique file set) holding all three files (client erasure, driver-memory, rest), plus this branch's assertion that every service site is analytics.query, and the #20897 comment re-worded from 'Two producer faces' to 'Three'. Form note: the per-site 3-entry list became the 3-file set. The per-file site counts stay pinned by §3, whose key-for-key comparison against the ledger checks each count (5 and 2).",
    "packages/client/src/envelope-caller-census.test.ts hunk 3 (§3 'records the split'): main 6 not-SDK, this branch 3. Resolved: 8, with the title '18 payload pins, 10 result-insensitive, 8 not-SDK'; recomputed from the merged LEDGER, not by hand.",
    "scripts/cross-package-test-inputs.mjs (the @objectstack/client globs, after 'scripts/'): main added the rest per-file glob with its comment; this branch added the driver-memory per-file glob. Resolved: both, main's first. This branch's comment opened 'ONE file under packages/', which is now false with two; it is re-worded to 'Declared by name for the same reason', and its last line names 'the scripts/ entry' instead of 'the entry above'. The glob string is unchanged.",
    "turbo.json (@objectstack/client#test inputs, after $TURBO_ROOT$/scripts/**): main's rest input and this branch's driver-memory input. Resolved: both, main's first, with the comma JSON needs; the file parses."
    ],
    "beyond_conflict_resolution": "none. The only edits beyond the union are the three comment re-wordings named above, each inside its conflict hunk."
    },
    "tests": "head 94daed8. [census] pnpm --filter @objectstack/client exec vitest run --maxWorkers=2 src/envelope-caller-census.test.ts: exit 0, 20 passed. [client] pnpm --filter @objectstack/client run typecheck && test: exit 0, 50 files / 641 passed. [cross-package] pnpm check:cross-package-test-inputs: exit 0 ('OK: 29 package(s) read outside themselves, all declared, and turbo.json hashes every declared glob'). node scripts/check-ci-filter-parity.mjs: exit 0 ('all 189 declared cross-package glob(s) (136 unique) are covered'). pnpm check:turbo-task-graph: exit 0. pnpm check:nul-bytes: exit 0. [changeset] node scripts/check-adr-0087-registration.mjs --base origin/main: exit 0. node scripts/check-changeset-no-major.mjs --base origin/main: exit 0. node scripts/check-empty-changeset.mjs --base origin/main: exit 0. [driver-memory] The merge brought f8178ff into this package. Closure rebuilt first (driver-memory, driver-mongodb, client, client-react, with their deps incl. spec, whose filter.zod.ts the merge moved). pnpm --filter @objectstack/driver-memory test: exit 0, 69 files / 1613 passed. The three relevant files alone (memory-exists-non-boolean-refusal, memory-null-comparand-refusal, memory-20874-contains-membership): 3 files / 192 passed. [driver-mongodb] Not touched by the merge, but its spec dependency moved, so it was re-run: typecheck exit 0; test 30 passed / 5 skipped (opt-in live mongod), 671 passed / 172 skipped. driver-memory typecheck: exit 0. [eslint] The 2 code files the merge resolved (census test, scripts/cross-package-test-inputs.mjs), --no-inline-config: 2 files / 0 errors / 0 warnings. [not re-run] The full dispatch-gates union was not re-derived this round. The round named its proofs, and the branch delta against main is the same 9 files as at 378effc, where 79 were derived and reconciled.",
    "mcp_calls": "0",
    "api_writes": "1: this merge-round os-dev-report comment, POST /repos//issues/20897/comments through the fleet-write relay as objectstack-fleet[bot]. Plus git push of merge commit 94daed8 (not REST). No PR body, label, ready or auto-merge write.",
    "open_questions": [],
    "out_of_scope_findings": [
    "carried unchanged from os-dev-report 5921891523: class a · objectql having-filter aggregation filter / having read $exists by truthiness and drop a non-boolean $null constraint, on every driver · route into #20822 F8 / #20873",
    "carried unchanged: carrier #20869 (formula matches-filter) · carrier #20874 (memory-driver.ts $exists arm has no local totality check; #20984 landed without one, still unreachable behind the gate) · carrier: the having-filter flag-refusal landing (spec 'every query face refuses' prose) · noted, not filed"
    ],
    "gates": {
    "head": "94daed863",
    "ran": [
    "pnpm --filter @objectstack/client exec vitest run --maxWorkers=2 src/envelope-caller-census.test.ts :: exit 0",
    "pnpm check:cross-package-test-inputs :: exit 0",
    "node scripts/check-ci-filter-parity.mjs :: exit 0",
    "node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0",
    "node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0",
    "node scripts/check-empty-changeset.mjs --base origin/main :: exit 0",
    "pnpm check:turbo-task-graph :: exit 0",
    "pnpm check:nul-bytes :: exit 0",
    "pnpm --filter @objectstack/client run typecheck && pnpm --filter @objectstack/client test :: exit 0",
    "pnpm --filter @objectstack/driver-memory test :: exit 0",
    "pnpm --filter @objectstack/driver-memory --filter @objectstack/driver-mongodb run typecheck && pnpm --filter @objectstack/driver-mongodb test :: exit 0"
    ],
    "not_measured": [
    "the full dispatch-gates union at 94daed8 (not re-derived this round; see tests)"
    ],
    "ci_at_report": "PR #20979 at 94daed8: draft false, mergeable_state blocked (checks running). Not awaited, not touched."
    },
    "deviations": [
    "The newest Claim on the card is now 5922434933, an amendment of 5921382209 (same session, same branch, surface extended to the census, scripts and turbo.json files). It names this branch, so I proceeded.",
    "Worktree recreated from the pushed branch and rebuilt for this round. It is removed again after the report comment and its read-back."
    ],
    "files_changed": [
    ".changeset/20897-exists-non-boolean-refused.md",
    "packages/client/src/envelope-caller-census.test.ts",
    "packages/drivers/driver-memory/src/filter-refusal.ts",
    "packages/drivers/driver-memory/src/memory-exists-non-boolean-refusal.test.ts",
    "packages/drivers/driver-memory/src/memory-null-comparand-refusal.test.ts",
    "packages/drivers/driver-mongodb/src/mongodb-exists-non-boolean-refusal.test.ts",
    "packages/drivers/driver-mongodb/src/mongodb-filter.ts",
    "scripts/cross-package-test-inputs.mjs",
    "turbo.json"
    ]
    }


    Generated by Claude Code

  11. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed — PR #20979 as a3dc8171c (the card closes)

    domain:engine#2 (seat post #20966) · session_01Ujdtvqs7ree7WyQmEDwEnG · 2026-10-01T03:25Z.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:apiThe API a customer can call, and integrations — REST, connectors, webhooks, jobsbugSomething isn't workingdomain:enginepriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions