Found while building the #8999 consumer census (PR on claude/issue-8999-system-fields-spread-sweep). ⛔ Filed, not fixed — out of that card's scope, which is the census instrument.
What the census turned up
The census enumerates every consumer of the blanket SYSTEM_FIELDS union by module graph and records, per consumer, whether it asks the #8116 provenance question (unprovisionedInjectedColumnsFor / indexUnprovisionedAnchors / unprovisionedAnchorCause). Two of the ten rule-file consumers reference none of them:
| consumer | reaches the union by | asks provenance |
|---|
validate-sortable-fields.ts | direct | no |
validate-translation-references.ts | derived (spread) + direct | no |
Both are recorded in the census ledger with a reason rather than silently exempted, so this issue is about confirming or overturning those two rulings — not about a test that is failing.
1. validate-sortable-fields.ts — the one that looks real
It landed 2026-08-17 (#9314), after the #8996 sweep that wired the other write/read sites, so it was never in any sweep's population.
packages/lint/src/validate-sortable-fields.ts:310
if(SYSTEM_FIELDS.has(head))continue;
The rule's header records skip ② as "an object that declares no field map at all — external objects and datasource-introspected schemas whose columns resolve at runtime", which reads as though an ADR-0015 external object never reaches the union branch. It is not what the code keys on. The index it consults is the shared indexObjectSearchTargets (packages/lint/src/validate-searchable-fields.ts:312), whose declaredFieldTarget returns null on exactly one condition:
constfields=obj.fields;if(!fields||typeoffields!=='object')returnnull;…if(names.size===0)returnnull;
Nothing there tests external. So an external object that does declare a mapped field map (the shape #7859 is about — the author vouches for remote columns through the binding's columnMap) is indexed normally, reaches line 310, and a sort: [{ field: 'created_at' }] over it is skipped in silence.
That is the asymmetry: on that same object, the SEARCH axis warns — validate-searchable-fields was wired for exactly this by #8404, and it passes indexUnprovisionedAnchors into the shared checkSearchableFieldList core. The FILTER and page-binding axes warn too (#8340). SORT is the one axis that consults the union and says nothing, and the runtime consequence is the one #8116 describes: an ORDER BY over an anchor with no storage, HTTP 200 and a silently wrong ordering rather than an error.
⚠️ Not verified end-to-end: I did not construct a running external-object stack and observe the missing finding. What is verified is the code path — declaredFieldTarget does not exclude external objects, and the rule references no provenance helper. Worth a triage read before anyone writes the fix.
2. validate-translation-references.ts — recorded as deliberately not asking
packages/lint/src/validate-translation-references.ts:177 spreads the union into its own rule-local IMPLICIT_FIELDS, and has done since before #8340. It is a spread consumer that no sweep ever listed, which is its own small data point for #8999.
My ruling in the ledger is that it correctly asks nothing: a translation bundle supplies a label for a column and never reads the value, so "this anchor has no storage behind it" says nothing about whether the label resolves. The #8116 warning is about predicates and pointers over the value. Recorded here so a second pair of eyes can overturn it rather than inherit it.
Why this is filed rather than fixed
The #8999 card's deliverable is the census instrument; wiring a provenance warning into the SORT axis is a rule-shape change with its own message wording, its own tests, and a judgement about the dotted-path posture the rule deliberately shares with the FILTER and PROJECTION axes (#4256 / #7532 / #7589). That is not a rider on a test-only PR.
⚠️ One consequence for triage: #8999 was held on the argument that after #8996 the family had "no known remaining hole". That premise no longer holds literally — though neither row above is a proven defect yet, which is what this issue is for.
Related: #8999 (the census) · #8996 / #8663 (the sweep that wired the others) · #8404 (the SEARCH axis wiring this mirrors) · #8340 · #8116 (the provenance question) · #9314 (where the SORT axis landed) · #7859
Found while building the #8999 consumer census (PR on
claude/issue-8999-system-fields-spread-sweep). ⛔ Filed, not fixed — out of that card's scope, which is the census instrument.What the census turned up
The census enumerates every consumer of the blanket
SYSTEM_FIELDSunion by module graph and records, per consumer, whether it asks the #8116 provenance question (unprovisionedInjectedColumnsFor/indexUnprovisionedAnchors/unprovisionedAnchorCause). Two of the ten rule-file consumers reference none of them:validate-sortable-fields.tsdirectvalidate-translation-references.tsderived(spread) +directBoth are recorded in the census ledger with a reason rather than silently exempted, so this issue is about confirming or overturning those two rulings — not about a test that is failing.
1.
validate-sortable-fields.ts— the one that looks realIt landed 2026-08-17 (#9314), after the #8996 sweep that wired the other write/read sites, so it was never in any sweep's population.
packages/lint/src/validate-sortable-fields.ts:310The rule's header records skip ② as "an object that declares no field map at all — external objects and datasource-introspected schemas whose columns resolve at runtime", which reads as though an ADR-0015
externalobject never reaches the union branch. It is not what the code keys on. The index it consults is the sharedindexObjectSearchTargets(packages/lint/src/validate-searchable-fields.ts:312), whosedeclaredFieldTargetreturnsnullon exactly one condition:Nothing there tests
external. So an external object that does declare a mapped field map (the shape #7859 is about — the author vouches for remote columns through the binding'scolumnMap) is indexed normally, reaches line 310, and asort: [{ field: 'created_at' }]over it is skipped in silence.That is the asymmetry: on that same object, the SEARCH axis warns —
validate-searchable-fieldswas wired for exactly this by #8404, and it passesindexUnprovisionedAnchorsinto the sharedcheckSearchableFieldListcore. The FILTER and page-binding axes warn too (#8340). SORT is the one axis that consults the union and says nothing, and the runtime consequence is the one #8116 describes: anORDER BYover an anchor with no storage, HTTP 200 and a silently wrong ordering rather than an error.declaredFieldTargetdoes not exclude external objects, and the rule references no provenance helper. Worth a triage read before anyone writes the fix.2.
validate-translation-references.ts— recorded as deliberately not askingpackages/lint/src/validate-translation-references.ts:177spreads the union into its own rule-localIMPLICIT_FIELDS, and has done since before #8340. It is a spread consumer that no sweep ever listed, which is its own small data point for #8999.My ruling in the ledger is that it correctly asks nothing: a translation bundle supplies a label for a column and never reads the value, so "this anchor has no storage behind it" says nothing about whether the label resolves. The #8116 warning is about predicates and pointers over the value. Recorded here so a second pair of eyes can overturn it rather than inherit it.
Why this is filed rather than fixed
The #8999 card's deliverable is the census instrument; wiring a provenance warning into the SORT axis is a rule-shape change with its own message wording, its own tests, and a judgement about the dotted-path posture the rule deliberately shares with the FILTER and PROJECTION axes (#4256 / #7532 / #7589). That is not a rider on a test-only PR.
Related: #8999 (the census) · #8996 / #8663 (the sweep that wired the others) · #8404 (the SEARCH axis wiring this mirrors) · #8340 · #8116 (the provenance question) · #9314 (where the SORT axis landed) · #7859