Commit 54818fe
docs(api): sort spellings are slot-specific, not equivalent across routes (#19055)
Fixes #19027
Clause-②: no
## What the page said, and what the runtime does
`content/docs/api/data-api.mdx` taught three `POST /data/:object/query`
sort
spellings as "all equivalent". Reproduced at the exact input
`rest-server.ts`
builds — `FindDataRequestSchema.safeParse({ object, query: { ...body,
object } })`
— and again over real HTTP against a booted `examples/app-crm`:
| documented bag | schema-level | over HTTP |
|:---|:---|:---|
| `{"orderBy": [{"field": "created_at", "order": "desc"}]}` ⭐ lit
control | `success: true` | **200**, rows descending |
| `{"orderBy": ["-created_at"]}` | `query.orderBy.0` · `invalid_type` ·
"expected object, received string" | **400** `VALIDATION_FAILED` |
| `{"orderBy": {"created_at": "desc"}}` | `query.orderBy` ·
`invalid_type` · "expected array, received object" | **400**
`VALIDATION_FAILED` |
The lit control returns 200, so the instrument is sound and the defect
is the
page's, as the card diagnosed.
## Where the card's diagnosis needed correcting
The card says the record map and the shorthand array "are transport-slot
values
— they have to arrive on `$orderby` / `sort`". Measured, that holds for
the map
and **not** for the shorthand array. Every POST-body slot, one tree:
| body value | `orderBy` | `$orderby` | `sort` |
|:---|:---|:---|:---|
| `[{"field": "created_at", "order": "desc"}]` | 200 | 200 | 200 |
| `{"created_at": "desc"}` (and `1` / `-1`) | 400 | 200 | 200 |
| `["-created_at"]` | 400 | 400 | 400 |
| `"-created_at"` (bare string) | 400 | 400 | 400 |
`QueryTransportParamsSchema.$orderby` is `DataEngineSortSchema`, which
declares
the record map and `SortNode[]` and no string form at all. So the
shorthand
string is not a transport-slot value on this route either — it is a
**querystring** spelling, read by `normalizeSortNodes` at the ingress.
Measured
on the querystring: `?sort=-created_at`, `?$orderby=-created_at` and
`?orderBy=-created_at` all sort; the two JSON shapes sent there are
`400 INVALID_SORT`, because that route parses no JSON in this slot and
reads the
whole value as one field name.
Had the page simply been trimmed to the card's diagnosis, a reader sent
to
`{"$orderby": ["-created_at"]}` would have got the same 400 from a
different
line.
## The fix
The sentence is replaced by a per-route, per-slot table plus two short
paragraphs. Every cell in it is a measurement, not a reading of the
schema.
The page's only mention of `POST /data/:object/query` was inside the
sentence
being replaced, so the table is also now that route's only description
here.
The status-code split is stated per route as well: a wrong *shape* in a
body is
`400 VALIDATION_FAILED` with the path in `fields` (`query.orderBy.0`,
`query.orderBy.0.order`) and never reaches the field check, while an
unknown
field name is `400 INVALID_SORT` on either route.
## Verification
* **Reproduction, schema level** — the three bags at the rest-server
input, plus
the full slot matrix above and the malformed-shape cases.
* **Reproduction, HTTP level** — `pnpm dev:crm -- --fresh -p PORT` (a
random high port), signed in
as the seeded dev admin, `GET /data/crm_account` and
`POST /data/crm_account/query`. Ascending/descending row order is the
lit
control on every 200 cell: `['Acme Corp','Globex Ltd','Initech']` vs
`['Initech','Globex Ltd','Acme Corp']`, so an accepted-but-unapplied
sort
could not read as a pass.
* **Post-condition probe, written before the edit** — it parses the new
table
and asserts each cell's verdict token equals the measured one, that the
four
spellings are still *shown* (so deleting them would fail it), and that
no
cross-route equivalence wording survives. It reports 8 failures against
the
pre-edit page and passes against the edited one; both legs were run with
the
same scoped instrument.
* **Derived gates** — `node scripts/pm/dispatch-gates.mjs --commands
--repo
objectstack-ai/objectstack`, 39 commands, all run; exits in the report.
## No changeset — measured, not asserted
No published package contains `content/docs/api/data-api.mdx`: over the
70
non-private manifests in the tree, zero sit at or above that path.
Positive
control, same method: `packages/spec/dist/index.js` resolves into
`@objectstack/spec`, whose `files[]` ships `dist`. `apps/docs`, which
renders
this tree, is `private: true`. So nothing published moves and the change
takes
`skip-changeset`.
## Acceptance notes
* `#18977` is not addressed here, and `PR #19018` is untouched by this
diff —
this PR edits one file, that one edits four, and they do not overlap. No
`packages/spec/**` path is in this diff: the measurement showed the
defect is
the documentation's, which is the branch the dispatch reserved for
staying on
this page.
* Noted, not filed: `packages/spec/src/api/odata.zod.ts` already carries
the
correct account of this split in a schema comment (the `#18977`
cross-reference), including the sentence that the string forms are
served by
`normalizeSortNodes` and not by the body door. The customer-facing page
had
no pointer to it in either direction. Successor: the next PR to touch
that
schema comment or this page.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_017ef78bLdybu3AffehKkhfk
---
_Generated by [Claude
Code](https://claude.ai/code/session_017ef78bLdybu3AffehKkhfk)_
Co-authored-by: Claude <noreply@anthropic.com>1 parent 0a56d3b commit 54818fe
1 file changed
Lines changed: 25 additions & 6 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
114 | 114 | | |
115 | 115 | | |
116 | 116 | | |
117 | | - | |
118 | | - | |
119 | | - | |
120 | | - | |
121 | | - | |
122 | | - | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
123 | 142 | | |
124 | 143 | | |
125 | 144 | | |
| |||
0 commit comments