Summary
Removing a query tile with the Dashboard trash icon updates dashboard.tiles[] but does not update the saved query's compatibility spec.favorite flag.
This leaves one persisted workspace containing two contradictory membership states:
{
"queries": [
{
"id": "q1",
"spec": {
"favorite": true
}
}
],
"dashboard": {
"tiles": []
}
}
After reloading the Workbench, the query still appears starred even though it has no Dashboard tile. The user must unstar and then star it again to restore membership.
This issue owns membership semantics and the Dashboard trash workflow. General cross-tab refresh-before-write consistency remains tracked by #343.
Reproduction
- Save a panel-role query.
- Star the query in the Workbench so it is added to the Dashboard.
- Open the editable Dashboard.
- Press the trash icon on that tile.
- Reload the Workbench.
Actual result
- The tile is absent from
dashboard.tiles[].
- The saved query still contains
spec.favorite: true.
- The Workbench renders the star as enabled.
- Clicking the star once only changes
favorite to false; clicking it a second time recreates the tile.
Expected result
Removing the final Dashboard tile for a query also makes that query unstarred.
If another tile instance still references the same query, the query remains starred.
Current workflow
The Workbench star path performs an atomic dual-write:
- toggles
spec.favorite;
- adds or removes Dashboard tile membership;
- commits the complete
StoredWorkspaceV1 aggregate.
The Dashboard trash path currently dispatches only:
{ type: 'remove-tile', tileId }
The command removes the tile from dashboard.tiles[], but the committed candidate preserves latest.queries unchanged. Therefore spec.favorite remains true.
The existing DashboardAuthoringSession.toggleMembership() already demonstrates the intended compatibility behavior: after changing membership, it updates spec.favorite to match whether the query still has a tile. The live Dashboard UI bypasses that semantic operation and calls raw remove-tile.
Canonical membership rule
During the compatibility period:
Dashboard membership source of truth: dashboard.tiles[]
Compatibility mirror: query.spec.favorite
For a panel-role query:
const member = dashboard.tiles.some(tile => tile.queryId === query.id);
The Workbench star UI should reflect canonical tile membership rather than trusting a stale spec.favorite value.
spec.favorite should still be written to match canonical membership until the compatibility field is retired.
Required delete behavior
Deleting a Dashboard tile must atomically:
- remove the selected tile;
- remove that tile ID from every explicit
filters[].targets array;
- check whether another tile still references the same query;
- set the query's
spec.favorite compatibility flag to that post-delete membership;
- normalize the active layout and fallback;
- increment the Dashboard revision once;
- validate and commit the complete workspace.
Examples:
Delete the only tile for q1
→ q1.favorite = false
Delete one of multiple tile instances for q1
→ q1.favorite = true
Deleting a tile does not delete the saved query from the Library.
Legacy inconsistent state
Do not automatically recreate tiles for every persisted query with favorite: true and no tile.
That state can also result from an intentionally imported query collection, because imported favorite flags do not automatically create Dashboard tiles.
Instead:
- render the Workbench star from canonical
dashboard.tiles[] membership;
- treat
favorite: true without a tile as unstarred;
- one click should add the tile and repair the compatibility flag in the same commit.
This repairs existing affected workspaces without guessing whether a missing tile should be recreated automatically.
Read-before-write consistency
Every membership mutation must be expressed as a semantic transform over the latest workspace loaded at write time, not as a complete candidate built from an older UI snapshot.
The broader two-tab refresh-before-write behavior is tracked by #343. This issue does not require protection against two users committing during the same read/write instant; it only requires that ordinary sequential edits from the Workbench and Dashboard are based on the latest persisted workspace.
Acceptance criteria
Related
Summary
Removing a query tile with the Dashboard trash icon updates
dashboard.tiles[]but does not update the saved query's compatibilityspec.favoriteflag.This leaves one persisted workspace containing two contradictory membership states:
{ "queries": [ { "id": "q1", "spec": { "favorite": true } } ], "dashboard": { "tiles": [] } }After reloading the Workbench, the query still appears starred even though it has no Dashboard tile. The user must unstar and then star it again to restore membership.
This issue owns membership semantics and the Dashboard trash workflow. General cross-tab refresh-before-write consistency remains tracked by #343.
Reproduction
Actual result
dashboard.tiles[].spec.favorite: true.favoritetofalse; clicking it a second time recreates the tile.Expected result
Removing the final Dashboard tile for a query also makes that query unstarred.
If another tile instance still references the same query, the query remains starred.
Current workflow
The Workbench star path performs an atomic dual-write:
spec.favorite;StoredWorkspaceV1aggregate.The Dashboard trash path currently dispatches only:
The command removes the tile from
dashboard.tiles[], but the committed candidate preserveslatest.queriesunchanged. Thereforespec.favoriteremains true.The existing
DashboardAuthoringSession.toggleMembership()already demonstrates the intended compatibility behavior: after changing membership, it updatesspec.favoriteto match whether the query still has a tile. The live Dashboard UI bypasses that semantic operation and calls rawremove-tile.Canonical membership rule
During the compatibility period:
For a panel-role query:
The Workbench star UI should reflect canonical tile membership rather than trusting a stale
spec.favoritevalue.spec.favoriteshould still be written to match canonical membership until the compatibility field is retired.Required delete behavior
Deleting a Dashboard tile must atomically:
filters[].targetsarray;spec.favoritecompatibility flag to that post-delete membership;Examples:
Deleting a tile does not delete the saved query from the Library.
Legacy inconsistent state
Do not automatically recreate tiles for every persisted query with
favorite: trueand no tile.That state can also result from an intentionally imported query collection, because imported favorite flags do not automatically create Dashboard tiles.
Instead:
dashboard.tiles[]membership;favorite: truewithout a tile as unstarred;This repairs existing affected workspaces without guessing whether a missing tile should be recreated automatically.
Read-before-write consistency
Every membership mutation must be expressed as a semantic transform over the latest workspace loaded at write time, not as a complete candidate built from an older UI snapshot.
The broader two-tab refresh-before-write behavior is tracked by #343. This issue does not require protection against two users committing during the same read/write instant; it only requires that ordinary sequential edits from the Workbench and Dashboard are based on the latest persisted workspace.
Acceptance criteria
spec.favoritetofalsein the same workspace commit.spec.favoritetrue while another instance remains.favorite: truequery with no tile renders as unstarred in the Workbench.favorite: trueatomically.favorite: truequeries do not automatically gain Dashboard tiles on load.Related
view: "table"and auto-renders a chart #368 — Dashboard tile presentation bug; the original second report was split into this issuedashboard: null