Skip to content

driver-mongodb: field unique is built as a sparse index, so two explicit null values collide, unlike the NULL-distinct rule of driver-sql and driver-memory #29278

Closed

Description

@objectstack-fleet

domain:server#3 · os-steve · session_01ADHkmQJdgarUjPLNgFBY67

Filing gate: ① out-of-scope finding of the #29176 dev (os-dev-report 6575284496, out_of_scope_findings class b), read from source, NOT MEASURED (the dev's container could not download mongod). Filed by domain:server seat 3. ⛔ Not a claim. Suggested-grade: p3 · a driver diverges from the shared unique rule; no grant or disclosure.

Reach: a deployment on driver-mongodb with an optional field declared unique, when two records hold no value in it.
Reader who acts: a domain:server seat (packages/drivers/driver-mongodb/src/mongodb-schema.ts).

What happens (source reading)

Wanted

Measure on a real mongod first (the conformance harness). If confirmed, build the unique index so missing and null are both excluded (for example a partialFilterExpression on the field's type, or the driver omitting null fields on write), and add the case to the driver conformance ledger so all drivers answer it alike.

Dedupe: searched mongodb sparse unique null, mongo unique index null collide; none. New.

Links back: #29176, PR #29271.

Activity

  1. added
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    on Nov 2, 2026
  2. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: p3 · domain:server · pm:queue · 2026-11-02T10:56Z

    ⛔ Not a claim.

    • Read fresh. Read from source, not measured: driver-mongodb builds unique as a sparse index, which still indexes an explicit null, so two null rows collide where driver-sql and driver-memory treat NULLs as distinct. One driver diverges from the shared rule; nothing is granted or disclosed. p3. Measure on a real mongod first, then add the case to the driver conformance ledger.

    Generated by Claude Code

  3. added
    domain:serverDomain lane — seat card indexed by label:pm:seat
    pm:queueReady for the PM dispatch loop
    and removed
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    on Nov 2, 2026
  4. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#1 · os-steve · session_01ADHkmQjPiXgNoSaEpqeGvY

    Claim: PM loop round 3 · 2026-11-02T11:18Z
    Session: session_01ADHkmQjPiXgNoSaEpqeGvY (seat); dev = card thread (session named in its first report)
    Account: os-steve
    Branch: the card thread's harness-pinned branch, named in its first report (it names no issue, so this claim is the identity record)
    Worktree: the card thread's own
    Domain: domain:server
    Seat: domain:server#1
    File surface: packages/drivers/driver-mongodb/src/mongodb-schema.ts (and its index-sync site), driver tests, the driver conformance case, a @objectstack/driver-mongodb changeset. ⛔ No packages/spec/**, no driver-sql / driver-memory behaviour change, no new error code; ⛔ no fix shipped without a real mongod measurement. (stop on breach; explain in the report)
    Container & model: S, mode:thread, model as the thread was opened, effort: medium
    Clause-②: yes (widening) (expected: two explicit null values in an optional unique field, refused on Mongo today, are admitted as on sql and memory; the dev measures)
    Metadata-protocol: none (expected)
    Responsibility: the code is the driver-mongodb unique index · the platform path is the shared NULL-distinct unique rule · reached by every Mongo deployment with an optional unique field
    Thread-read: 6575398009
    Premise: as filed; the dev re-measures on current main first
    Serial constraints cleared: no open PR of this seat touches driver-mongodb; the dev reads open PRs on the package before the first write

    Orders: the seat's standard order plus the card delta, in the project's shared folder (server-seat1/standard-order.md, server-seat1/29278-delta.md).

  5. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Nov 2, 2026
  6. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#1 · os-steve · session_01ADHkmQjPiXgNoSaEpqeGvY

    Claim: PM loop round 3 (revised: branch and dev session named) · 2026-11-02T11:21Z
    Session: session_01ADHkmQjPiXgNoSaEpqeGvY (seat); dev = card thread session_01ADHkmRc2jLWPKqPfVHStXw
    Account: os-steve
    Branch: claude/project-thread-rmcblv (the card thread's harness-pinned branch; it names no issue, so this claim is the identity record)
    Worktree: the card thread's own
    Domain: domain:server
    Seat: domain:server#1
    File surface: packages/drivers/driver-mongodb/src/mongodb-schema.ts (and its index-sync site), driver tests, the driver conformance case, a @objectstack/driver-mongodb changeset. ⛔ No packages/spec/**, no driver-sql / driver-memory behaviour change, no new error code; ⛔ no fix shipped without a real mongod measurement. (stop on breach; explain in the report)
    Container & model: S, mode:thread, model as the thread was opened, effort: medium
    Clause-②: yes (widening) (expected: two explicit null values in an optional unique field, refused on Mongo today, are admitted as on sql and memory; the dev measures)
    Metadata-protocol: none (expected)
    Responsibility: the code is the driver-mongodb unique index · the platform path is the shared NULL-distinct unique rule · reached by every Mongo deployment with an optional unique field
    Thread-read: 6575398009
    Premise: as filed; the dev re-measures on current main first
    Serial constraints cleared: no open PR of this seat touches driver-mongodb; the dev reads open PRs on the package before the first write

    Orders: the seat's standard order plus the card delta, in the project's shared folder (server-seat1/standard-order.md, server-seat1/29278-delta.md).

  7. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#1 · os-steve · session_01ADHkmQjPiXgNoSaEpqeGvY

    Hold · 2026-11-02T11:40Z · claim 6575878708 withdrawn

    This card is a driver-mongodb defect fix, and driver-mongodb is under the maintainer's investment freeze (#5499: "driver-mongodb 暂无客户"; new driver-memory / driver-mongodb cards go straight to pm:on-hold citing #5499, not to pm:queue). It entered the queue by mistake and this seat dispatched it without checking. The card thread stopped at Step 0 with zero writes (no mongod reachable from the container), so nothing was built.

    Moved pm:dispatched → pm:on-hold. Wake condition, verbatim from #5499:

    Restart-when: git grep -l -E 'driver-memory|InMemoryDriver' origin/main -- 'packages/**/*.test.*' 'packages/**/__tests__/**' 'packages/qa/**' '.github/workflows/**' ':!packages/drivers/driver-memory/**' ':!**/CHANGELOG.md' returns 0
    
  8. added
    pm:on-holdDecision made, deliberately deferred — no dispatch, no nag; restart condition in the hold comment
    and removed
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    on Nov 2, 2026
  9. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#1 · os-steve · session_01ADHkmQjPiXgNoSaEpqeGvY

    Hold withdrawn · 2026-11-02T11:45Z · correction of 6576235761

    The hold 6576235761 was wrong. The #5499 freeze was lifted for driver-mongodb on 2026-08-11T00:00Z, as origin/main records in packages/spec/src/data/aggregation-conformance.ts (head note: "the maintainer lifted the #5499 investment freeze for driver-mongodb on 2026-08-11") and in packages/drivers/driver-mongodb/src/test-mongod.ts ("The premise that is GONE is #5499 … It dissolved 2026-08-11"). This seat read the #5499 body and missed the lift.

    Back to pm:dispatched, assignee os-steve. Claim 6575878708 (revised: branch claude/project-thread-rmcblv, dev session_01ADHkmRc2jLWPKqPfVHStXw) stands again. The dev measured the premise on a real mongod 8.3.7 (two null → E11000, two missing → admitted, two '' → E11000) and resumes the fix.

  10. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:on-holdDecision made, deliberately deferred — no dispatch, no nag; restart condition in the hold comment
    on Nov 2, 2026
  11. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#1 · os-steve · session_01ADHkmRc2jLWPKqPfVHStXw
    os-dev-report

    {
      "issue": 29278,
      "status": "done",
      "branch": "claude/project-thread-rmcblv",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/29339",
      "session": "session_01ADHkmRc2jLWPKqPfVHStXw",
      "premise_still_valid": true,
      "summary": "Premise MEASURED on a real mongod 8.3.7 (conda-forge build, supplied via MONGOMS_SYSTEM_BINARY; fastdl.mongodb.org is 403 here; Steve approved running it): on base the sparse unique index refuses a second null (E11000) and admits two missing. Field-level and declared indexes[] unique indexes are now partial over every non-null BSON type (no sparse), with a plain read index idx_FIELDS beside each because the planner COLLSCANs an equality lookup on a partial index. A legacy sparse unique index the driver built is rebuilt at the next schema sync (drop + create under its name, measured); any other same-name index is left alone. memory, sql and mongo now answer the two-nulls case alike.",
      "tests": "new src/mongodb-unique-null-distinct.test.ts (7 recording-Db + 8 real-mongod): base 276ca307 8 failed/7 passed, head 84fb196d 15/15. driver-mongodb full suite with real mongod 38 files / 911 tests passed, default lane 721 passed 190 skipped. Ablations via ablation-replace.mjs (suite reads src, no dist leg): A1 unique:false 6 red; A2 any same-name index read as legacy 2 red; A3 no read index 3 red; each restored blob==HEAD, git diff HEAD empty. Cross-driver probe on built dist (head 84fb196d): null x2, missing x2, (eu,null) x2 admitted on all three; '' x2, 'X' x2, (eu,t) x2 refused on all three.",
      "gates": "head e38ab227: dispatch-gates --commands 60 families, 60 x exit 0 (check-plugin-teardown-shape --self-test first exit 3, prerequisite; re-run exit 0 after git fetch --depth=1 origin 621a4876); --ran: 60 derived, 60 run, 0 NOT-MEASURED, 0 UNRUN. pnpm --filter @objectstack/driver-mongodb typecheck exit 0. pnpm exec eslint --no-inline-config packages/drivers/driver-mongodb exit 0. check:driver-conformance 66 covered / 0 DEBT / 0 exempt on base and head. PR check-runs: in_progress at report time.",
      "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm",
      "mcp_calls": "4 — issue_read x2 (get, get_comments), pull_request_read get (#29271), list_pull_requests; no write tool",
      "api_writes": "2 — relay pr_create (#29339, run 41443883629) and this os-dev-report comment via post-stamped.mjs",
      "files_changed": [
        "packages/drivers/driver-mongodb/src/mongodb-schema.ts",
        "packages/drivers/driver-mongodb/src/mongodb-unique-null-distinct.test.ts",
        ".changeset/29278-mongodb-unique-null-distinct.md"
      ],
      "line_budget": "449 insertions, 12 deletions across 3 files (src 166, test 281, changeset 14); effort M (the index migration needed code)",
      "deviations": [
        "No shared conformance case-set: the case-sets live in packages/spec/src/data, which this lane does not edit; replaced by a measured three-driver probe (PR acceptance notes) and an open question.",
        "Declared indexes[] unique entries changed too (same file, same defect class): a single-column declaration converges on idx_FIELD_unique with the field-level route, so leaving it sparse would ask for one name with two option sets.",
        "A plain read index idx_FIELDS is added beside each unique index (measured COLLSCAN otherwise); not named in the delta.",
        "origin/main moved 27 commits past base 276ca307; none touch packages/drivers or packages/spec/src/data, so the branch was not merged before the PR.",
        "Harness asks to assign os-steve and request his review on the PR; skipped per os-dev (pr_create carries no assignees) and the no-MCP-write rule.",
        "The real-mongod readings depend on a conda-forge mongod binary in this container; the opt-in suite still runs on no CI lane (#5517)."
      ],
      "open_questions": [
        {
          "question": "Should the spec seat add a shared unique NULL-distinct conformance case-set so every driver enrolls in one standard?",
          "options": ["A: spec seat files a card for packages/spec/src/data/unique-conformance.ts; drivers enroll in a follow-up", "B: keep per-driver pins only"],
          "recommendation": "A, because the gate (check:driver-conformance) only enforces cross-driver agreement through a shared case-set; today the agreement is a one-off probe."
        },
        {
          "question": "Keep the extra plain read index beside each unique index?",
          "options": ["A: keep it (lookups by a unique field stay indexed; one more index per unique field to maintain on writes)", "B: drop it and accept COLLSCAN on lookups by a unique field"],
          "recommendation": "A, because B turns every lookup by email/SKU-style fields into a full collection scan, a worse regression than the p3 bug."
        },
        {
          "question": "Is the drop-then-create rebuild of a legacy index at schema sync acceptable, given a brief window without a unique index?",
          "options": ["A: as shipped (a duplicate written in the window fails the sync loudly with E11000)", "B: leave legacy indexes alone and log a warning naming the manual rebuild"],
          "recommendation": "A, because B leaves every deployed collection on the old refusal with no fix reaching it."
        }
      ],
      "security_findings": "none",
      "out_of_scope_findings": []
    }

    Stamped 2026-11-02T12:21Z.


    Generated by Claude Code

  12. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#1 · os-steve · session_01ADHkmQjPiXgNoSaEpqeGvY

    Landed · 2026-11-02T13:25Z

    Probe Reading
    PR #29339 MERGED 2026-11-02T13:25Z, merge commit 5924e5002f, through the merge queue
    #29278 closed completed by Fixes #29278

    pm:dispatched is removed. Seat records: review PASS 6577415377, ACCEPT 6577460267.

    The card's "Wanted" asked for a shared NULL-distinct unique conformance case-set. That set would sit in packages/spec/src/data, which is outside the server lane's claim. It is handed to the spec lane through the coordinator, and the case-set stays unbuilt until a spec seat takes it. driver-sql, driver-memory and driver-mongodb now agree on two null values, as measured on PR #29339.

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

Metadata

Metadata

Assignees

Labels

domain:serverDomain lane — seat card indexed by label:pm:seatpriority:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions