Filed from objectui#3950's implementation (objectui PR #4965). Recorded, not claimed.
The gap
packages/lint/src/validate-searchable-fields.ts rejects a searchableFields entry naming a formula field at authoring time, at error level. Its own docblock states the reason in the general form:
once the REST read path validates that override against the object (#4254), a stale declaration the engine had been silently skipping becomes a 400 INVALID_FIELD on every list search for that object — a request-time break whose cause is an authoring typo made long before.
The SORT axis now has exactly that shape and no such rule. After #6994 (ingress, assertSortFieldsExist) and #7095 (engine, assertOrderByIsMaterializable), an orderBy naming a formula field is a hard 400 INVALID_SORT. But nothing reads a view's sort declaration at authoring time:
Net effect: a list view authored with sort: 'expected_revenue desc' (a formula field) validates, publishes, and reports valid — then fails on first load, every load, with a 400 the author cannot connect to the declaration. That is strictly worse than the search-axis case the existing rule gates, because a refused sort is the view's initial fetch, not one optional interaction.
Why this is not objectui's to fix, and why it is not covered by objectui#3950
objectui#3950 / PR #4965 closes the affordance half in the renderer: the column header no longer offers a sort on a formula column. It deliberately does not filter a DECLARED sort out of the outgoing query — silently dropping an author's declaration would hide the authoring error, which is the fallback-in-the-consumer move AGENTS.md #0.1 forbids. So the loud runtime failure is the intended behaviour on that side, and it is intended precisely because the producer is supposed to reject it first. Right now nothing does.
The sort picker in objectui's list toolbar also keeps such a field listed when the current sort already names it — that row is the only way for a user to REMOVE the offending sort. So the runtime is already built assuming the declaration can exist and must be fixable; the missing piece is refusing it where it is written.
Suggested shape
A lint rule mirroring validate-searchable-fields, judging by the same spec predicate the other three doors read (isVirtualSearchField / SEARCH_VIRTUAL_TYPES from @objectstack/spec/data — the storage fact, pinned to formula alone), over every author-written sort position:
Deliberately NOT COMPUTED_VALUE_TYPES — summary and autonumber have real stored columns and sort correctly; the trap is pinned by name in the engine's own conformance suite.
Level is the open question worth deciding rather than guessing: error matches the search-axis precedent and the consequence here is larger (the view's first fetch), but it would newly fail lint for metadata that exists today and currently only fails at runtime.
Not verified by me
Whether any shipped example app or corpus view already declares such a sort. objectui#3950's own corpus sweep found formula fields RENDERED as columns (crm_opportunity.expected_revenue, showcase project.budget_remaining) but did not find one named in a sort declaration — so this may be a gate with no current violations, which is the cheap moment to add it.
Refs: #6994 (ingress refusal), #7095 (engine refusal), #6674 (the SEARCH axis' twin, with the lint rule), #8296 (the FILTER axis), objectui#3950 / objectui PR #4965 (the renderer-affordance half).
Filed from objectui#3950's implementation (objectui PR #4965). Recorded, not claimed.
The gap
packages/lint/src/validate-searchable-fields.tsrejects asearchableFieldsentry naming aformulafield at authoring time, aterrorlevel. Its own docblock states the reason in the general form:The SORT axis now has exactly that shape and no such rule. After #6994 (ingress,
assertSortFieldsExist) and #7095 (engine,assertOrderByIsMaterializable), anorderBynaming aformulafield is a hard400 INVALID_SORT. But nothing reads a view'ssortdeclaration at authoring time:git ls-treeoverpackages/lint/src/has no sort/order rule, and no non-test rule file readssort:/orderBy/defaultSortoff a view. The only hit forsort:in that tree is a fixture (showcase-shape.fixtures.ts:186).ListViewSchema.sortisstring | Array<{ field, order }>, so Zod cannot catch it either: the field name is a bare string, exactly assearchableFieldsentries were before A virtual formula field declared in searchableFields clears lint and the #4254 gate, then silently matches nothing — the same fail-open shape #4254 closed one axis over #6674.Net effect: a list view authored with
sort: 'expected_revenue desc'(aformulafield) validates, publishes, and reports valid — then fails on first load, every load, with a 400 the author cannot connect to the declaration. That is strictly worse than the search-axis case the existing rule gates, because a refused sort is the view's initial fetch, not one optional interaction.Why this is not objectui's to fix, and why it is not covered by objectui#3950
objectui#3950 / PR #4965 closes the affordance half in the renderer: the column header no longer offers a sort on a formula column. It deliberately does not filter a DECLARED sort out of the outgoing query — silently dropping an author's declaration would hide the authoring error, which is the fallback-in-the-consumer move
AGENTS.md#0.1 forbids. So the loud runtime failure is the intended behaviour on that side, and it is intended precisely because the producer is supposed to reject it first. Right now nothing does.The sort picker in objectui's list toolbar also keeps such a field listed when the current sort already names it — that row is the only way for a user to REMOVE the offending sort. So the runtime is already built assuming the declaration can exist and must be fixable; the missing piece is refusing it where it is written.
Suggested shape
A lint rule mirroring
validate-searchable-fields, judging by the same spec predicate the other three doors read (isVirtualSearchField/SEARCH_VIRTUAL_TYPESfrom@objectstack/spec/data— the storage fact, pinned toformulaalone), over every author-written sort position:sort(objectlistViews.*and standalone view metadata);query.orderBy(already noted inengine.find()still drops aformulaORDER BY silently — decide whether the engine refuses or keeps its internal-caller tolerance #7095 as author-reachable and forwarded verbatim intoengine.findbyplugin-reports);Deliberately NOT
COMPUTED_VALUE_TYPES—summaryandautonumberhave real stored columns and sort correctly; the trap is pinned by name in the engine's own conformance suite.Level is the open question worth deciding rather than guessing:
errormatches the search-axis precedent and the consequence here is larger (the view's first fetch), but it would newly fail lint for metadata that exists today and currently only fails at runtime.Not verified by me
Whether any shipped example app or corpus view already declares such a sort. objectui#3950's own corpus sweep found formula fields RENDERED as columns (
crm_opportunity.expected_revenue, showcaseproject.budget_remaining) but did not find one named in asortdeclaration — so this may be a gate with no current violations, which is the cheap moment to add it.Refs: #6994 (ingress refusal), #7095 (engine refusal), #6674 (the SEARCH axis' twin, with the lint rule), #8296 (the FILTER axis), objectui#3950 / objectui PR #4965 (the renderer-affordance half).