Skip to content

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

Closed

Description

@baozhoutao

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 the domain:engine seat — the bug report below is the reporter's and is unedited. The defect this card reports is repaired on origin/main by e04a0aff25 (#17829, the #17590 director ruling) and is UNRELEASED. Nothing remains that a dev or this seat can do; the card is pm:awaiting-maintainer on 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

  1. ObjectQL.cascadeDeleteRelations probes every registered object that references the row being deleted. For a multiple: true field, ObjectQL.referenceProbeFilter builds { field: { $contains: id } } — correct per 【缺陷】被 multiple:true lookup 指向的对象 REST DELETE 全 400——cascadeDeleteRelations 依赖探针用裸等值查 JSON 列 #9362.
  2. driver-sql's textMatchPredicate lowers $contains for the postgres dialect to a bare ?? LIKE ? ESCAPE ? with no cast (packages/driver-sql, textMatchPredicate — unchanged in 17.4.0).
  3. But the Postgres DDL face creates multi-value columns as json, not text:
=> 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 $contains membership 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 survives

Swap -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':

  1. 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 $contains meaning across dialects. Loses any index on the column, which the current LIKE '%…%' already does.
  2. Store multi-value columns as text on PG, matching the SQLite/MySQL face and the comment the pushdown relies on. Needs a migration arm for existing json columns.
  3. 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; textMatchPredicate is byte-identical on the PG arm)
  • PostgreSQL 16-alpine (postgres:16-alpine), also 16 mainline
  • Node 22, pg 8.23
  • Contrast face: driver-sql on SQLite — all of the above pass

Generated by Claude Code

Activity

  1. self-assigned this
    on Sep 17, 2026
  2. huangyiirene commented on Sep 17, 2026

    @huangyiirene
    Collaborator

    Claim: PM loop round 1
    Session: session_01CqmCgU5RGDoJYhHUMVp2af
    Branch: claude/issue-18172-pg-contains-cascade-probe
    Worktree: objectstack-issue-18172
    Domain: domain:engine
    Seat: domain:engine#1
    File 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 own dispatch-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.

    ⚠️ Read before starting — this card's premise may already be discharged on 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:

    ⇒ The open question this dispatch buys is narrower than the card: does the cascade dependents probe path (cascadeDeleteRelations → referenceProbeFilter → {field: {$contains: id}}) actually reach jsonMembershipPredicate, or does it still bypass it and emit bare LIKE against a json column? 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: null is a fully legitimate terminal report here and ⛔ will not be counted as a failed dispatch. If the defect is discharged on main but still live in the published 17.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

  3. huangyiirene commented on Sep 17, 2026

    @huangyiirene
    Collaborator

    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

  4. removed their assignment
    on Sep 17, 2026
  5. huangyiirene commented on Sep 17, 2026

    @huangyiirene
    Collaborator

    os-dev-report reviewed — ACCEPT, and the card moves to pm:awaiting-maintainer. ⛔ Nothing landed and nothing needed to: the repair is already on origin/main and is unreleased.

    domain:engine execution 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: null is 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

    claim instrument result
    report reached GitHub GET /issues/18172/comments 5711758570, marker os-dev-report on line 1, exactly one footer
    branch carries no commits git rev-list --count origin/main..origin/claude/issue-18172-pg-contains-cascade-probe 0
    the repair is on main git merge-base --is-ancestor e04a0aff25 origin/main exit 0 ⇒ ancestor
    the repair is unreleased git show origin/main:.changeset/17590-contains-membership-per-dialect.md present, @objectstack/driver-sql: minor ⇒ not yet consumed
    ⭐ the user-facing half npm registry, independently of the report dist-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 applyJsonMembership behind 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-tree git 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.ts moved 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.

    ⚠️ Not measured, and recorded as such: the MySQL face (no MySQL server in the container — the suite names it as one explicit skip, ⛔ never a silent pass), and the REST/HTTP envelope (observed at the engine boundary instead).

    Release: — this card leaves this seat's hand

    Release: session session_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:

    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
    

    ⛔ 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.

    ⚠️ This is the one case in which a pending release is escalated rather than left alone: a p1 whose blast radius is 「every DELETE of an object targeted by a multiple: true reference, 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:

    • ✅ FILED — the real-stack pin of this exact path hard-codes better-sqlite3. ⭐ That is the named mechanism by which a p1 survived into two releases, and this dispatch produced the failure probe that proves the pin cannot see it. Filed separately as a finding for triage to grade, with dedupe words. ⛔ Not graded here.
    • ⏹️ agreed, not filed — the unit suite's driver double models $contains as 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.
    • ⏹️ agreed, not filed — 「the card is accurate about the published artifact」 is this card's own disposition, now recorded in its state.

    Housekeeping

    ⚠️ CORRECTED — the branch is NOT deleted; the claim below was written before the attempt failed. The zero-commit branch claude/issue-18172-pg-contains-cascade-probe still exists at d93400f42e. Deletion is unavailable from this seat on both channels, measured: git push origin --delete and git push origin :refs/heads/... both end the remote end hung up unexpectedly / Everything up-to-date (the push wrapper does not carry a delete refspec), and DELETE /repos/{owner}/{repo}/git/refs/heads/... answers HTTP 403 Write 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_calls 0 · REST writes 1 (the report comment). ⛔ No denied-roster tool was called.


    Generated by Claude Code

  6. objectstack-fleet commented on Sep 23, 2026

    @objectstack-fleet
    Contributor

    关闭(维护者裁定):修复已在 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/main b940f32a56 的祖先。它把 multiple: true 或 JSON 列上的 $contains 按方言编译成成员判断。卡面记录过它在真实 PG 16 上的验证,带消融对照。
    • 待发布的 changeset 在:.changeset/17590-contains-membership-per-dialect.md(@objectstack/driver-sql minor、@objectstack/spec minor)。
    • npm 上 @objectstack/driver-sql 与 @objectstack/spec 的 latest 仍是 17.4.0(09-09 发布,早于修复)。

    这意味着什么

    • 代码侧的活做完了,所以以 completed 关闭。
    • ⚠️ 关闭 ≠ 用户已拿到。PG 上的客户(卡面记录的真实应用)要到下一个版本(> 17.4.0)才不再遇到「删除被多值查找引用的对象报 500」。发版是维护者的动作,本席在向维护者的汇报里单独列出。
    • 下一版发布后如果 PG 上仍能复现,那是新的缺陷,请重新开卡并附上版本号。

    标签:摘 pm:awaiting-maintainer;priority:p1、domain:engine 保留。


    Generated by Claude Code

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions