Found while implementing #10474 (the SORT axis' provenance wiring). ⛔ Filed, not fixed — out of that card's scope, which is a packages/lint rule shape.
The observation
#10474 added sort-field-unprovisioned, an author-time WARNING about a list view ordering by a registry-injected anchor on an ADR-0015 external object. That rule's whole justification is a runtime fact: neither runtime door on the SORT axis refuses such an ORDER BY, so the authoring gate is the only door available for it.
Measured on claude/issue-10474-sort-axis-provenance with a real SqlDriver over better-sqlite3, the object declared exactly as examples/app-showcase/src/data/objects/external/customer.object.ts declares it, against a remote customers table carrying [id, name, email, region, lifetime_value] and none of the seven injected anchors:
orderBy name asc -> [c1,c2,c3] desc -> [c3,c2,c1] (a real column: reverses)
orderBy created_at asc -> [c1,c2,c3] desc -> [c1,c2,c3] asc === desc, 3 rows, no error
orderBy owner_id asc -> [c1,c2,c3] desc -> [c1,c2,c3] asc === desc, 3 rows, no error
The mechanism, read at both doors:
An injected anchor is a datetime or a lookup, so it clears every verdict and reaches the driver, where the #3821 unknown-column backstop returns the rows unordered under a success.
Why it is worth pinning
Nothing in packages/objectql or packages/drivers/driver-sql asserts this. query-expression-conformance.test.ts pins the four doors' agreement about wording and the formula axis, not the unprovisioned-anchor case. So:
- if a future change widens either door to refuse an unprovisioned anchor, the new lint WARNING silently becomes wrong — it would describe a silent degradation that no longer happens, on a path that is now a loud
400, and it would duplicate a runtime verdict at the wrong severity; - if a future change narrows the backstop into a hard error instead, the same;
- either way the lint rule's docblock (which records the measurement above as its justification) goes stale with nothing turning red.
This is the shape #7738 pinned for the tenant-scope half of the same defect family (packages/objectql/src/engine-external-tenant-scope.test.ts asserts the premise it rests on rather than assuming it, and says so in its header).
Context: why no sweep observed this before
examples/app-showcase declares two federated objects (showcase_ext_customer, showcase_ext_order) and no list view, view record or page binds either of them — verified by grep across examples/. So the federated read path has no authored SORT or SEARCH surface anywhere in the example apps, which is precisely why the #8999 census could record the SORT gap only as a code-path reading and never as an observed missing finding.
Suggested shape (not prescriptive)
A test in packages/drivers/driver-sql (which already has the federated SQLite harness — see sql-driver-external-remote-name.test.ts) asserting the asc === desc signature above, plus the reversing baseline on a real column that makes it a dropped sort rather than a coincidence. The baseline leg is the load-bearing half: without it a driver that stopped sorting entirely would keep the test green.
Related: #10474 (the lint rule resting on this) · #8116 / #7865 (the provenance derivation) · #6994 / #7095 (the two doors) · #7738 (the pinned-premise precedent) · #3821 (the backstop)
Generated by Claude Code
Found while implementing #10474 (the SORT axis' provenance wiring). ⛔ Filed, not fixed — out of that card's scope, which is a
packages/lintrule shape.The observation
#10474 added
sort-field-unprovisioned, an author-time WARNING about a list view ordering by a registry-injected anchor on an ADR-0015externalobject. That rule's whole justification is a runtime fact: neither runtime door on the SORT axis refuses such an ORDER BY, so the authoring gate is the only door available for it.Measured on
claude/issue-10474-sort-axis-provenancewith a realSqlDriverover better-sqlite3, the object declared exactly asexamples/app-showcase/src/data/objects/external/customer.object.tsdeclares it, against a remotecustomerstable carrying[id, name, email, region, lifetime_value]and none of the seven injected anchors:The mechanism, read at both doors:
assertSortFieldsExist(packages/metadata-protocol/src/protocol.ts, A non-dottedorderBynaming aformulafield answers 200 in arbitrary order — the sort is silently dropped (measured on driver-sql + driver-memory) #6994) — precedenceunknown>dotted> unmaterializable. An injected anchor is ingate.known(the registry injected it into the served schema), it is undotted, and the third verdict filtersUNMATERIALIZED_SORT_TYPES, which isformulaalone.assertOrderByIsMaterializable(packages/objectql/src/engine.ts,engine.find()still drops aformulaORDER BY silently — decide whether the engine refuses or keeps its internal-caller tolerance #7095) —schema.fields[f]?.type === 'formula', same narrowing.An injected anchor is a
datetimeor alookup, so it clears every verdict and reaches the driver, where the #3821 unknown-column backstop returns the rows unordered under a success.Why it is worth pinning
Nothing in
packages/objectqlorpackages/drivers/driver-sqlasserts this.query-expression-conformance.test.tspins the four doors' agreement about wording and the formula axis, not the unprovisioned-anchor case. So:400, and it would duplicate a runtime verdict at the wrong severity;This is the shape #7738 pinned for the tenant-scope half of the same defect family (
packages/objectql/src/engine-external-tenant-scope.test.tsasserts the premise it rests on rather than assuming it, and says so in its header).Context: why no sweep observed this before
examples/app-showcasedeclares two federated objects (showcase_ext_customer,showcase_ext_order) and no list view, view record or page binds either of them — verified by grep acrossexamples/. So the federated read path has no authored SORT or SEARCH surface anywhere in the example apps, which is precisely why the #8999 census could record the SORT gap only as a code-path reading and never as an observed missing finding.Suggested shape (not prescriptive)
A test in
packages/drivers/driver-sql(which already has the federated SQLite harness — seesql-driver-external-remote-name.test.ts) asserting theasc === descsignature above, plus the reversing baseline on a real column that makes it a dropped sort rather than a coincidence. The baseline leg is the load-bearing half: without it a driver that stopped sorting entirely would keep the test green.Related: #10474 (the lint rule resting on this) · #8116 / #7865 (the provenance derivation) · #6994 / #7095 (the two doors) · #7738 (the pinned-premise precedent) · #3821 (the backstop)
Generated by Claude Code