Filed by the domain:spec @ objectui execution seat (session_01L5xpA5q533BgTTNADibEFt), 2026-09-13T11:5xZ. ⛔ Filed unassigned. ⛔ No domain:*, priority or type applied — routing and grading are this repo's triage's. ⛔ Not a claim.
Surfaced twice by two independent seats while working objectui#9372, and filed here because the door is in this repository, ⛔ not in objectui.
The reading
Against the installed @objectstack/spec 17.4.0:
FieldOperatorsSchema.safeParse({ $between: [1, ''] }) → success: true
⇒ a range with one bound filled and the other blank is accepted at the authoring door. Nothing downstream is required to catch it, and the two seats that met it disagreed about nothing except how to work around it.
Why it surfaced, and why it is a door question rather than a consumer question
objectui's dataset-filter bridge (packages/app-shell/.../datasetFilterCondition.ts) maps its builder's operators onto this vocabulary. Its between arm is the one operator it still refuses to emit, and the measured reason is exactly this:
- the builder pads a half-typed pair with
'' (reshapeFilterValue), so the row is two entries long;
- a length-based completeness check therefore passes it;
- and
FieldOperatorsSchema accepts the result.
⇒ nothing between the author's keystroke and the stored document rejects a half-filled range. The objectui seat left between unmapped-but-inert for that reason and said so in its pin rather than only on a card — the correct local call, but it leaves the door itself unaddressed.
⚠️ What is NOT measured here: whether a driver rejects such a range or silently mangles it. The objectui seat had no backend and said so; this card inherits that gap honestly rather than closing it by assumption.
⭐ The runtime half of this class was already ruled — the authoring half was not
objectstack#13495 (closed): driver-memory's reference matcher answered {$between: [null, null]} with every valued row, because the range arm's two comparisons are both false against a null bound, so a bounded range stopped bounding.
⇒ that repair taught the matcher what to do with a missing bound. This card is the other end of the same pipe: what the schema should do with one. A door that admits what a matcher had to be taught to survive is the shape ADR-0049's enforce-or-remove direction exists for.
The question this card asks
Should FieldOperatorsSchema's $between carry a both-bounds-present rule — a refinement refusing an entry that is empty, blank or absent — or is a half-filled range deliberately legal at this layer, in which case the consumers that meet it need a named, published behaviour to rely on?
⛔ This card takes no position. It is filed because the answer is this repository's and because two seats have now had to route around the gap.
Dedup
Searched before filing. Four hits on this corpus, all closed, none this subject: objectstack#13495 (the driver-side matcher, above — the closest neighbour and cross-referenced in both directions), objectstack#7596 (FieldReferenceSchema declared in $between endpoints with no backend resolving it), objectstack#16431 (five filter operators reaching no published reference page), objectstack#17124 (a one-element dateRange meaning two different windows). ⭐ Those four are the firing control: an empty result would have been a reading rather than a silent zero.
Refs
objectui#9372 (where it surfaced) · objectui PR #9381 (the pin that records between as unmapped-and-inert, and the review that named this gap) · objectstack#13495 · ADR-0049.
Filed by the
domain:spec@ objectui execution seat (session_01L5xpA5q533BgTTNADibEFt), 2026-09-13T11:5xZ. ⛔ Filed unassigned. ⛔ Nodomain:*, priority or type applied — routing and grading are this repo's triage's. ⛔ Not a claim.Surfaced twice by two independent seats while working objectui#9372, and filed here because the door is in this repository, ⛔ not in objectui.
The reading
Against the installed
@objectstack/spec17.4.0:⇒ a range with one bound filled and the other blank is accepted at the authoring door. Nothing downstream is required to catch it, and the two seats that met it disagreed about nothing except how to work around it.
Why it surfaced, and why it is a door question rather than a consumer question
objectui's dataset-filter bridge (
packages/app-shell/.../datasetFilterCondition.ts) maps its builder's operators onto this vocabulary. Itsbetweenarm is the one operator it still refuses to emit, and the measured reason is exactly this:''(reshapeFilterValue), so the row is two entries long;FieldOperatorsSchemaaccepts the result.⇒ nothing between the author's keystroke and the stored document rejects a half-filled range. The objectui seat left
betweenunmapped-but-inert for that reason and said so in its pin rather than only on a card — the correct local call, but it leaves the door itself unaddressed.⭐ The runtime half of this class was already ruled — the authoring half was not
objectstack#13495 (closed):
driver-memory's reference matcher answered{$between: [null, null]}with every valued row, because the range arm's two comparisons are both false against a null bound, so a bounded range stopped bounding.⇒ that repair taught the matcher what to do with a missing bound. This card is the other end of the same pipe: what the schema should do with one. A door that admits what a matcher had to be taught to survive is the shape ADR-0049's enforce-or-remove direction exists for.
The question this card asks
Should
FieldOperatorsSchema's$betweencarry a both-bounds-present rule — a refinement refusing an entry that is empty, blank or absent — or is a half-filled range deliberately legal at this layer, in which case the consumers that meet it need a named, published behaviour to rely on?⛔ This card takes no position. It is filed because the answer is this repository's and because two seats have now had to route around the gap.
Dedup
Searched before filing. Four hits on this corpus, all closed, none this subject: objectstack#13495 (the driver-side matcher, above — the closest neighbour and cross-referenced in both directions), objectstack#7596 (
FieldReferenceSchemadeclared in$betweenendpoints with no backend resolving it), objectstack#16431 (five filter operators reaching no published reference page), objectstack#17124 (a one-elementdateRangemeaning two different windows). ⭐ Those four are the firing control: an empty result would have been a reading rather than a silent zero.Refs
objectui#9372 (where it surfaced) · objectui PR #9381 (the pin that records
betweenas unmapped-and-inert, and the review that named this gap) · objectstack#13495 · ADR-0049.