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
Deliver the complete multi-Dashboard workspace and Dashboard/Library UX through the staged issues #424–#429.
This issue is the authoritative epic for sequencing, integration rules, completion status and final acceptance. Child issues contain the detailed implementation contracts for each phase.
Reconciled (#427, 2026-07-25): one scope deviation
#427's "make sourceQueryId required at an explicit persisted-contract boundary"
is NOT implemented, deliberately. A From/To time-range pair and a free-text search
box have no option list and therefore no lossless source query to derive, and core/time-range.ts only pairs filters that have NO source — so requiring one
would make an authored time range impossible to persist. Eleven such filters ship
across five example bundles (clickhouse-operations, ontime-charts, shop-charts, iceberg-dba-dashboard, iceberg-catalog-dashboard), which would
all have become un-importable.
The invariant implemented instead is curated => dedicated single-owner source:
a filter that HAS a sourceQueryId must own exactly one filter-role query, while a
plain filter remains valid and owns nothing. Every other #427 acceptance criterion
is unaffected. Whether plain filters should become a distinct persisted control
kind (and curated sources then required) is tracked separately.
This also revises the "Final integration cleanup" item "remove transitional
source-less filter authoring/runtime branches after migration": source-less
filters are a supported final kind, not a transitional state, so only the
FAVOURITE-driven implicit-source branch was removed (#427 replaced it with
name-based matching over Library filter-role queries).
Before #427, Dashboard-member queries still appear in the lower Queries list, so they remain reachable even without a tree.
#427 removes Dashboard-owned queries from the lower list and renames that projection to Library. Therefore the Dashboard tree must exist first, otherwise migration would hide queries before another navigation surface could open them.
full right-hand Dashboard surface while preserving the sidebar;
preserved Query editor/results state across surface switching;
View/Edit mode;
tile/filter focus requests;
selected-ID import/export;
fail-closed explicit import targeting;
selection reset across workspace changes;
generation-safe surface lifecycle.
#426 may extend the #425 navigation state with retained current-member identity and in-place same-Dashboard focus. That is an integration refinement required by the tree, not a reason to reopen the completed phase.
Depends on #426 and #427. It may be implemented in parallel with #428 after those dependencies land.
Depends additionally on #447 (phases 1–2, shipped), on #428 (PR #484, merged
2026-07-27) and on #438 for the focus-tile gestures on flow layouts only.
Split into six phases (2026-07-27), listed as a ## Phases checklist in #429:
1 — resolve-before-navigate + blank-title trim; 2 — Dashboard-row gesture split;
3 — create path + rename pencil; 4 — panel-row edit; 5 — panel-row delete;
6 — repair-planner decision. Each phase is a vertical slice (planner + UI + tests
in one PR) rather than planners-first, specifically to avoid repeating the
no-caller-planner problem #449 recorded. Phases 2–5 now build on top of#484's
Library drop-target wiring on the same rows, which they must preserve.
Phase 1 is implemented on fix/resolve-before-navigate-429p1 (9813cf3) and is not
yet pushed or PR'd.
Rescoped in the same pass:#447's hard removal means curated filters no longer
exist, so #429's curated-filter row/pencil/trash sections and its updateOwnedFilter / removeDashboardFilter commands were deleted rather than
adapted. Panel tiles are now the only members owning a dedicated query; variable
management stays with #447 phase 3 and #465.
Scope — this phase is the single owner of Dashboard-tree management and clicks:
Every user mutation builds and validates one complete V3 candidate and commits atomically.
Only the targeted Dashboard revision increments for Dashboard-content mutations.
Query-metadata-only changes do not increment Dashboard revision unless an existing documented aggregate rule requires it.
All non-target Dashboards remain canonical-value unchanged.
Stale dialogs, drags, focus requests and async callbacks cannot act on a replacement workspace/surface.
Final acceptance
The epic is complete when:
every stored Dashboard is reachable and navigable;
the lower pane is Library/History and contains only standalone queries;
each panel/filter has one dedicated query;
source-less filters have been migrated or rejected precisely at the migration boundary;
favourites have no membership side effects;
Library-to-Dashboard assignment always copies;
Dashboard/member edit and delete operations are complete and atomic;
migration preserves user data, order, IDs, layout and revisions;
import/export round trips all Dashboards and owned queries;
no hidden/unreachable Dashboard can permanently block a repairable operation;
all child-issue tests and integrated journeys pass;
database tree, Query editor, results, History and existing Dashboard execution have no unintended regressions.
Immediate next action
Implement #426 against current main after #432 and #436. Do not start #427 until #426 has landed and every Dashboard-owned/shared query is reachable through the tree.
Mission
Deliver the complete multi-Dashboard workspace and Dashboard/Library UX through the staged issues #424–#429.
This issue is the authoritative epic for sequencing, integration rules, completion status and final acceptance. Child issues contain the detailed implementation contracts for each phase.
Current status
Add to dashboard…) is unmet, and it is deferred to Keyboard-accessible "Add to dashboard…" command for Library queries #483File-menu reconciliation (#452 → #463)
exists, Import dashboard… is additive on every surface, and Export
dashboard… resolves an exact Dashboard from the workspace rather than
requiring one on screen — shipped in PR feat(#463): File menu creates, imports and exports Dashboards workspace-wide #482. It removed
planImportDashboard's compatibility-slot / explicit-target / create-firsttargeting outright, so no import path can write
dashboards[0]any more.Reconciling the empty-Dashboard placeholder's own Create dashboard button
with that command was triaged out of Reconcile the two Dashboard-creation paths behind one action #481 and absorbed into Dashboard tree management: direct Dashboard and Panel actions #429, which
already owns Dashboard-row/document-level metadata (its rename pencil is the
create action's sibling). Reconcile the two Dashboard-creation paths behind one action #481 is closed. Triage findings: the placeholder
renders only for an empty collection, so the divergence is visible as the
FIRST Dashboard in a fresh workspace being titled
Dashboardwith no prompt;and
reconcileMainSurface(missing/duplicate id → Query) is what keeps theplaceholder's non-empty-collection refusal unreachable. Dashboard tree management: direct Dashboard and Panel actions #429 narrows its
"creation" non-goal to cover exactly this reconcile — no new entry points.
Related follow-up defects/debt:
mainby Refactor File menu for multi-Dashboard creation, import, and export #463 (PR feat(#463): File menu creates, imports and exports Dashboards workspace-wide #482) and closed:planImportDashboardno longer remints-and-replaces under a new id, it appends and reportsimportedDashboardId, and the File menu reveals it.Add to dashboard…) belongs to Add Library drag assignment to Dashboard panels and variables #428's lineage — a Library-row affordance over Add Library drag assignment to Dashboard panels and variables #428's assignment commands — not to Dashboard tree management: direct Dashboard and Panel actions #429's Dashboard-tree scope.Authoritative implementation order
Reconciled (#427, 2026-07-25): one scope deviation
#427's "make
sourceQueryIdrequired at an explicit persisted-contract boundary"is NOT implemented, deliberately. A From/To time-range pair and a free-text search
box have no option list and therefore no lossless source query to derive, and
core/time-range.tsonly pairs filters that have NO source — so requiring onewould make an authored time range impossible to persist. Eleven such filters ship
across five example bundles (
clickhouse-operations,ontime-charts,shop-charts,iceberg-dba-dashboard,iceberg-catalog-dashboard), which wouldall have become un-importable.
The invariant implemented instead is curated => dedicated single-owner source:
a filter that HAS a
sourceQueryIdmust own exactly one filter-role query, while aplain filter remains valid and owns nothing. Every other #427 acceptance criterion
is unaffected. Whether plain filters should become a distinct persisted control
kind (and curated sources then required) is tracked separately.
This also revises the "Final integration cleanup" item "remove transitional
source-less filter authoring/runtime branches after migration": source-less
filters are a supported final kind, not a transitional state, so only the
FAVOURITE-driven implicit-source branch was removed (#427 replaced it with
name-based matching over Library filter-role queries).
Why #426 precedes #427
Before #427, Dashboard-member queries still appear in the lower Queries list, so they remain reachable even without a tree.
#427 removes Dashboard-owned queries from the lower list and renames that projection to Library. Therefore the Dashboard tree must exist first, otherwise migration would hide queries before another navigation surface could open them.
#426 must support both:
sourceQueryId;Phase contracts
Phase 1 — #424: Storage collection — complete
Implemented by #432.
Delivered:
StoredWorkspaceV3withdashboards[];No further #424 work is required for the epic sequence.
Phase 2 — #425: Selected Dashboard surface — complete
Implemented by #436.
Delivered:
#426 may extend the #425 navigation state with retained current-member identity and in-place same-Dashboard focus. That is an integration refinement required by the tree, not a reason to reopen the completed phase.
Phase 3 — #426: Read-only Dashboard tree — next
Implement now.
Scope:
Databases | Dashboardsswitcher;Dashboard -> Filters -> Panels;The lower pane remains
Queries | Historyin this phase.Phase 4 — #427: Ownership migration and Library
Begins only after #426 is merged.
Scope:
QueriestoLibraryatomically with the zero-owner projection;Phase 5 — #428: Assignment
Depends on #426 and #427.
Scope:
Add to dashboard…equivalent.Phase 6 — #429: Edit/delete
Depends on #426 and #427. It may be implemented in parallel with #428 after those dependencies land.
Depends additionally on #447 (phases 1–2, shipped), on #428 (PR #484, merged
2026-07-27) and on #438 for the focus-tile gestures on flow layouts only.
Split into six phases (2026-07-27), listed as a
## Phaseschecklist in #429:1 — resolve-before-navigate + blank-title trim; 2 — Dashboard-row gesture split;
3 — create path + rename pencil; 4 — panel-row edit; 5 — panel-row delete;
6 — repair-planner decision. Each phase is a vertical slice (planner + UI + tests
in one PR) rather than planners-first, specifically to avoid repeating the
no-caller-planner problem #449 recorded. Phases 2–5 now build on top of #484's
Library drop-target wiring on the same rows, which they must preserve.
Phase 1 is implemented on
fix/resolve-before-navigate-429p1(9813cf3) and is notyet pushed or PR'd.
Rescoped in the same pass: #447's hard removal means curated filters no longer
exist, so #429's curated-filter row/pencil/trash sections and its
updateOwnedFilter/removeDashboardFiltercommands were deleted rather thanadapted. Panel tiles are now the only members owning a dedicated query; variable
management stays with #447 phase 3 and #465.
Scope — this phase is the single owner of Dashboard-tree management and clicks:
createDashboardpath shared by the File menu and the empty-Dashboard placeholder (absorbs Reconcile the two Dashboard-creation paths behind one action #481);openSavedQueryresolving before it navigates (absorbs openSavedQuery switches surfaces before resolving the id, and stays silent when it fails #443);Final integration cleanup
After #428 and #429:
Final product model
Final invariants:
Final sidebar
The header contains non-interactive branding:
Dashboard View/Edit and Back-to-query controls remain in the Dashboard surface toolbar.
Final interaction grammar
Dashboard row
Panel row
Filter row
Mouse actions must have keyboard-accessible equivalents.
Cross-phase integration rules
Final acceptance
The epic is complete when:
Immediate next action
Implement #426 against current
mainafter #432 and #436. Do not start #427 until #426 has landed and every Dashboard-owned/shared query is reachable through the tree.