Repository navigation
driver-sql (PG): every DELETE of an object targeted by a multiple: true reference 500s — the cascade probe's $contains compiles to LIKE against a json column (42883) #18172
Description
Maintainer-action: cut the release that ships @objectstack/driver-sql > 17.4.0, consuming the pending .changeset/17590-contains-membership-per-dialect.md (minor) — done when the npm dist-tag latest for @objectstack/driver-sql resolves to a version whose dist carries the jsonb @> membership construct, i.e. npm view @objectstack/driver-sql version no longer reads 17.4.0
⚠️ PM state line, added by thedomain:engineseat — the bug report below is the reporter's and is unedited. The defect this card reports is repaired onorigin/mainbye04a0aff25(#17829, the #17590 director ruling) and is UNRELEASED. Nothing remains that a dev or this seat can do; the card ispm:awaiting-maintaineron the release action alone. Evidence in the transition comment.
TL;DR
On PostgreSQL, deleting any record of an object that is the target of a multiple: true lookup fails with 500 DATABASE_ERROR — always, with an empty dependents table, as platform admin. SQLite is fine. The cascade dependents probe compiles $contains to LIKE and aims it at a column PG created as json, and json ~~ text does not exist (SQLSTATE 42883).
This is the Postgres half of #9362 / #9390: the $contains pushdown those issues introduced assumes every SQL backend stores a multi-value column as JSON TEXT. The Postgres DDL face does not — it emits json.
Measured on published 17.3.0 and 17.4.0 (both broken), PostgreSQL 16-alpine, against a real customer app.
Symptom
DELETE /api/v1/data/<object>/<id> → 500 { "error": "Internal server error", "code": "DATABASE_ERROR" }
Server log:
[sql-driver] DATABASE_ERROR — the backend refused a read on 'os_tianshun_ehr_production_plan' (42883).
select * from "os_tianshun_ehr_production_plan"
where ("organization_id" = $1 or "organization_id" is null)
and "work_object" LIKE $2 ESCAPE $3
- operator does not exist: json ~~ text
The record survives. The caller wrote no filter — this is the engine's own dependents probe.
In the UI this is invisible: the console's bulk delete only counts settled promises, so a user selecting 100 rows sees 成功 0 条,失败 100 条 ("0 succeeded, 100 failed") with no reason anywhere.
Cause
ObjectQL.cascadeDeleteRelationsprobes every registered object that references the row being deleted. For amultiple: truefield,ObjectQL.referenceProbeFilterbuilds{ field: { $contains: id } }— correct per 【缺陷】被 multiple:true lookup 指向的对象 REST DELETE 全 400——cascadeDeleteRelations 依赖探针用裸等值查 JSON 列 #9362.driver-sql'stextMatchPredicatelowers$containsfor thepostgresdialect to a bare?? LIKE ? ESCAPE ?with no cast (packages/driver-sql,textMatchPredicate— unchanged in 17.4.0).- But the Postgres DDL face creates multi-value columns as
json, nottext:
=> select table_name, column_name, data_type from information_schema.columns
where column_name in ('work_object','positions','members','equipment');
os_tianshun_ehr_production_plan | work_object | json
os_tianshun_ehr_task_version | positions | json
os_tianshun_ehr_team | members | json
os_tianshun_ehr_work_island | equipment | json
LIKE has no json operand in PG ⇒ 42883 ⇒ findRows raises backendStatementFault ⇒ the #8895 catch (missing-table only) rethrows ⇒ the whole delete aborts.
The in-code comment that the pushdown rests on states the assumption explicitly:
…and every SQL backend in this repo puts that array in a JSON TEXT column.
That is true for SQLite/MySQL and false for Postgres.
Blast radius — measured
Same app package, same admin session, same moment; only the driver differs.
| operation | SQLite | PostgreSQL 16 |
|---|---|---|
delete an object targeted by a multiple: true lookup (work_object) |
200 | 500 DATABASE_ERROR |
delete another such object (position) |
200 | 500 |
delete another such object (equipment) |
200 | 500 |
delete an object not targeted by any multi-value lookup (project) |
200 | 200 |
delete an object not targeted by any multi-value lookup (product) |
200 | 200 |
| 100 parallel deletes (console bulk delete) | 100/100 ok | 0/100 ok |
GET /api/v1/data/<obj>?filter={"multi_field":{"$contains":"x"}} |
200 | 500 DATABASE_ERROR |
Two consequences beyond delete:
- Any
$containsmembership filter over a multi-value field is dead on Postgres — this is not limited to the cascade path. (A multi-select filter in a list view, an app querying "plans that include this item", etc.) - The refusal cascades outward: because the child cannot be deleted, its own parents stay referenced and refuse deletion too with a correct-looking
409 DELETE_RESTRICTED, which sends the operator hunting in the wrong direction.
Minimal repro
Two objects; child.items is a multi-value lookup at parent. No rows in child are needed.
// parent.object.ts
ObjectSchema.create({ name: 'repro_parent', label: 'Parent', fields: {
name: { type: 'text', label: 'Name', required: true },
}});
// child.object.ts
ObjectSchema.create({ name: 'repro_child', label: 'Child', fields: {
name: { type: 'text', label: 'Name', required: true },
items: { type: 'lookup', label: 'Items', reference: 'repro_parent', multiple: true },
}});os dev -d "postgres://postgres:postgres@127.0.0.1:5432/repro"
# create one repro_parent, then:
curl -X DELETE .../api/v1/data/repro_parent/<id> # → 500 DATABASE_ERROR, row survivesSwap -d file:./dev.sqlite and the same delete returns 200.
Suggested direction (yours to rule)
Three candidate faces, listed only so the axis is visible — the choice is the maintainers':
- Lower the operand on the PG dialect: emit
(??)::text LIKE ? ESCAPE ?when the column's declared type is multi-value/JSON-class. Smallest change; keeps one$containsmeaning across dialects. Loses any index on the column, which the currentLIKE '%…%'already does. - Store multi-value columns as
texton PG, matching the SQLite/MySQL face and the comment the pushdown relies on. Needs a migration arm for existingjsoncolumns. - Give the probe a dialect-native membership spelling (
json_array_elements_text/jsonb @>), which would also make it indexable — the largest change.
Whatever face is chosen, the delete path deserves a conformance test on all three dialects: #9362 and #9390 both fixed this family on the SQLite face only, and the Postgres face has been failing since.
Environment
@objectstack/*17.3.0 and 17.4.0 (both reproduce;textMatchPredicateis byte-identical on the PG arm)- PostgreSQL 16-alpine (
postgres:16-alpine), also 16 mainline - Node 22,
pg8.23 - Contrast face:
driver-sqlon SQLite — all of the above pass
Generated by Claude Code
Activity
huangyiirene commentedon Sep 17, 2026
Claim: PM loop round 1
Session:
session_01CqmCgU5RGDoJYhHUMVp2afBranch:
claude/issue-18172-pg-contains-cascade-probeWorktree:
objectstack-issue-18172Domain:
domain:engineSeat:
domain:engine#1File surface:
packages/drivers/driver-sql/src/·packages/objectql/src/(stop on breach; explain in the report)Container & model:
M,mode:subagent,model: opus (default judgement tier)— quoting this dispatch's owndispatch-gates.mjs --tier packages/drivers/driver-sql/src/sql-driver.ts packages/objectql/src/engine.ts: 「Model tier — no path-derived mandate: the surface hits none of the 3 declared glob(s) … The tier stays the PM's per-card judgment call (floor sonnet · default opus · ceiling fable).」 ⇒ default judgement tier, chosen because the repair face (if any is still owed) is a dialect-semantics judgement, ⛔ not a gate-decided mechanical edit.Clause-②: no
Thread-read: 5690492850
Serial constraints cleared:
#17231 is DEFERRED behind this card — both land in packages/drivers/driver-sql/src/sql-driver.ts (this card at the $contains/textMatchPredicate lowering region, #17231 at createColumn). ⛔ No region release is claimed: this lane's charter allows a region split only from the other card's REAL change itinerary, and neither card has a PR, so the itinerary is UNKNOWN and the file serialises whole. #17975 is DEFERRED on the same rule for packages/objectql/src/engine.ts. No other in-flight claim exists in this lane (pm:dispatched = 0 lane-wide at seating, 0 remote branches on any queue card). No sibling card pins behaviour this card asserts.origin/main⛔ This is a PM mechanism reading, not a ruling, and refuting it is the first deliverable. The card measured published
17.3.0/17.4.0, ⛔ not the tree. Between the filing and today the tree moved:e04a0aff25—fix(driver-sql,spec): compile $contains on a JSON column as a per-dialect MEMBERSHIP test (#17829), carrying director ruling driver-sql: the $contains MEMBERSHIP spelling on any multi-valued / JSON column is a DATABASE_ERROR 500 on live PostgreSQL (SQLSTATE 42883, operator does not exist: json ~~ text) — it has only ever been executed on SQLite #17590 (2026-09-12).origin/maintoday,jsonMembershipPredicate(packages/drivers/driver-sql/src/sql-driver.ts) lowers PostgreSQL$containsto??::jsonb @> ?::jsonb, MySQL toJSON_CONTAINS(??, ?), SQLite to ajson_eachscan — pinned atsql-driver-17590-json-column-membership.test.ts:243-245. That is the card's own suggested direction 3.d285bf0fbc—fix(spec)!: refuse multiple: true outside the multi-capable types, and derive driver-sql's storage from the spec predicate (#18187)also moved this area.⇒ The open question this dispatch buys is narrower than the card: does the cascade dependents probe path (
cascadeDeleteRelations→referenceProbeFilter→{field: {$contains: id}}) actually reachjsonMembershipPredicate, or does it still bypass it and emit bareLIKEagainst ajsoncolumn? A membership lowering that keys off declared multi-valued-ness may or may not be reachable from a probe built without that schema context. Measure it; ⛔ do not assume either way.premise_still_valid: false+pr: nullis a fully legitimate terminal report here and ⛔ will not be counted as a failed dispatch. If the defect is discharged onmainbut still live in the published17.4.0, say so explicitly — that is a release-tracking outcome, ⛔ not a code one, and it is the answer this seat needs.Generated by Claude Code
huangyiirene commentedon Sep 17, 2026
os-dev-report
{ "issue": 18172, "status": "done", "branch": "claude/issue-18172-pg-contains-cascade-probe", "head_sha": "d93400f42ea69215b3de22bfe3ca057385ff0eea", "head_note": "branch carries ZERO commits; its tip is byte-identical to origin/main at dispatch. packages/drivers/driver-sql/src/sql-driver.ts at my head hashes 3ca63ecc5149f35e432dfd59316f35ff8a6f44af == `git rev-parse HEAD:packages/drivers/driver-sql/src/sql-driver.ts`. The ablation mutated that file inside THIS worktree only, 2026-09-17T08:53:42Z..08:53:51Z, and the restore leg is proven by hash equality plus a whole-tree `git status --porcelain` that is empty.", "pr": null, "session": "session_01CqmCgU5RGDoJYhHUMVp2af", "premise_still_valid": false, "files_changed": [], "line_budget": "n/a — no skills/** path in the diff; files_changed is empty", "card_body_sanitizer_check": "intact — 5948 bytes, 7 level-2 headings, 10 code fences (balanced), all three angle-bracket path fragments survived inside fences, body ends on a complete line + newline. No truncation found.", "summary": "Measured on the dispatch base d93400f42e against a private live PostgreSQL 16.13 cluster: the cascade dependents probe DOES reach the ruled #17590 construct. `cascadeDeleteRelations` -> `referenceProbeFilter` -> `{items:{$contains:id}}` compiles to `select * from \"repro_child\" where (\"items\"::jsonb @> $1::jsonb)`, the DELETE returns 200 and the parent row is gone; the card's second reported path, a bare `$contains` filter on the multi-value column, compiles the same construct and returns rows instead of 42883. The repair is e04a0aff25 (`fix(driver-sql,spec): compile $contains on a JSON column as a per-dialect MEMBERSHIP test (#17829)`, 2026-09-12, an ancestor of my head — `git merge-base --is-ancestor` exit 0, self-certifying on a shallow checkout), and it is UNRELEASED: npm dist-tag `latest` for @objectstack/driver-sql is 17.4.0 published 2026-09-09, and the 17.4.0 tarball's dist/index.mjs carries 0 occurrences of `jsonb @>` and 0 of `jsonMembershipPredicate` against 14 of `ESCAPE` (positive control). A user on 17.4.0 still hits every row of the card's blast-radius table; the pending `.changeset/17590-contains-membership-per-dialect.md` is a `minor`, so the fix ships in 17.5.0.", "pm_readings": { "R1": "CONFIRMED — the card's premise is discharged on origin/main. The card measured published 17.3.0/17.4.0, not the tree.", "R2": "REFUTED — the probe path is NOT bypassing the lowering. `isJsonColumn(table, localField)` reads `jsonFields[table]`, filled by BOTH `initObjects` and `registerObjectMetadata` from `isJsonField` = `JSON_COLUMN_TYPES.has(type) || isMultiValueField(field)`; a probe-time filter carries no schema context and does not need any, because the population is keyed by table+local field name on the driver, not by the filter.", "R3": "MEASURED — the runtime defect does NOT reproduce on the base. Positive control fired (see `tests`).", "Zone3_suggested_route": "WRONG, and stated as asked. `referenceProbeFilter` needs no schema context for the lowering to recognise the column: the recognition happens entirely inside the driver, from its own per-table registry. The gap the PM guessed at does not exist." }, "tests": "PG cluster: private initdb + pg_ctl, PostgreSQL 16.13, 127.0.0.1:54318, torn down at end. (1) BASE RUN, card's minimal repro (repro_parent / repro_child with a `multiple: true` lookup, empty child table), engine-level through ObjectQL + SqlDriver on live PG: DDL face `repro_child.items = json` (the card's own precondition, unchanged); probe SQL `select * from \"repro_child\" where (\"items\"::jsonb @> $1::jsonb)`; VERDICT DELETE_OK; parent row deleted; `$contains` filter OK. (2) POSITIVE CONTROL (ablation) — `applyJsonMembership` neutered with a runtime-evaluated marker `ABLATE18172_NEVER`, anchor occurrence count 1 before / marker count 0 before, 1 after, source blob 3ca63ecc..->c99e4786.., `pnpm --filter @objectstack/driver-sql build`, then `node scripts/ablation-dist-preflight.mjs @objectstack/driver-sql ABLATE18172_NEVER` -> marker present in 2 built files. Ablated run reproduces the card BYTE-FOR-BYTE: `[sql-driver] DATABASE_ERROR — the backend refused a read on 'repro_child' (42883) ... select * from \"repro_child\" where \"items\" LIKE $1 ESCAPE $2 - operator does not exist: json ~~ text`, VERDICT DELETE_THREW DATABASE_ERROR / 500, parent row survives. Direction: RED, as predicted. Restore leg: `git checkout HEAD -- packages/drivers/driver-sql/src/sql-driver.ts`, rebuild, `ablation-dist-preflight ... --absent` -> marker absent from all 6 built files AND whole-tree clean, exit 0. (3) EXTENDED LIVE MATRIX, PG 16.13 vs SQLite, four cases each, all four identical across the two dialects: set_null WITH dependents -> DELETE OK, remainder written correctly; restrict WITH dependents -> DELETE_RESTRICTED / 409 (full ADR-0112 envelope, not a bare throw); an id needing JSON escaping -> the `$or` limb compiles TWO membership predicates and the guard still fires 409; `$notContains` -> `(\"items\" is null or NOT (\"items\"::jsonb @> $1::jsonb))`, correct rows. (4) EXISTING SUITES on the base, under scripts/pm/os-verify-lock.sh, VERDICT command-exit 0 each: `pnpm --filter @objectstack/driver-sql exec vitest run --maxWorkers=2 src/sql-driver-17590-json-column-membership.test.ts` -> 21 passed | 2 skipped; the same file WITH OS_TEST_POSTGRES_URL pointed at the live cluster -> 28 passed | 1 skipped (the live-PG cell moved from skipped to passing; the 1 remaining skip is the MySQL cell); `pnpm --filter @objectstack/objectql exec vitest run --maxWorkers=2 src/engine-cascade-delete-multivalue-probe.test.ts` -> 17 passed.", "gates": { "derivation": "node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands -> exit 2; stderr: \"dispatch-gates: gate list derived from the tree of 'objectstack-ai/objectstack' at commit d93400f42e\" / \"--repo 'objectstack-ai/objectstack' checked against this checkout's 'origin' remote — it holds.\" / \"this branch changes nothing against 'origin/main' (merge base d93400f42) — nothing to derive.\"", "families_owed": [], "families_ran": [], "reconcile_ran_file": "not applicable — the derivation refuses to emit a list for an empty changed set", "not_measured": [ "MySQL face of the membership construct — no MySQL server in this container; the 17590 suite reports it as 1 named skip, never a silent pass", "REST/HTTP layer — the 500 envelope was observed at the engine boundary (DATABASE_ERROR / 500) in the ablated control, which is the same error object the data-plane DELETE serialises" ] }, "api_writes": "1 — POST /repos/objectstack-ai/objectstack/issues/18172/comments (this report). No POST /pulls: no PR is owed. Reads were GET /issues/18172 and GET /issues/18172/comments.", "mcp_calls": "0 — no MCP GitHub tool was called, read or write", "deviations": [ "PG data directory placed at /var/tmp/os-dev-18172-pg instead of the ordered /tmp/claude-0/os-dev-18172/: initdb refuses to run as root, and /tmp/claude-0 is mode 0700 root so the `postgres` OS user cannot traverse it. Chmod-ing a directory shared with other live devs was refused. All gate output, logs and instrument files DID go to /tmp/claude-0/os-dev-18172/ as ordered.", "The two repro harness scripts had to live under packages/rest/.repro-18172/ — packages/rest is the only package whose node_modules links BOTH @objectstack/objectql and @objectstack/driver-sql, and the repo root links neither. Untracked throughout, copied to the scratchpad, then deleted; whole-tree `git status --porcelain` is empty and ablation-dist-preflight --absent exits 0 against it.", "No gate family was run, because there is no diff to judge. This is a declared zero, not an omission: the derivation tool itself refuses to emit a list." ], "open_questions": [], "out_of_scope_findings": [ "noted, not filed: packages/runtime/src/cascade-delete-multivalue-lookup-real-driver.integration.test.ts — the one real-stack pin of this exact path (#9362's own reproduction, ObjectQL + real SqlDriver + the protocol data-plane delete) hard-codes `client: 'better-sqlite3'` at line 123 while its header calls itself 'the card's reproduction, on the REAL stack'. That single hard-coded client is the whole mechanism by which #18172 survived into two releases: the SQLite face was pinned, the PostgreSQL face of the same path never was. This is #18172's own closing ask ('the delete path deserves a conformance test on all three dialects') and nothing in the tree satisfies it — the #17590 pin is a compiler-level pin on applyFilters, not on the cascade path. NOT filed by me: no shipped code is wrong and nothing fails, so it is none of the three filing classes; and the file is OUTSIDE my declared surface (packages/runtime/), so I did not touch it. Successor who will hit this file: whoever next edits referenceProbeFilter, cascadeDeleteRelations or the dependents probe — the sibling unit suite's own header points here as the real-stack half. Dedupe words if the PM chooses to file it: cascade-delete-multivalue-lookup-real-driver, better-sqlite3 hard-coded, live-dialect-matrix.testkit, ADR-0053 D-A3 driver axis, postgres face unpinned.", "noted, not filed: packages/objectql/src/engine-cascade-delete-multivalue-probe.test.ts — its driver double still models `$contains` on a JSON column as a SUBSTRING of the serialization (`JSON.stringify(stored).includes(String(comparand))`), which is the pre-#17590 reading the real driver no longer has. Verdicts are unaffected, because the double is LOOSER than the driver and the engine narrows through `storedReferenceIncludes` afterwards, so both readings yield the same dependent set — which is why it passes and why this is a fidelity note rather than a defect. Successor: same as above.", "noted, not filed: nothing in the card is stale or wrong about the published artifact — the card is an accurate bug report against 17.3.0/17.4.0 and remains live for anyone on npm `latest`. Its disposition is a RELEASE-tracking question (ship 17.5.0), not a code one." ] }Generated by Claude Code
huangyiirene commentedon Sep 17, 2026
os-dev-report reviewed — ACCEPT, and the card moves to
pm:awaiting-maintainer. ⛔ Nothing landed and nothing needed to: the repair is already onorigin/mainand is unreleased.domain:engineexecution seat,session_01CqmCgU5RGDoJYhHUMVp2af, R1, 2026-09-17T09:05Z. Report under review:5711758570. ⛔ Verified against GitHub, git and the npm registry — ⛔ not against the report's own account of itself.Verdict
premise_still_valid: false+pr: nullis a legitimate terminal report and is recorded as such: ⛔ not a failed dispatch, ⛔ not a rework. The dispatch's first instruction was to falsify the PM's reading, and it did — including falsifying the PM's own suggested route, which is exactly what the third zone of a dispatch order is for.What this seat re-measured itself
GET /issues/18172/comments5711758570, markeros-dev-reporton line 1, exactly one footergit rev-list --count origin/main..origin/claude/issue-18172-pg-contains-cascade-probemaingit merge-base --is-ancestor e04a0aff25 origin/maingit show origin/main:.changeset/17590-contains-membership-per-dialect.md@objectstack/driver-sql: minor⇒ not yet consumeddist-tag latest= 17.4.0, published 2026-09-09; no 17.5.0 exists⇒ The card is an accurate, still-live bug report against the published artifact, and simultaneously a discharged one against the tree. Both halves are true at once, and the card's disposition follows from the second while its user impact follows from the first.
Why the seat believes the "it no longer reproduces" half
⛔ A green is not evidence on its own — the question is always whether the harness could have seen the failure at all. This report answers it: the dev ablated
applyJsonMembershipbehind a runtime-evaluated marker, rebuilt, confirmed the marker reached the built output, and the card reproduced byte-for-byte (operator does not exist: json ~~ text, 42883,... where "items" LIKE $1 ESCAPE $2, DELETE 500, parent row survives). Then it restored and proved the restore by source-blob hash equality plus an empty whole-treegit status --porcelain.⭐ That is the positive control this seat would otherwise have had to demand. The direction was RED as predicted, so the subsequent green is a reading rather than a silence.
⭐ Also accepted: the live-PG cell of
sql-driver-17590-json-column-membership.test.tsmoved from skipped to passing when pointed at a real cluster (21 passed | 2 skipped → 28 passed | 1 skipped, the remaining skip being the MySQL cell). A skip that becomes a pass under a real server is worth more than the local green either side of it.Release:— this card leaves this seat's handRelease:sessionsession_01CqmCgU5RGDoJYhHUMVp2af· cause: work discharged by an already-merged commit; the only remaining action is a human release · destination:pm:awaiting-maintainer, assignee cleared in the same stroke.The
Maintainer-action:line is now in the body and repeated here for the two-channel anchor:⛔ This state has no machine exit: the label comes off by the director seat verifying the maintainer's action, with the evidence comment in the same stroke. ⛔ This seat will not chase it and will not re-dispatch it. ⛔ A completed release is the only thing that closes this card.
p1whose blast radius is 「every DELETE of an object targeted by amultiple: truereference, on PostgreSQL, always」 is merged and waiting. It has been named to the maintainer with that consequence stated.Findings carried out of this dispatch
The report returned three
out_of_scope_findings, all 「noted, not filed」. This seat re-read them and files one, agreeing on the other two:better-sqlite3. ⭐ That is the named mechanism by which ap1survived into two releases, and this dispatch produced the failure probe that proves the pin cannot see it. Filed separately as afindingfor triage to grade, with dedupe words. ⛔ Not graded here.$containsas a substring of the serialization. Verdicts provably unaffected (the double is LOOSER than the driver and the engine narrows afterwards) ⇒ a fidelity note, ⛔ not a defect.Housekeeping
claude/issue-18172-pg-contains-cascade-probestill exists atd93400f42e. Deletion is unavailable from this seat on both channels, measured:git push origin --deleteandgit push origin :refs/heads/...both endthe remote end hung up unexpectedly/Everything up-to-date(the push wrapper does not carry a delete refspec), andDELETE /repos/{owner}/{repo}/git/refs/heads/...answers HTTP 403Write access to this GitHub API path is not permitted through this proxy. ⇒ Recorded as an open housekeeping item rather than a done one. The original reasoning stands and is why it is worth doing when a channel exists: an empty remote branch is a phantom 「in flight」 signal for the next seating's mutual-exclusion scan, and a branch with no commits carries nothing to lose.mcp_calls0 · REST writes 1 (the report comment). ⛔ No denied-roster tool was called.Generated by Claude Code
test(runtime): pin the multi-value cascade-delete path on PostgreSQL,…
objectstack-fleet commentedon Sep 23, 2026
关闭(维护者裁定):修复已在
main,用户在下一个版本拿到分诊席(
session_01Tw7jnJinGHvoGSi8aFkhPJ),2026-09-23T14:34Z。维护者在分诊席的「待维护者」复核批次 ① 里答「18172 … 关闭」。本席读完了本卡能读到的全部评论。读数(本席今天复核)
e04a0aff25(fix(driver-sql,spec): compile $contains on a JSON column as a per-dialect MEMBERSHIP test #17829,09-12)是origin/mainb940f32a56的祖先。它把multiple: true或 JSON 列上的$contains按方言编译成成员判断。卡面记录过它在真实 PG 16 上的验证,带消融对照。.changeset/17590-contains-membership-per-dialect.md(@objectstack/driver-sqlminor、@objectstack/specminor)。@objectstack/driver-sql与@objectstack/spec的latest仍是 17.4.0(09-09 发布,早于修复)。这意味着什么
completed关闭。标签:摘
pm:awaiting-maintainer;priority:p1、domain:engine保留。Generated by Claude Code
test(runtime): pin the multi-value cascade-delete path on PostgreSQL,…