Surfaced by the os-dev working objectui#9554, and filed by the domain:ui @ objectui execution seat 3 (session_01Xm4WFhEe5mwcgyqHjxR2hn). ⛔ Not graded and ⛔ not routed by me — domain:*, type and priority:* are the triage seat's sole production. Filed here rather than in objectui because the fix lands in packages/spec.
This is the redirect target for objectui#9554, whose premise this measurement falsified.
$orderby is declared TWICE, incompatibly, with no cross-reference in either direction
Every reading below was re-verified at source by this seat on origin/main = 26c73fb, ⛔ not taken from the dev report.
| declaration |
where |
accepts |
refuses |
ODataQuerySchema.$orderby |
packages/spec/src/api/odata.zod.ts:119-122 |
string, string[] |
the object array, the record maps |
QueryTransportParamsSchema.$orderby = DataEngineSortSchema |
packages/spec/src/data/data-engine.zod.ts:853 → :43-47 |
Record<string,'asc'|'desc'>, Record<string,1|-1>, SortNode[] ({field, order}) |
string and string[] — deliberately |
Verbatim, odata.zod.ts:119:
$orderby: z.union([
z.string(), // "name desc"
z.array(z.string()), // ["name desc", "email asc"]
]).optional().describe('Sort order'),
Verbatim, data-engine.zod.ts:43:
export const DataEngineSortSchema = lazySchema(() => z.union([
z.record(z.string(), z.enum(['asc', 'desc'])),
z.record(z.string(), z.union([z.literal(1), z.literal(-1)])),
z.array(SortNodeSchema)
]).describe('Sort order definition'));
The exclusion is stated as deliberate in the second file's own source (data-engine.zod.ts, ~:826), verbatim:
⛔ Three shapes are deliberately NOT declared, because lowering them means PARSING — and a second parser beside the door's is how one rule gets two implementations that disagree: a JSON-encoded $filter string, the OData $orderby / sort expression string ('name desc', '-created_at') and its string[] form.
⇒ the two declarations are not merely different, they are complementary refusals: each accepts exactly what the other rejects.
Which one grades a query bag
packages/spec/src/api/protocol.zod.ts:1904 — query: QueryWithTransportSchema.optional() — reaches QueryTransportParamsSchema, hence DataEngineSortSchema. Per the dev's reading (⚠️ not independently re-derived by this seat): ODataQuerySchema's only in-repo consumer is the buildUrl helper inside its own file, so it grades no runtime door today.
Why this is a class-(c) trap and not a tidiness item
An author — an AI author especially — reads one of the two and writes a value the other refuses:
- following
ODataQuerySchema ⇒ writes $orderby: 'name desc' into a stored query or an RPC body ⇒ refused by the schema that actually grades it;
- following
DataEngineSortSchema ⇒ writes [{field, order}] ⇒ refused by the OData schema.
Neither declaration points at the other, so reading one of them carefully and completely still produces the wrong answer, with no signal that a second declaration exists.
⭐ The measured cost, which is what makes this worth a card
objectui#9554 is that failure, already paid for. A competent seat read ODataQuerySchema, correctly quoted it, and concluded that a shipped object-grid producer was sending an undeclared shape. It was filed, triaged, graded type Bug priority:p2, and dispatched. The measurement then showed the producer was sending the canonical shape all along, and that the "correct" shape the card would have converged it onto is the one the governing declaration deliberately refuses. ⇒ one filing seat, one triage pass and one dispatch spent, and a fix that would have been a regression was one premise-check away from being written.
Options (input for whoever rules, ⛔ not a ruling by this seat)
The dev's four, reproduced because they are the useful framing — with the caveat that this seat has not run the four-axis analysis either, because the decision belongs to this repo's domain:spec lane, not to an objectui seat:
- A — document
ODataQuerySchema as a non-governing OData-compatibility surface and cross-reference it to DataEngineSortSchema as the declaration that grades query bags. Costs no runtime behaviour and kills the misreading class. (the dev's recommendation)
- B — widen
ODataQuerySchema.$orderby to include the object array. Makes the two agree on this key, but the transport schema still refuses the string forms OData declares, so opposite answers remain derivable on a different shape.
- C — widen
DataEngineSortSchema to accept the string forms. ⚠️ Argued against in the source itself, in the sentence quoted above.
- D — change nothing in the spec and correct only objectui#9554's framing. Leaves the dual declaration for the next reader.
Dedup words
orderby dual declaration · ODataQuerySchema DataEngineSortSchema disagree · odata.zod orderby narrower than transport · query-transport refuses orderby string · $orderby declared twice spec
⛔ Not deduped by me (filer attaches the words, triage runs them). ⚠️ Any zero needs a lit control, and dedup must include CLOSED cards.
Provenance
out_of_scope_findings of the objectui#9554 dev report (objectui#9554 comment, session session_01Xm4WFhEe5mwcgyqHjxR2hn). The four schema readings and the protocol binding above were re-verified at source by the filing seat on origin/main = 26c73fb; the "only consumer is buildUrl" reading and the runtime-ingress legs are the dev's and are ⚠️ labelled as such rather than adopted.
Generated by Claude Code
Surfaced by the
os-devworking objectui#9554, and filed by thedomain:ui@ objectui execution seat 3 (session_01Xm4WFhEe5mwcgyqHjxR2hn). ⛔ Not graded and ⛔ not routed by me —domain:*,typeandpriority:*are the triage seat's sole production. Filed here rather than in objectui because the fix lands inpackages/spec.This is the redirect target for objectui#9554, whose premise this measurement falsified.
$orderbyis declared TWICE, incompatibly, with no cross-reference in either directionEvery reading below was re-verified at source by this seat on
origin/main=26c73fb, ⛔ not taken from the dev report.ODataQuerySchema.$orderbypackages/spec/src/api/odata.zod.ts:119-122string,string[]QueryTransportParamsSchema.$orderby=DataEngineSortSchemapackages/spec/src/data/data-engine.zod.ts:853→:43-47Record<string,'asc'|'desc'>,Record<string,1|-1>,SortNode[]({field, order})stringandstring[]— deliberatelyVerbatim,
odata.zod.ts:119:Verbatim,
data-engine.zod.ts:43:The exclusion is stated as deliberate in the second file's own source (
data-engine.zod.ts, ~:826), verbatim:⇒ the two declarations are not merely different, they are complementary refusals: each accepts exactly what the other rejects.
Which one grades a query bag
packages/spec/src/api/protocol.zod.ts:1904—query: QueryWithTransportSchema.optional()— reachesQueryTransportParamsSchema, henceDataEngineSortSchema. Per the dev's reading (ODataQuerySchema's only in-repo consumer is thebuildUrlhelper inside its own file, so it grades no runtime door today.Why this is a class-(c) trap and not a tidiness item
An author — an AI author especially — reads one of the two and writes a value the other refuses:
ODataQuerySchema⇒ writes$orderby: 'name desc'into a stored query or an RPC body ⇒ refused by the schema that actually grades it;DataEngineSortSchema⇒ writes[{field, order}]⇒ refused by the OData schema.Neither declaration points at the other, so reading one of them carefully and completely still produces the wrong answer, with no signal that a second declaration exists.
⭐ The measured cost, which is what makes this worth a card
objectui#9554 is that failure, already paid for. A competent seat read
ODataQuerySchema, correctly quoted it, and concluded that a shippedobject-gridproducer was sending an undeclared shape. It was filed, triaged, gradedtype Bugpriority:p2, and dispatched. The measurement then showed the producer was sending the canonical shape all along, and that the "correct" shape the card would have converged it onto is the one the governing declaration deliberately refuses. ⇒ one filing seat, one triage pass and one dispatch spent, and a fix that would have been a regression was one premise-check away from being written.Options (input for whoever rules, ⛔ not a ruling by this seat)
The dev's four, reproduced because they are the useful framing — with the caveat that this seat has not run the four-axis analysis either, because the decision belongs to this repo's
domain:speclane, not to an objectui seat:ODataQuerySchemaas a non-governing OData-compatibility surface and cross-reference it toDataEngineSortSchemaas the declaration that grades query bags. Costs no runtime behaviour and kills the misreading class. (the dev's recommendation)ODataQuerySchema.$orderbyto include the object array. Makes the two agree on this key, but the transport schema still refuses the string forms OData declares, so opposite answers remain derivable on a different shape.DataEngineSortSchemato accept the string forms.Dedup words
orderby dual declaration·ODataQuerySchema DataEngineSortSchema disagree·odata.zod orderby narrower than transport·query-transport refuses orderby string·$orderby declared twice spec⛔ Not deduped by me (filer attaches the words, triage runs them).⚠️ Any zero needs a lit control, and dedup must include CLOSED cards.
Provenance
out_of_scope_findingsof the objectui#9554 dev report (objectui#9554 comment, sessionsession_01Xm4WFhEe5mwcgyqHjxR2hn). The four schema readings and the protocol binding above were re-verified at source by the filing seat onorigin/main=26c73fb; the "only consumer isbuildUrl" reading and the runtime-ingress legs are the dev's and areGenerated by Claude Code