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
Commit e7b6ff5
Browse filesBrowse the repository at this point in the historyBrowse files
fix(objectql,metadata-core): refuse a by-id update whose scalar where.id names a different row than the payload id (#11142)
update(obj, { id: 'rec_1', ... }, { where: { id: 'rec_2' } }) used to bind
rec_1 and silently discard the where.id predicate — the one
unhonoured-predicate shape #11009's refusal deliberately left standing,
because refusing it reverses the #5748-pinned verdict 'a SCALAR data.id
still wins over a scalar where.id'. The maintainer ruling on #11142
(2026-08-23, option A) authorizes that reversal for the UNEQUAL truthy
scalar shape only.
resolveEngineUpdateDispatch now rejects the conflict with a message naming
both ids, decorated code UPDATE_ID_MISMATCH + status 400 (the
recordNotFoundError convention; code registered in the ADR-0112 ledger
under @objectstack/objectql). Both throwers — assertEngineUpdateDispatch
(every pinned fake) and ObjectQL.update — go through one shared
engineUpdateDispatchRejectError, so fakes and the real engine refuse
identically. The #5748/#11009 refusals stay plain Errors, byte-identical.
Pin-reversal discipline: the interrupted pin flips to a refusal pin in
ENGINE_UPDATE_DISPATCH_CASES and in the #6435 contrast pins — never
deleted; the equal-ids spelling (REST folds the path id into the payload)
gains its own passing pin; falsy and non-scalar where.id boundaries keep
their pre-existing verdicts, pinned so the refusal cannot creep past the
ruled scope.
Fixes#11142
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RfyXxZ2WPjcjhuXpiQQc3y
// spelling and objectstack#5748 left it exactly as it was.
328
462
{what: 'id carried in the data payload, no where at all',data: {id: 'rec_1',title: 'x'},options: undefined,expect: 'by-id',expectId: 'rec_1'},
329
463
{what: 'a SCALAR data.id still wins over an explicit multi:true',data: {id: 'rec_1',title: 'x'},options: {multi: true},expect: 'by-id',expectId: 'rec_1'},
330
-
{what: 'a SCALAR data.id still wins over a scalar where.id',data: {id: 'rec_1',title: 'x'},options: {where: {id: 'rec_2'}},expect: 'by-id',expectId: 'rec_1'},
464
+
// [#11142] The REVERSED #5748 pin. This row read
465
+
// `expect: 'by-id', expectId: 'rec_1'` from #5748 until the maintainer
466
+
// ruling on #11142 (2026-08-23) flipped the UNEQUAL shape to a refusal: a
467
+
// truthy scalar `where.id` naming a different row than the payload id is a
468
+
// predicate the by-id path would silently discard — the last silent member
469
+
// of the #5748/#11009 dropped-declaration family. The pin flips, it does
470
+
// not disappear; the EQUAL spelling keeps its own passing pin right below.
471
+
{what: 'a SCALAR data.id beside a DIFFERENT scalar where.id — refused, no longer silently wins (#11142 reverses the #5748 pin for the unequal shape)',data: {id: 'rec_1',title: 'x'},options: {where: {id: 'rec_2'}},expect: 'reject'},
472
+
// [#11142] The equal-ids spelling stays honoured: the REST ingress folds
473
+
// the path id into the payload (`{ ...data, id: request.id }` beside
474
+
// `where: { id: request.id }`), so redundant-but-agreeing is a NORMAL
475
+
// spelling, not a conflict.
476
+
{what: 'data.id === where.id — the redundant-but-agreeing spelling (REST folds the path id into the payload) stays by-id (#11142)',data: {id: 'rec_1',title: 'x'},options: {where: {id: 'rec_1'}},expect: 'by-id',expectId: 'rec_1'},
477
+
// [#11142] `multi: true` cannot rescue the conflict: the payload id outranks
478
+
// `multi` (#5748), so the call is still a by-id write carrying a where.id it
479
+
// can never honour.
480
+
{what: 'a SCALAR data.id beside a DIFFERENT scalar where.id and multi:true — still refused, the payload id outranks multi (#11142)',data: {id: 'rec_1',title: 'x'},options: {where: {id: 'rec_2'},multi: true},expect: 'reject'},
331
481
// ── The payload's scalar test (objectstack#5748). A non-scalar `data.id`
332
482
// names no row, so it stops shadowing everything under it: the decision
333
483
// falls through to `where.id`, then `multi`, then `reject`. Before #5748
0 commit comments