Uh oh!
There was an error while loading. Please reload this page.
fix(driver-mongodb): refuse the rejected alias reference_to at the schema door - #13481
Conversation
…schema door `syncCollectionSchema` gated its field-level join index on `field.reference_to`. `reference` is the only relationship spelling `@objectstack/spec` declares; `reference_to` is a rejected alias (`FieldSchema` answers `unrecognized_keys` for it on any field type, carrying any value). So one key had two doors with opposite answers, and the silent one was the one that touched the database. The door is stated ahead of `createCollection` and ahead of every per-field branch, matching the placement `sql-driver.ts` uses for its copy: the spec's verdict is gated on neither the field's type nor the key's value, so neither may gate the driver's. Predicate is `!== undefined`, identical to the SQL door. The join-index arm is left byte-identical and its `field.reference_to` conjunct is now unreachable, deliberately: deleting it would start building `idx_FIELD_lookup` for canonical `reference` lookups, which is a boot-time behaviour change for existing deployments and a separate, still-open ruling. Graded `minor`, not `patch`: this package's published README taught the key in a sample calling `driver.syncSchema` directly, and `syncSchema` forwards verbatim with no Zod, so the affected population is non-zero by construction. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F3jdziLbAPGeceVNmSox5L
…message `check:doc-authoring`'s sibling-package prose-id leg went red on the new message: a runtime string reaches authors, operators and generated surfaces, none of whom can resolve an internal issue id. The pointer stays in the adjacent TSDoc, where the reader who can resolve it already is. Maintainer ruling 2026-08-12, verbatim: 「处理 issue 时犯的错应该总结成经验,保留 issue id没有意义」 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F3jdziLbAPGeceVNmSox5L
📓 Docs Drift CheckThis PR changes 1 package(s): 1 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 1 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 6 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 1bea04b50853c62ba12ab21e0794060a177195aa && git checkout 1bea04b50853c62ba12ab21e0794060a177195aa
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 8fcd0816dadc10c66aa7a5d2fa5d8b57e7844344 00fd198c10499df823ebec788bf19e87143c1e1a && git checkout -B drift-repro 8fcd0816dadc10c66aa7a5d2fa5d8b57e7844344 && git merge --no-ff 00fd198c10499df823ebec788bf19e87143c1e1a
node scripts/docs-audit/affected-docs.mjs --json 8fcd0816dadc10c66aa7a5d2fa5d8b57e7844344
|
zhuangjianguo
commented
Aug 30, 2026
PM review — ACCEPT. Verified by content, on the diff and on What I checked myself
The open question — ruling A: keep |
Uh oh!
There was an error while loading. Please reload this page.
Part of #13222 — deliberately
Part ofand not a closing keyword. The card as filed covers two things; the maintainer's 2026-08-30 ruling (verbatim 「13222 A」) takes part (1) only, and part (2) is a separate ruling carried in a later batch.mongodb-schema-declared-indexes.test.tsstill names #13222 as the owner of part (2), so auto-closing the card on merge would leave that pin pointing at a closed issue. Close it by hand once part (2) has its own home.What this does
syncCollectionSchemagated its field-level join index onfield.reference_to.referenceis the only relationship spelling@objectstack/specdeclares;reference_tois a rejected alias. So one key had two doors with opposite answers, and the silent one was the one that touched the database.A field still carrying
reference_towhen it reaches schema sync now throwsVALIDATION_ERROR/400, naming it as a rejected alias in the same wordsFieldSchemauses. No deprecation window, per the ruling.Where the door sits, and why it agrees with the SQL door
The SQL door (
sql-driver.ts:15191, #11567) is stated before themultipleshort-circuit and before the type switch, on the predicatefield.reference_to !== undefined. Measured against@objectstack/specbuilt from this tree, that placement is the spec's own shape:FieldSchema.safeParse{ type:'lookup', reference_to:'company' }success:false,unrecognized_keys{ type:'text', reference_to:'company' }success:false,unrecognized_keys— same verdict, different type{ type:'lookup', reference_to: null }/''success:false,unrecognized_keys— same verdict again{ type:'lookup', reference:'company' }success:true{ ..., reference_to: undefined }(own key present)success:false— the schema's predicate is key PRESENCEreference_to⇒ The verdict is gated on neither type nor value, so neither may gate the driver.
mongodb-schema.tshas no type switch, so the equivalent placement is ahead of the whole field loop — which also puts it ahead ofdb.createCollection, so a refused sync leaves nothing behind. The predicate is!== undefined, identical to the SQL door: both doors are narrower than the schema by exactly one shape (an explicitly-spreadundefined), and identically so.⛔ What this deliberately does NOT change
The join-index arm is byte-identical —
git diffshows no+/-line inside it. Itsfield.reference_toconjunct is now unreachable, and deleting it would start buildingidx_FIELD_lookupfor canonicalreferencelookups: a boot-time behaviour change for deployments holding large collections, and part (2)'s unruled call. The arm's observable behaviour is exactly what it was, pinned by the last case in the new suite: auserfield is indexed, a canonicalreferencelookup is not.mongodb-schema-declared-indexes.test.ts(#12252's pin) andmongodb-driver.test.ts(#13224's) are untouched and stay green.Why
minorand notpatch#11567 graded its half
patchon "no authored deployment could reach the branch". That does not transfer. This package's own publishedREADME.mdtaughtreference_toinside a sample callingdriver.syncSchema(...)directly, andsyncSchema(object, schema: unknown)casts and forwards verbatim with no Zod (mongodb-driver.ts:632-641).README.mdis in the packagefilesarray, so it shipped at@objectstack/driver-mongodb17.2.0 and every earlier version. The affected population is non-zero by construction. The changeset's upgrade note names that population.Re-read on this branch, as asked:
README.mdno longer teaches the key. Its only remainingreference_tois at:167-168, prose recording that the spelling is refused; the sample at:176spellsreference: 'company'.Reverse verification (ablation)
Prediction stated before running: RED, and only the six door-dependent cases. Procedure: implementation committed FIRST (
23cc629a47), then the door block deleted frommongodb-schema.tsunder anEXIT INT TERMtrap with an absoluteREPO_ROOT.grep -cwent 1 to 0,git hash-object1a55193ato298d86b4../mongodb-schema.js, a relative in-package specifier that vitest resolves to source. Noexportshop todist/, so this pair is outsideKNOWN_UNALIASED_TEST_IMPORTS' concern. The red below is itself the proof the source is what ran.vitestexit 1 — 6 failed, 9 passed. Exactly the predicted six; the two door-independent cases and all seven of the siblingmongodb-schema-declared-indexes.test.tscases stayed green, which is the control that the harness was wired to something.git checkout HEAD -- ABSOLUTE_PATH, then on-disk hash back to1a55193aand byte-equal to the HEAD blob,git diff HEAD0 bytes,git status --porcelainempty, door call-site count back to 1.Docs callout — a bounded in-place correction, declared
content/docs/protocol/objectql/types.mdx:734carried awarnwhose middle clause read "The SQL driver's FK DDL is gated on areference_toproperty…" — present tense. #11567 retired that DDL and put a refusal in its place (sql-driver.ts:15191), so writing the new MongoDB sentence beside it would have produced a self-contradicting callout: SQL still honouring the key while MongoDB refuses it, which is the opposite of the ruling. The clause is corrected to what landed, evidence being the landedrefuseRejectedReferenceAliascall site, and the MongoDB half added beside it. No other content moved;master_detail/treeand thedeleteBehaviorsentence are preserved.Gates, all run locally at
00fd198c10Family derived from the real diff, not recalled:
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack(4 paths vs merge base71627f7b4). Exit codes captured before any pipe.eslint . --no-inline-config— exit 0, 62s, zero bytes of output. No narrowing was needed, so none is claimed.pnpm --filter @objectstack/driver-mongodb test— 504 passed, 143 skipped (the 5 opt-inmongodb-memory-serversuites, driver-mongodb 测试:两个套件并发下载 MongoDB 二进制,mongodb-memory-server 的 rename 竞态让全绿测试 exit 1(队列 flaky) #5517).typecheckexit 0.tsconfig.jsonexcludes every*.test.ts, sotypechecksays nothing about the new suite — measured with--listFiles: 0 hits for it. The number that does cover it is the TEST_DEBT ratchet, and it is green:pnpm check:type-check-debt— "29 ledger entr(ies) re-measured in 227.1s, 1547 raw tsc error(s) total, none above its recorded number", on a fully built workspace. Reconstructed separately for this package alone: exactly 10 raw errors,TS1309 x7 + TS2550 x3— byte-for-byte the composition the ledger records — with 0 diagnostics naming the new file.nul-bytes·changeset-gate-self-tests·empty-changeset·changeset-no-major·adr-0087-registration·objectui-changeset·pm-half-states·keyed-text-bounds·release-rehearsal-clone --self-test·doc-anchors·doc-authoring·docs-single-h1·docs-redirects·docs-audit-scope·role-word·doc-frontmatter·doc-route-spelling·docs-section-name·section-landing-index·published-readme-links·react-page-adapter-contract·cross-package-test-inputs(both spellings) ·test-source-alias·type-source-resolution·driver-conformance·engine-double-contract·where-matcher·query-options-erasure·objectql-double-limit·logger-receiver-detach·slot-lookup·page-declaration-shape·published-files·comment-mask-adoption·undeclared-dep-imports·plugin-teardown-shape·type-check-coverage·ci-filter-parity·shard-attestation·dual-build-cjs-loads· speccheck:docs/yaml-examples/skill-examples/empty-state/liveness/strictness-ledger/variant-docs· lintdoc-formula-expressions/doc-security-posture.check:doc-authoringcaught a real red on the first pass and it is repaired in00fd198c10: the runtime message carried an internal tracker id, which reaches operators who cannot resolve it. The pointer moved to the adjacent TSDoc.scripts/pm/check-half-states.mjs(exit 3,PREREQUISITE NOT MET— no GitHub credential in this container; it swept nothing) andscripts/check-test-completeness.mjs(exit 3 — it needs a savedturbo run testlog, which the family invocation cannot supply).Files
packages/drivers/driver-mongodb/src/mongodb-schema.ts— the door, its TSDoc, andFieldDef.reference_tore-typedunknownand re-documented as a refused key the door reads.packages/drivers/driver-mongodb/src/mongodb-13222-reference-to-refusal.test.ts— new; one path beyond the file surface my dispatch listed, added because the dispatch also required the behaviour be driven through the fake-Dbrecorder rather than the opt-in mongod lane. Declared here rather than left for a reviewer to notice.content/docs/protocol/objectql/types.mdx— the callout above..changeset/mongodb-refuse-rejected-reference-to-alias.md—minor.skills/**is untouched, so the published-skill line-count readings do not apply.Generated by Claude Code