You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Rewritten 2026-07-27 after a code review against main. The original body was
written during #447 phase 2 and is now wrong in both halves — item 2 is
implemented (only its regression test is missing), and item 1's citation names a
file that no longer exists, though the defect it describes is real and open.
1. The two-String-column rule is enforced on ONE path, and the batch still cannot check it — OPEN
Corrected citation. The original said the only caller is "the variable editor's Test action (ui/variable-editor.ts)". That file was deleted by #457 along with
the option-SQL drawer and its Test action. The check was then re-hosted by #465
— shipped in PR #493 (fix(#465): validate a dashboard-variable tab's Run as a variable option query, merged 2026-07-27, a0417ed; #465 closed as completed) —
on the dashboard-variable tab's ordinary Run:
src/ui/workbench/workbench-session.ts:441-448 — optionSqlDiagnostics(sql) runs
first and sends nothing when the text is already knowably wrong;
:475 — the SQL executes through compileOptionProbe(sql), not raw, so Run cannot
pass for SQL the batch would reject (compileOptionProbe shares nestBounded with compileVariableOptionBatch);
:487-489 — on a clean transport, validateOptionColumns(result.columns) and its
message becomes the result's error.
The asymmetry is unchanged.readVariableOptionBatch
(src/core/variable-options.ts:413) checks only that the merged response has the
three columns the compiler projects, because UNION ALL reports ONE merged column
list for every branch — names from the first branch, types promoted to a supertype —
so per-branch column types are not visible in the response at all. The code now
documents this deliberately at variable-options.ts:351-361 and :310-322.
Consequence, still live: SELECT id, name FROM countries with id UInt64 runs fine
in the refresh batch and its options are offered (the transport stringifies every
cell), while Run correctly reports "Both option columns must be String". Run is
stricter than the runtime, and a user can "fix" SQL that was working.
What is missing
Nothing warns the user before they hit the difference. There is no proactive hint
about the contract anywhere in the variable tab — the strings "two columns" /
"both … String" exist only inside the two diagnostics, i.e. after a failure.
Options, unchanged from the original filing:
Accept the asymmetry and state it in the variable tab's hint — cheapest, and
consistent with the spec designating the single-variable path as the diagnostic
path. The code already documents the asymmetry; this only surfaces it.
Project a per-branch type witness into the batch (an earlier draft used toTypeName(tuple(*))) — it works, but costs a fourth column on every row of
every refresh, and was cut as engineering past the spec.
Recommended: option 1.
2. Duplicate collapsing vs. the server-side LIMIT — IMPLEMENTED, test missing
The original concern was that duplicates collapse client-side after each branch's
server LIMIT 1001, so SELECT country, country FROM visits could return an
arbitrary short list presented as complete, with no truncation warning because
the collapsed count lands under the cap.
That is fixed on main, by the detection route this issue proposed — but it
shipped under an unrelated issue number, which is why it looks untouched.
fix(#468): close three selection-destroying paths found in review — switched the flag to the raw count. This is item 2's actual fix.
So item 2 was resolved as collateral in a review of an unrelated issue — one of #468's "three selection-destroying paths." #468's body never mentions #461, PR #469's
title does not either, and this issue was never updated. The only trace linking
the fix back here is the (#461) reference inside the code comment at src/core/variable-options.ts:436.
That also explains the missing test below: the fix arrived under #468's
selection-preservation lens (don't prune a committed selection against a partial
list) rather than under this issue's completeness lens, so it got a test for #468's
concern and none for #461's own collapse case. readVariableOptionBatch (src/core/variable-options.ts:432-441) counts RAW rows
before the blank/duplicate filters and flags from that count:
Its comment cites this issue by number and states the reasoning exactly: "a query
returning 1,001 rows of 500 distinct values is still an incomplete list".
It also reaches the user, which the original did not ask for:
dashboard-viewer-session.ts:1061-1066 raises a variable-options-truncated
diagnostic naming the affected variables — "Narrow the option SQL to see the rest."
:1071 passes the flag into applyOptions, so a committed selection is not pruned
against a list known to be incomplete.
The one thing left: make it falsifiable
No test covers the collapsing case, and this issue is cited in no test. The only
truncation test — tests/unit/variable-options.test.ts:427"caps each variable
independently and reports which were truncated" — pushes VARIABLE_OPTION_CAP + 5
rows of distinct values (v${i}), so raw == kept and it cannot distinguish
raw-count logic from kept-count logic.
Concretely: swapping line 441 for a kept-count test such as if (options.length >= VARIABLE_OPTION_CAP) still passes the whole existing suite,
while silently restoring the exact bug this issue reported.
Add one regression test: a branch returning VARIABLE_OPTION_CAP + 1 raw rows
that collapse to a handful of distinct values must still land in truncated.
Optionally assert the variable-options-truncated diagnostic in dashboard-viewer-session.test.ts for the same shape. Cite #461 in the test
name, so the next reader does not have to reconstruct the provenance table above.
Scope
Two small, independent changes:
a hint in the variable tab stating the two-String-column contract (item 1,
option 1);
the collapse-case regression test, and optionally its viewer-session counterpart
(item 2).
Rewritten 2026-07-27 after a code review against
main. The original body waswritten during #447 phase 2 and is now wrong in both halves — item 2 is
implemented (only its regression test is missing), and item 1's citation names a
file that no longer exists, though the defect it describes is real and open.
1. The two-
String-column rule is enforced on ONE path, and the batch still cannot check it — OPENCorrected citation. The original said the only caller is "the variable editor's
Test action (
ui/variable-editor.ts)". That file was deleted by #457 along withthe option-SQL drawer and its Test action. The check was then re-hosted by #465
— shipped in PR #493 (
fix(#465): validate a dashboard-variable tab's Run as a variable option query, merged 2026-07-27,a0417ed; #465 closed as completed) —on the
dashboard-variabletab's ordinary Run:src/ui/workbench/workbench-session.ts:441-448—optionSqlDiagnostics(sql)runsfirst and sends nothing when the text is already knowably wrong;
:475— the SQL executes throughcompileOptionProbe(sql), not raw, so Run cannotpass for SQL the batch would reject (
compileOptionProbesharesnestBoundedwithcompileVariableOptionBatch);:487-489— on a clean transport,validateOptionColumns(result.columns)and itsmessage becomes the result's error.
The asymmetry is unchanged.
readVariableOptionBatch(
src/core/variable-options.ts:413) checks only that the merged response has thethree columns the compiler projects, because
UNION ALLreports ONE merged columnlist for every branch — names from the first branch, types promoted to a supertype —
so per-branch column types are not visible in the response at all. The code now
documents this deliberately at
variable-options.ts:351-361and:310-322.Consequence, still live:
SELECT id, name FROM countrieswithid UInt64runs finein the refresh batch and its options are offered (the transport stringifies every
cell), while Run correctly reports "Both option columns must be String". Run is
stricter than the runtime, and a user can "fix" SQL that was working.
What is missing
Nothing warns the user before they hit the difference. There is no proactive hint
about the contract anywhere in the variable tab — the strings "two columns" /
"both … String" exist only inside the two diagnostics, i.e. after a failure.
Options, unchanged from the original filing:
consistent with the spec designating the single-variable path as the diagnostic
path. The code already documents the asymmetry; this only surfaces it.
toTypeName(tuple(*))) — it works, but costs a fourth column on every row ofevery refresh, and was cut as engineering past the spec.
Recommended: option 1.
2. Duplicate collapsing vs. the server-side
LIMIT— IMPLEMENTED, test missingThe original concern was that duplicates collapse client-side after each branch's
server
LIMIT 1001, soSELECT country, country FROM visitscould return anarbitrary short list presented as complete, with no truncation warning because
the collapsed count lands under the cap.
That is fixed on
main, by the detection route this issue proposed — but itshipped under an unrelated issue number, which is why it looks untouched.
Provenance (why nobody could find this)
16cabe7249f7a0feat(#468): restore the searchable multi-select for Array(scalar T) Dashboard variables, merged 2026-07-26)fix(#468): close three selection-destroying paths found in review— switched the flag to the raw count. This is item 2's actual fix.So item 2 was resolved as collateral in a review of an unrelated issue — one of
#468's "three selection-destroying paths." #468's body never mentions #461, PR #469's
title does not either, and this issue was never updated. The only trace linking
the fix back here is the
(#461)reference inside the code comment atsrc/core/variable-options.ts:436.That also explains the missing test below: the fix arrived under #468's
selection-preservation lens (don't prune a committed selection against a partial
list) rather than under this issue's completeness lens, so it got a test for #468's
concern and none for #461's own collapse case.
readVariableOptionBatch(src/core/variable-options.ts:432-441) counts RAW rowsbefore the blank/duplicate filters and flags from that count:
Its comment cites this issue by number and states the reasoning exactly: "a query
returning 1,001 rows of 500 distinct values is still an incomplete list".
It also reaches the user, which the original did not ask for:
dashboard-viewer-session.ts:1061-1066raises avariable-options-truncateddiagnostic naming the affected variables — "Narrow the option SQL to see the rest."
:1071passes the flag intoapplyOptions, so a committed selection is not prunedagainst a list known to be incomplete.
The one thing left: make it falsifiable
No test covers the collapsing case, and this issue is cited in no test. The only
truncation test —
tests/unit/variable-options.test.ts:427"caps each variableindependently and reports which were truncated" — pushes
VARIABLE_OPTION_CAP + 5rows of distinct values (
v${i}), soraw == keptand it cannot distinguishraw-count logic from kept-count logic.
Concretely: swapping line 441 for a kept-count test such as
if (options.length >= VARIABLE_OPTION_CAP)still passes the whole existing suite,while silently restoring the exact bug this issue reported.
Add one regression test: a branch returning
VARIABLE_OPTION_CAP + 1raw rowsthat collapse to a handful of distinct values must still land in
truncated.Optionally assert the
variable-options-truncateddiagnostic indashboard-viewer-session.test.tsfor the same shape. Cite #461 in the testname, so the next reader does not have to reconstruct the provenance table above.
Scope
Two small, independent changes:
String-column contract (item 1,option 1);
(item 2).
Neither blocks #447 phase 3.