fix(#429): resolve a saved query before navigating, trim blank tile titles (phase 1) - #490
Merged
Merged
Conversation
…itles Phase 1 of #429 — the two prerequisites later phases build row commands on. #443: `openSavedQuery` switched to the Query surface and pushed a history entry before it knew whether the id resolved, so a dead click threw the user off their surface, opened no tab and reported nothing. It resolves first now; a miss toasts and changes no surface, no route and no tab. All three callers (the tree's open-query command, its post-assignment reveal, and the per-tile Open in Workbench) address a query they just resolved or created, so none depended on the unconditional switch. #476: `dashboardTileV1.title` has no `minLength`, so `" "` is a legal document, and being truthy it beat the query-name fallback — a blank `.dash-tile-name` heading and accessible names reading "Open, — , in Workbench". The viewer trims the authored title before the fallback, in the one place a tile's display title is resolved, so every consumer of `state.title` is settled at once. Behaviour change for such documents: they now show the query name. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018mujm1kW7jDGpTEfscndcU
…ss wording
Review follow-ups on phase 1.
`flashToast` reuses ONE `.share-toast` element per document, so counting
elements can never distinguish one report from two — the "reports once" test
was asserting something it could not fail on. It now materialises the element
and counts how many times a report raises it, which a deliberate double-report
does fail.
The miss wording matches `openDashboard`'s exactly ("no longer part of this
workspace") rather than inventing a third phrasing next to it, and the #426
click-cancellation rationale no longer cites the dead-id hazard #443 just moved
to the callee.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018mujm1kW7jDGpTEfscndcU
This was referenced Jul 27, 2026
Open
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
Part of #429 — phase 1 of 6: the two prerequisites every later phase builds on. Both halves are corrections to primitives rather than features, so they land green and self-consistent on their own.
openSavedQueryresolves before it navigates (#443)app.openSavedQuery(queryId)calledshowQuerySurface()before it knew whether the id resolved. An unresolved id therefore threw the user off whatever surface they were on, pushed a history entry, opened no tab and reported nothing — a dead click that also lost their place.It resolves first now. A miss reports one diagnostic ("That query is no longer part of this workspace.", the wording
openDashboardalready uses for a missing Dashboard) and changes no surface, no route, no tab. A resolving id behaves exactly as before.The issue asks for a caller audit before tightening. There are three production callers, and none depended on the unconditional switch:
src/ui/dashboard-tree.ts:289— the row'sopen-querycommandsrc/ui/dashboard-tree.ts:350— the post-assignment revealsrc/ui/dashboard.ts:1855— per-tile Open in WorkbenchnullunlessqueryById.has(ts.queryId)Not reachable through the tree today (#426 cancels pending clicks on workspace switch and disposal), which is precisely why it lands here: it goes live the moment phases 2–5 add more row commands over the same primitive.
A whitespace-only tile title never reaches a heading or an accessible name (#476)
dashboardTileV1.titlecarries nominLength, so a hand-authored or imported" "is a schema-legal document — and being truthy it beat the query-name fallback unfiltered. The visible.dash-tile-nameheading rendered blank, and the names composed from it announced as "Open, — , in Workbench" and "Remove, — , from the dashboard".The viewer now trims the authored title before the fallback chain, in the one place a tile's display title is resolved — so every consumer of
state.titleis settled at once: the heading, the KPI cardaria-label, the Open in Workbench and Remove tile labels, the tile-search haystack, the parameter-analysis labels, and the variable-conflict diagnostic. A padded authored title is kept, trimmed.This is a deliberate behaviour change for existing documents carrying such a title: they start showing the query name. No shipped UI writes
tile.title, so only hand-authored and imported documents are affected. (dashboard-tree-model.tsalready trimmed, so the tree row was correct and the canvas disagreed with it.)Acceptance criteria claimed
openSavedQueryresolves before it navigates and reports a miss (openSavedQuery switches surfaces before resolving the id, and stays silent when it fails #443).Every other bullet is explicitly deferred: the pencil/trash row actions, the one-create-command reconcile (#481), the chevron/name/trailing gesture split (#472), ownership safety, concurrency, and the repair-planner decision (#449/#431) → phases 2–6. Per the issue's
#438note: this phase adds no focus-tile gesture, so no layout engine needs verifying for one.Verification
npm test— 5816 passed, 177 files, per-file gate held (statements/lines 100%).npx tsc --noEmitclean;npm run buildclean.npm run test:e2e— 281 passed on chromium + webkit (Firefox can't launch locally; it comes from CI).Follow-up filed
tile.descriptionhas the identical untrimmed-fallback shape one line below the title (dashboard-viewer-session.ts:563), so a whitespace-only description still renders an empty.dash-tile-descspan instead of falling back to the query description. Out of #476's scope (it corrupts no accessible name), filed as #489 (inbox) rather than folded in here.Checklist
npm testpasses (the per-file coverage gate is non-negotiable)npm run buildsucceeds (single-filedist/sql.html)src/core/, network insrc/net/(injected fetch), DOM insrc/ui/CHANGELOG.md([Unreleased]) updated if behavior or the deployed surface changed