Skip to content

Commit ca67776

Browse files
committed
docs(spec): keep SortNodeSchema's page description short; rationale moves to line comments
`build-docs.ts` takes the FIRST `/** */` block in a `.zod.ts` file as the reference page's description, and joins every line of it with a blank line. The long #4721 rationale therefore rendered as a 56-line wall at the top of `content/docs/references/data/query.mdx`, where a customer reads what a sort node IS — not why one schema in the file is strict. The prose is unchanged, it is just `//` instead of `/** */` so the generator cannot pick it up, with a note at the top saying why it must stay that way. Regenerated: query.mdx (two lines), and the two skill reference indexes, which grew transitive entries because query.zod.ts now imports shared/strict-object. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ehu85kbvMcrNTUJjwxvLJ9
1 parent 6e55269 commit ca67776

4 files changed

Lines changed: 55 additions & 35 deletions

File tree

content/docs/references/data/query.mdx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ description: Query protocol schemas
77

88
Sort Node
99

10-
Represents "Order By".
10+
Represents "Order By" — one `\{ field, order \}` pair. Unknown keys are
11+
12+
REJECTED (#4721); spell the direction `order`, never `direction`.
1113

1214
<Callout type="info">
1315
**Source:** `packages/spec/src/data/query.zod.ts`

packages/spec/src/data/query.zod.ts

Lines changed: 40 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -9,41 +9,47 @@ import { strictObject } from '../shared/strict-object';
99

1010
/**
1111
* Sort Node
12-
* Represents "Order By" — one `{ field, order }` pair.
13-
*
14-
* **Closed against unknown keys (#4721, #4001).** This is the one site in
15-
* `query.zod.ts` carved out of the file's blanket `open` classification, and the
16-
* carve-out is what the shape earns: the rest of the file is the query DIALECT,
17-
* where user data flows through predicate values, while a sort node is a closed
18-
* two-key tuple with no user-data face at all. Classing the whole file was the
19-
* imprecise thing, not closing this schema.
20-
*
21-
* What it stops, measured on `main` before the change:
22-
*
23-
* ```
24-
* SortNodeSchema.parse({ field: 'updated_at', direction: 'desc' })
25-
* → { field: 'updated_at', order: 'asc' }
26-
* ```
27-
*
28-
* `direction` was stripped, `order` fell back to its `asc` default, and the sort
29-
* ran in the OPPOSITE direction under an ordinary success. Paired with `limit` —
30-
* which is how a caller asks for "the latest N" — that is not a reordered page
31-
* but a DIFFERENT SET OF ROWS, with no signal anywhere in the response.
32-
*
33-
* `direction` gets a named alias rather than a distance-based suggestion because
34-
* it is not a typo: it is `IReportService.orderBy`'s live vocabulary
35-
* (`contracts/report-service.ts`), a genuinely different contract that
36-
* `plugin-auth/objectql-adapter.ts` already translates by hand. Edit distance
37-
* can never reach a different WORD for the same intent — the `visibleWhen →
38-
* visible` class (see `shared/strict-object.ts`) — so only a hand-written entry
39-
* puts the prescription in the author's hands.
40-
*
41-
* The wire-facing half of the same door is `normalizeSortNodes`
42-
* (`metadata-protocol/src/protocol.ts`), which rejects `direction` by name with
43-
* `400 INVALID_SORT` before a request ever reaches this schema. Both were closed
44-
* in one change deliberately: closing only the schema is the door asymmetry
45-
* #1535 shipped and #4522 had to come back for.
12+
* Represents "Order By" — one `{ field, order }` pair. Unknown keys are
13+
* REJECTED (#4721); spell the direction `order`, never `direction`.
4614
*/
15+
// ⚠️ Keep the block above short: `build-docs.ts` takes the FIRST JSDoc block in
16+
// the file as this page's description, so the rationale below is line comments.
17+
//
18+
// ─── Why this one schema is strict while the rest of the file is not (#4721) ──
19+
//
20+
// `query.zod.ts` is classed `open` in the #4001 strictness ledger, and
21+
// `SortNodeSchema` is carved out of that blanket. The carve-out is what the
22+
// shape earns: the rest of the file is the query DIALECT, where user data flows
23+
// through predicate values, while a sort node is a closed two-key tuple with no
24+
// user-data face at all. Classing by FILE was the imprecise instrument here, not
25+
// closing this schema.
26+
//
27+
// What the closure stops, measured on `main` before the change:
28+
//
29+
// SortNodeSchema.parse({ field: 'updated_at', direction: 'desc' })
30+
// → { field: 'updated_at', order: 'asc' }
31+
//
32+
// `direction` was stripped, `order` fell back to its `asc` default, and the sort
33+
// ran in the OPPOSITE direction under an ordinary success. Paired with `limit` —
34+
// which is how a caller asks for "the latest N" — that is not a reordered page
35+
// but a DIFFERENT SET OF ROWS, with no signal anywhere in the response.
36+
//
37+
// `direction` gets a named alias rather than a distance-based suggestion because
38+
// it is not a typo: it is `IReportService.orderBy`'s live vocabulary
39+
// (`contracts/report-service.ts`), a genuinely different contract that
40+
// `plugin-auth/objectql-adapter.ts` already translates by hand. Edit distance
41+
// can never reach a different WORD for the same intent — the `visibleWhen →
42+
// visible` class (see `shared/strict-object.ts`) — so only a hand-written entry
43+
// puts the prescription in the author's hands.
44+
//
45+
// The wire-facing half of the same door is `normalizeSortNodes`
46+
// (`metadata-protocol/src/protocol.ts`), which rejects `direction` by name with
47+
// `400 INVALID_SORT` before a request ever reaches this schema. Both were closed
48+
// in one change deliberately: closing only the schema is the door asymmetry
49+
// #1535 shipped and #4522 had to come back for.
50+
//
51+
// Deliberately NOT taken here: `BaseQuerySchema`'s own top level stays
52+
// non-strict. That is #4001's to schedule.
4753
export const SortNodeSchema = lazySchema(() => strictObject(
4854
{
4955
surface: 'this sort node',

skills/objectstack-api/references/_index.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,16 @@ from `node_modules` — there is no local copy in the skill bundle.
2424
- `node_modules/@objectstack/spec/src/api/error-code-ledger.zod.ts` — Error-Code Ledger (ADR-0112 D3).
2525
- `node_modules/@objectstack/spec/src/api/realtime-shared.zod.ts` — Realtime Shared Protocol
2626
- `node_modules/@objectstack/spec/src/data/data-engine.zod.ts` — Data Engine Protocol
27+
- `node_modules/@objectstack/spec/src/data/field.zod.ts` — Field Type Enum
2728
- `node_modules/@objectstack/spec/src/data/filter.zod.ts` — Unified Query DSL Specification
2829
- `node_modules/@objectstack/spec/src/data/query.zod.ts` — Sort Node
2930
- `node_modules/@objectstack/spec/src/kernel/execution-context.zod.ts` — Execution Context Schema
31+
- `node_modules/@objectstack/spec/src/kernel/metadata-protection.zod.ts` — Metadata Protection Model — Phase 1 (ADR-0010)
3032
- `node_modules/@objectstack/spec/src/security/explain.zod.ts`[ADR-0090 D6] Access-explanation contract — `explain(principal, object,
33+
- `node_modules/@objectstack/spec/src/shared/expression.zod.ts` — Expression Protocol
3134
- `node_modules/@objectstack/spec/src/shared/http.zod.ts` — Shared HTTP Schemas
3235
- `node_modules/@objectstack/spec/src/shared/identifiers.zod.ts` — System Identifier Schema
36+
- `node_modules/@objectstack/spec/src/shared/suggestions.zod.ts` — "Did you mean?" Suggestion Utilities
3337

3438
## How to read these
3539

skills/objectstack-query/references/_index.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,14 @@ from `node_modules` — there is no local copy in the skill bundle.
1313
- `node_modules/@objectstack/spec/src/data/filter.zod.ts` — Unified Query DSL Specification
1414
- `node_modules/@objectstack/spec/src/data/query.zod.ts` — Sort Node
1515

16+
## Transitive dependencies
17+
18+
- `node_modules/@objectstack/spec/src/data/field.zod.ts` — Field Type Enum
19+
- `node_modules/@objectstack/spec/src/kernel/metadata-protection.zod.ts` — Metadata Protection Model — Phase 1 (ADR-0010)
20+
- `node_modules/@objectstack/spec/src/shared/expression.zod.ts` — Expression Protocol
21+
- `node_modules/@objectstack/spec/src/shared/identifiers.zod.ts` — System Identifier Schema
22+
- `node_modules/@objectstack/spec/src/shared/suggestions.zod.ts` — "Did you mean?" Suggestion Utilities
23+
1624
## How to read these
1725

1826
1. The schemas are runtime Zod definitions. Use `Read` on the absolute

0 commit comments

Comments
 (0)