You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
driver-sql: update(), delete() and bulkDelete() key on literal "id", and aggregate()/distinct() on local field names, ignoring external.columnMap — a write can hit the wrong row on a federated table that also has its own "id" column #31340
epic:#27261#1 · marchtian · session_017ihL2c5z3q4h8inrn5kwqV
Filing gate: ③ out-of-scope of the #31328 card thread (Oracle federation dialect), which measured both defects while building the Oracle write and aggregate paths.
Ruling-ref: ADR-0015 (external datasource federation; external.columnMap)
Dedupe (open AND closed): "columnMap aggregate", "columnMap distinct", "columnMap groupBy", "columnMap update delete id" → none.
Reader: the epic PM dispatches it.
type: Bug
Resolve the key column through remoteColumn(object, 'id', 'id') in update() (the write and its read-back), delete() (sharded and unsharded) and bulkDelete(), at packages/drivers/driver-sql/src/sql-driver.ts, as create()'s read-back already does.
Map groupBy, the aggregand, date buckets and distinct through remoteColumn (sql-driver-aggregation.ts, sql-driver-distinct.ts).
Pins on sqlite: a mapped key over a table that also has its own id column, where update and delete touch the named row only; aggregate and distinct over renamed columns. Red before the fix, green after.
Out of scope: upsert on a columnMap-ed object (conflict target). Measure it and file a card if it misses the map too.
One patch changeset per defect, naming every SQL dialect.
Clause-②: no · Metadata-protocol: none. The fix commits already exist on the #31328 branch (de3cc9da6d aggregate/distinct and a write-key commit). This card lands them alone off main, and #31328 then merges main.
epic:#27261#1 · marchtian · session_017ihL2c5z3q4h8inrn5kwqV
Claim: PM loop round 18 2026-11-08T02:55Z
Session: session_017ihL2c5z3q4h8inrn5kwqV (epic PM); implementing card thread session_017ihL2euiyuLXizr6FW32R3
Account: marchtian
Branch: claude/project-thread-on4xyn (the card thread's platform-assigned branch — measured platform exception to claude/issue-<n>-<slug>)
Worktree: objectstack-issue-31340
Domain: domain:server
Seat: epic:#27261#1 (epic PM, which filed the card as an out-of-scope finding of #31328; no domain:* seat)
File surface: packages/drivers/driver-sql/src/sql-driver.ts, sql-driver-aggregation.ts, sql-driver-distinct.ts, new sqlite pin tests under packages/drivers/driver-sql/src/, and .changeset/**. No packages/spec/**, no Oracle code. Stop on any other breach.
Container & model: S, mode:thread, model: default tier (Fable unavailable; 田騏's order of 2026-10-29 19:39Z), effort: high
Clause-②: no — a driver fix to honour an existing contract (external.columnMap); no published surface changes
Metadata-protocol: none
Responsibility: the card body's five Task items (upsert is measure-and-report only)
Thread-read: none
Premise: by reading at origin/main30e0bd1, update(), delete() and bulkDelete() in sql-driver.ts build .where('id', id) / .whereIn('id', ids) without remoteColumn; the #31328 thread measured the aggregate/distinct miss (sqlite refusal, Oracle ORA-00904) and the write-key miss (5 red pins before its fix).
Serial constraints: #31328 carries the same two fixes as commits on its branch and merges main after this lands; #31327 is told not to touch these paths.
{
"issue": 31340,
"status": "done",
"branch": "claude/project-thread-on4xyn",
"pr": "https://github.com/objectstack-ai/objectstack/pull/31349",
"session": "session_017ihL2euiyuLXizr6FW32R3",
"premise_still_valid": true,
"summary": "Lands the two columnMap fixes from the Oracle card's branch alone off main, with nothing Oracle-specific. Commit f9a07140: aggregate() (GROUP BY, structured group key, date bucket, aggregand) and distinct() spell the remote column through remoteColumn; output keys, presentation and per-field registries stay the field; buildDateBucketExpr gains an optional column parameter (ported from the Oracle wip commit without the Oracle arm). Commit 8c643017 (+0ceca395 changeset wording): update() (statement and read-back), delete() and bulkDelete() resolve the key via remoteColumn(object,'id','id'). Oracle prose (ORA-00904) stripped from both changesets and both test docblocks; code byte-identical to the PM-supplied git-am patches otherwise. Two patch changesets, both name SQLite/PostgreSQL/MySQL. Merged origin/main once (dc7f5c4b4, disjoint paths) instead of rebasing, since the branch is not claude/issue-* and a rebase would need a force-push.",
"tests": "Ablation on 0ceca395 via git restore --source=origin/main of the fix sources (tree only, restored from HEAD, status clean after): writes.test 5/5 fail with sequence-semantics.ts + bulk-operations.ts reverted; aggregate.test 8/8 fail with aggregation.ts + distinct.ts + date-bucket.ts reverted; 13/13 pass with the fix, re-run green on merge head 05ecc979. pnpm --filter @objectstack/driver-sql test: 240 files passed / 12 skipped, 3880 tests passed / 218 skipped (live PG/MySQL cells not provisioned locally; they run in Temporal Conformance on main). typecheck exit 0; eslint over the 8 changed ts files exit 0.",
"gates": "Local, 0ceca395 (base 30e0bd16): dispatch-gates --commands derived 68; all 68 run after a packages/** turbo build, every one exit 0; dispatch-gates --ran: 68 derived, 68 run, 0 NOT-MEASURED, 0 UNRUN. Merge of main touched no driver-sql path. PR check-runs: not waited at report time.",
"dogfood_battery": "n/a, no ! in the PR title and no narrowing arm",
"mcp_calls": "1, mcp__github__issue_read (#31340 body); no write tool",
"api_writes": "2 relay writes as objectstack-fleet[bot]: pr_create (draft #31349, assignee marchtian) and this os-dev-report comment; plus git push of the branch",
"open_questions": [],
"out_of_scope_findings": [
"filed by the PM as #31342: upsert() on a columnMap-ed object uses unmapped id as its ON CONFLICT target (no such column: id on a table without id) and as its read-back key (table with its own surrogate id: upsert of key 1 inserted CODE 1 but answered the row whose surrogate is 1)"
]
}
update(), delete() and bulkDelete() hard-code WHERE "id" = ?. On an external object whose primary key columnMap maps to another remote column, while the table also has a plain id column, a delete removes the wrong row, silently, on PostgreSQL, MySQL and SQLite. Item 2 (aggregate / distinct) errors loudly.
Raised to p1: the reach is narrow (write-enabled external objects with a mapped key), but item 1 destroys data without an error, and the seat already lands it ahead of the Oracle card for that reason. The lane and the in-flight claim are unchanged. finding removed.
PR #31349 merged through the queue as e2e6ec7dc9 at 2026-11-08T04:21:45Z, from head 05ecc979. It has no contract face (Clause-②: no, not governed), so the review of record is CI plus the seat read in the ACCEPT.
The fix resolves the key column through remoteColumn in update(), delete() and bulkDelete(). It also spells aggregate()'s group keys, aggregands and date buckets, and distinct()'s column, through the map. Pins: 5/5 for the write doors and 8/8 for the read doors, each red before the fix. Upsert and the paged-read tie-breaker go on in #31342.
Activity
objectstack-fleet commentedon Nov 8, 2026
epic:#27261#1 · marchtian · session_017ihL2c5z3q4h8inrn5kwqV
Claim: PM loop round 18 2026-11-08T02:55Z
Session:
session_017ihL2c5z3q4h8inrn5kwqV(epic PM); implementing card threadsession_017ihL2euiyuLXizr6FW32R3Account:
marchtianBranch:
claude/project-thread-on4xyn(the card thread's platform-assigned branch — measured platform exception toclaude/issue-<n>-<slug>)Worktree:
objectstack-issue-31340Domain:
domain:serverSeat:
epic:#27261#1(epic PM, which filed the card as an out-of-scope finding of #31328; nodomain:*seat)File surface:
packages/drivers/driver-sql/src/sql-driver.ts,sql-driver-aggregation.ts,sql-driver-distinct.ts, new sqlite pin tests underpackages/drivers/driver-sql/src/, and.changeset/**. Nopackages/spec/**, no Oracle code. Stop on any other breach.Container & model:
S,mode:thread,model: default tier (Fable unavailable; 田騏's order of 2026-10-29 19:39Z),effort: highClause-②: no — a driver fix to honour an existing contract (
external.columnMap); no published surface changesMetadata-protocol: none
Responsibility: the card body's five Task items (upsert is measure-and-report only)
Thread-read: none
Premise: by reading at
origin/main30e0bd1,update(),delete()andbulkDelete()in sql-driver.ts build.where('id', id)/.whereIn('id', ids)withoutremoteColumn; the #31328 thread measured the aggregate/distinct miss (sqlite refusal, Oracle ORA-00904) and the write-key miss (5 red pins before its fix).Serial constraints: #31328 carries the same two fixes as commits on its branch and merges main after this lands; #31327 is told not to touch these paths.
objectstack-fleet commentedon Nov 8, 2026
epic:#27261#1 · marchtian · session_017ihL2euiyuLXizr6FW32R3
os-dev-report
{ "issue": 31340, "status": "done", "branch": "claude/project-thread-on4xyn", "pr": "https://github.com/objectstack-ai/objectstack/pull/31349", "session": "session_017ihL2euiyuLXizr6FW32R3", "premise_still_valid": true, "summary": "Lands the two columnMap fixes from the Oracle card's branch alone off main, with nothing Oracle-specific. Commit f9a07140: aggregate() (GROUP BY, structured group key, date bucket, aggregand) and distinct() spell the remote column through remoteColumn; output keys, presentation and per-field registries stay the field; buildDateBucketExpr gains an optional column parameter (ported from the Oracle wip commit without the Oracle arm). Commit 8c643017 (+0ceca395 changeset wording): update() (statement and read-back), delete() and bulkDelete() resolve the key via remoteColumn(object,'id','id'). Oracle prose (ORA-00904) stripped from both changesets and both test docblocks; code byte-identical to the PM-supplied git-am patches otherwise. Two patch changesets, both name SQLite/PostgreSQL/MySQL. Merged origin/main once (dc7f5c4b4, disjoint paths) instead of rebasing, since the branch is not claude/issue-* and a rebase would need a force-push.", "tests": "Ablation on 0ceca395 via git restore --source=origin/main of the fix sources (tree only, restored from HEAD, status clean after): writes.test 5/5 fail with sequence-semantics.ts + bulk-operations.ts reverted; aggregate.test 8/8 fail with aggregation.ts + distinct.ts + date-bucket.ts reverted; 13/13 pass with the fix, re-run green on merge head 05ecc979. pnpm --filter @objectstack/driver-sql test: 240 files passed / 12 skipped, 3880 tests passed / 218 skipped (live PG/MySQL cells not provisioned locally; they run in Temporal Conformance on main). typecheck exit 0; eslint over the 8 changed ts files exit 0.", "gates": "Local, 0ceca395 (base 30e0bd16): dispatch-gates --commands derived 68; all 68 run after a packages/** turbo build, every one exit 0; dispatch-gates --ran: 68 derived, 68 run, 0 NOT-MEASURED, 0 UNRUN. Merge of main touched no driver-sql path. PR check-runs: not waited at report time.", "dogfood_battery": "n/a, no ! in the PR title and no narrowing arm", "mcp_calls": "1, mcp__github__issue_read (#31340 body); no write tool", "api_writes": "2 relay writes as objectstack-fleet[bot]: pr_create (draft #31349, assignee marchtian) and this os-dev-report comment; plus git push of the branch", "open_questions": [], "out_of_scope_findings": [ "filed by the PM as #31342: upsert() on a columnMap-ed object uses unmapped id as its ON CONFLICT target (no such column: id on a table without id) and as its read-back key (table with its own surrogate id: upsert of key 1 inserted CODE 1 but answered the row whose surrogate is 1)" ] }objectstack-fleet commentedon Nov 8, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: regrade · p2 → p1 ·
domain:server·area:data· 2026-11-08T03:56Z⛔ Not a claim.
update(),delete()andbulkDelete()hard-codeWHERE "id" = ?. On an external object whose primary keycolumnMapmaps to another remote column, while the table also has a plainidcolumn, a delete removes the wrong row, silently, on PostgreSQL, MySQL and SQLite. Item 2 (aggregate/distinct) errors loudly.findingremoved.Generated by Claude Code
objectstack-fleet commentedon Nov 8, 2026
epic:#27261#1 · marchtian · session_017ihL2c5z3q4h8inrn5kwqV
Landed · 2026-11-08T04:22Z
PR #31349 merged through the queue as
e2e6ec7dc9at 2026-11-08T04:21:45Z, from head05ecc979. It has no contract face (Clause-②: no, not governed), so the review of record is CI plus the seat read in the ACCEPT.The fix resolves the key column through
remoteColumninupdate(),delete()andbulkDelete(). It also spellsaggregate()'s group keys, aggregands and date buckets, anddistinct()'s column, through the map. Pins: 5/5 for the write doors and 8/8 for the read doors, each red before the fix. Upsert and the paged-read tie-breaker go on in #31342.