Commit 6908830
* fix(objectql)!: `engine.find`/`findOne` refuse an unmaterializable formula ORDER BY (#7095)
#6994 closed the SORT axis at the REST ingress (`assertSortFieldsExist`,
`400 INVALID_SORT`), covering everything reaching `findData`. A caller reaching
`engine.find()` / `engine.findOne()` directly passed through none of it, and a
`formula` ORDER BY there was dropped in silence. Measured on this change's base,
real `ObjectQL` over a driver that really sorts:
engine.find(o, { orderBy: [{ field: <formula>, order: 'asc' }] }) -> C A E B D
engine.find(o, { orderBy: [{ field: <formula>, order: 'desc' }] }) -> C A E B D
asc === desc (byte-identical)
Ruled 2026-08-10 on #7095: refuse at the public boundary with guidance prose,
never a silent drop. `assertOrderByIsMaterializable` refuses on both entry
points with the same `400 INVALID_SORT` and the same remedy sentence the two
ingress verdicts emit — pinned as an equality across all three doors, since
separate wordings is how #4256 and #6673 drifted apart.
The tolerance was to survive only behind a pinned internal path, and only if a
MEASURED internal call site relied on it. The sweep found none: every hardcoded
internal sort names a real stored column, and no shipped object declares a
`formula` field. So no internal path shipped, and a negative pin keeps one off
the public options shape.
The one author-reachable consumer is why ingress-only was not tenable: a saved
report's `query.orderBy` is forwarded verbatim into `engine.find` by
`plugin-reports`. One path deliberately does NOT become a refusal — a nested
`expand` sort raises it inside `expandRelatedRecords`, whose pre-existing
graceful-degradation catch swallows every expand failure, so that path moves
from silent to observable (a warning naming the field and the fix) rather than
refusing. Reversing that backstop is #3821's decision, not this card's; it is
measured and pinned as-is.
The ingress gate is untouched, and the engine door judges only the third verdict
— unknown and dotted names still reach the driver from a direct call, because
refusing those is a posture change on two further axes.
Registered in the ADR-0087 step-17 ledger as
`engine-find-formula-order-by-refused`; artifacts regenerated.
Refs #7095, #6994, #6924, #4226, #4256, #3821, ADR-0087, ADR-0112
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HJgGahRqaYPRJ2oKmk9Czc
* test(objectql): type the #7095 query-options sites instead of erasing them to `any`
The four call sites the #7095 pins added tripped the #4918 query-options-erasure
ratchet (test surface 249 -> 253). Fixed at the call sites, per the rule's own
prescription — the ceiling is unchanged and no pin is weakened.
Three were ON-contract and are now typed:
- the `it.each` sort table is `Array<[string, NonNullable<EngineQueryOptions['orderBy']>]>`,
so the three refused sorts are checked as the well-formed `SortNode[]` they
are. It is the FIELD they name that the engine refuses, never their shape,
and an `as any` there would have erased the one channel that enforces
`{ field, order }` on a direct engine call — the `direction`-vs-`order`
mistake #4674 is about.
- both `expand` sites drop the assertion entirely: `EngineQueryOptions.expand`
is `Record<string, QuerySchema>`, so the nested `{ orderBy }` was always
assignable and the cast was never buying anything.
One is DELIBERATELY off-contract — the negative pin that smuggles an opt-out
flag onto the public options bag — and is now `as unknown as EngineQueryOptions`
rather than a bare `as any`: it names the contract being bypassed, keeps the
rest of the call type-checked, and greps as an intentional act. That is exactly
the case #4918 carved the spelling out for, since the assertion's whole subject
is that the engine rejects the unknown key.
Refs #7095, #4918, #4674, #4721
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HJgGahRqaYPRJ2oKmk9Czc
---------
Co-authored-by: claude[bot] <noreply@anthropic.com>
1 parent 69f1a5f commit 6908830
7 files changed
Lines changed: 492 additions & 32 deletions
File tree
- .changeset
- docs
- packages
- metadata-protocol/src
- objectql/src
- spec
- src/migrations
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
446 | 446 | | |
447 | 447 | | |
448 | 448 | | |
| 449 | + | |
| 450 | + | |
| 451 | + | |
| 452 | + | |
| 453 | + | |
| 454 | + | |
| 455 | + | |
| 456 | + | |
| 457 | + | |
449 | 458 | | |
450 | 459 | | |
451 | 460 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
5112 | 5112 | | |
5113 | 5113 | | |
5114 | 5114 | | |
5115 | | - | |
5116 | | - | |
5117 | | - | |
5118 | | - | |
5119 | | - | |
5120 | | - | |
5121 | | - | |
5122 | | - | |
5123 | | - | |
5124 | | - | |
| 5115 | + | |
| 5116 | + | |
| 5117 | + | |
| 5118 | + | |
| 5119 | + | |
| 5120 | + | |
| 5121 | + | |
| 5122 | + | |
| 5123 | + | |
| 5124 | + | |
| 5125 | + | |
| 5126 | + | |
| 5127 | + | |
| 5128 | + | |
| 5129 | + | |
| 5130 | + | |
| 5131 | + | |
| 5132 | + | |
| 5133 | + | |
| 5134 | + | |
| 5135 | + | |
| 5136 | + | |
| 5137 | + | |
| 5138 | + | |
| 5139 | + | |
| 5140 | + | |
| 5141 | + | |
| 5142 | + | |
| 5143 | + | |
| 5144 | + | |
| 5145 | + | |
5125 | 5146 | | |
5126 | 5147 | | |
5127 | 5148 | | |
| |||
5239 | 5260 | | |
5240 | 5261 | | |
5241 | 5262 | | |
5242 | | - | |
5243 | | - | |
5244 | | - | |
| 5263 | + | |
| 5264 | + | |
| 5265 | + | |
| 5266 | + | |
| 5267 | + | |
| 5268 | + | |
| 5269 | + | |
| 5270 | + | |
| 5271 | + | |
| 5272 | + | |
| 5273 | + | |
| 5274 | + | |
| 5275 | + | |
| 5276 | + | |
| 5277 | + | |
| 5278 | + | |
| 5279 | + | |
| 5280 | + | |
5245 | 5281 | | |
5246 | 5282 | | |
5247 | 5283 | | |
| |||
0 commit comments