fix(spec-builder): delete error in dialog, clear-filter exit (#7662) - #8728
Conversation
Design Review (Fable 5) — ✅ PASSDesign-level review of Both fixes check out cleanly against the shared components they claim to reuse: Design-Verdict: PASS Two real dead-end UX states fixed at the right layer, by reusing the two shared components documented for exactly these cases. [DESIGN-REVIEWED] 1323e76 |
UX Review (Fable 5) — ✅ PASSUX-level review of UX-Verdict: PASS Both fixes move feedback to where the user is actually looking — the dialog and the filtered rail — and the screenshots confirm clean rendering at all sizes. Suggestions
[UX-REVIEWED] 1323e76 |
GPT 5.6 Review — ✅ no blocking findingsGPT 5.6 completed its review of This comment is updated in place on each push. Review detailsNo findings. False positive or not applicable? A repository writer can comment: |
Opus 4.8 Review — ✅ no blocking findingsReviewed Review detailsThe change is clean: the removed i18n key has no call sites, No findings. [OPUS-REVIEWED] 1323e76 Verdict parsed from the review's SHA-scoped output markers for commit False positive or not applicable? A repository writer can comment: |
First Principles Review (Fable 5) — 🟡 CONCERNSPremise-level review of All facts are verified. The fix mechanisms are the documented ones ( First-Principles-Verdict: CONCERNS Both fixes land at mechanism level with derived causes, but the occluded-banner root cause survives one door over, in SettingsModal, unfixed. What this change shipsIntent: make a failed spec delete visible and give the filtered-to-nothing spec list a one-click exit (#7662). This is a FIX (two of them).
Watch
[FIRST-PRINCIPLES-REVIEWED] 1323e76 |
428ae24 to
476b6ce
Compare
476b6ce to
f5fcbfe
Compare
|
Disposition: fixed (in
Verified against the code:
On the Watch item ( |
Two UX gaps deferred out of PR #5111 by its UX Review lane: 1. A failed spec delete wrote its raw error to the page-top banner, which sits behind the confirm dialog's dimmed backdrop while focus is trapped inside the dialog — the user only saw the button revert from its busy state, inviting blind retries. The failure now renders inside the dialog through ErrorNotice: a translated what-happened + try-again lead plus the reason detail. No agent hand-off on this notice: the chat navigation unmounts the whole view, and SpecDetail can be holding stacked unsent review comments and an in-progress DocView draft — the hand-off would silently discard both. The mutation state resets when the dialog opens, so a dismissed failure does not greet the next attempt. 2. The rail's "no specs match that filter" empty state offered no recovery affordance. It now carries a one-click Clear-filter action (Issue Radar's HiddenByFilter convention) that empties the input and restores the full list. Both strings ship as catalog keys in all 13 locales; clear_filter reuses each locale's established components.ui.clear_filter wording. Tests: red-before-green proven — the four new/updated Vitest cases (in-dialog error + no banner call, fresh-dialog reset, empty-state action offered, click clears and restores) fail against the pre-fix components and pass after. Screenshots under temp-screenshots/spec-builder-7662/ are captured by the asserted-state harness (capture/spec-builder-7662.tsx + scripts/capture-spec-builder-7662.mjs), gateway-free. Closes #7662 Co-authored-by: Kiro Crew <noreply@kirodotdev.github.io>
f5fcbfe to
1323e76
Compare
|
Disposition: accepted-and-deferred → tracked in #8757
The finding holds — verified the same grep ( |
bolichen97
left a comment
There was a problem hiding this comment.
Tech Lead review: approved.
Scope matches #7662 exactly — both acceptance criteria met, nothing extra. The delete fix is a subtraction plus the documented shared surface: onError -> setErr dropped, ErrorNotice rendered inside the confirm dialog with askAgent correctly left at its opt-out default (the hand-off would unmount SpecDetail and discard stacked unsent review comments plus the in-progress DocView draft, and the call site comments say so). deleteMutation.reset() on dialog open closes the stale-error path. ErrorNotice renders nothing on a falsy message, so the first open is clean without a guard. The rail fix replaces a hand-rolled empty state with the shared FilteredEmpty, whose doc comment names this exact case; the trigger condition is unchanged, and the orphaned no_matching_specs key is removed from all 13 catalogs with the one new key added to all 13.
All gates green on 1323e76b (the six cancelled check-runs are superseded duplicates, each with a later success of the same name). GPT 5.6 and Opus 4.8: no findings. Design and UX: PASS. First Principles CONCERNS is advisory and correctly dispositioned — the sibling occluded-banner instance at SettingsModal.tsx:46/:61 is deferred to #8757 with a label, assignee, and due date rather than widening this diff.
Non-blocking, author's call: the UX lane's suggestion to drop "— try again." from couldn_t_remove_this_spec_try_again is a fair point — the bold lead can contradict a backend reason that says a retry will fail until something else is done first. Not worth another full re-review cycle on its own.
Problem / Motivation
Two UX gaps in the Spec Builder delete/filter flow, deferred out of PR #5111 by its UX Review lane:
Why it matters
A delete that fails silently reads as the app ignoring the click — the user retries into the same refusal (e.g. a mid-turn worker slot) with no way to learn why, and the actual reason is painted on a surface they physically cannot see. The filtered-to-nothing rail strands the user one unnecessary interaction away from their spec list. Both were flagged by the UX Review lane on #5111 and tracked in #7662.
What changed (motivation → approach → change)
onError → setErrraw pass-through ondeleteMutationand rendered the failure in the confirm dialog through the sharedErrorNotice(per theerrors-use-error-noticerule): a translated what-happened + try-again lead (couldn_t_remove_this_spec_try_again, new key in all 13 catalogs) with the reason detail beneath. No agent hand-off on this notice — the chat navigation unmounts the whole view, andSpecDetailcan be holding stacked unsent review comments and an in-progress DocView draft; the call site carries theNo hand-off:comment naming both.deleteMutation.reset()runs when the dialog opens, so a dismissed failure does not greet the next attempt as if it had already failed.FilteredEmptycomponent (components/ui.tsx, documented for exactly this case): it echoes the query back and offers the established Clear-filter action, which empties the input and restores the full list. No new i18n keys —FilteredEmptycarries its owncomponents.uiwording; the rail's now-orphanedno_matching_specskey is removed from all 13 catalogs (the dead-key ratchet enforces this).Tests
Red-before-green proven: all four fail against the pre-fix components and pass after.
SpecBuilderSpecDetailCoverage.test.tsx— surfaces a refused delete inside the dialog and leaves the spec open: a 409 delete renders the translated lead + reason in the dialog'srole="alert",setErr(the page-top banner) is not called,onDeletednot fired, dialog stays open.SpecBuilderSpecDetailCoverage.test.tsx— opens a fresh dialog without the previous attempt's failure: dismiss after a failure, reopen → no stale alert.SpecBuilderRailClearFilter.test.tsx— offers a Clear-filter action only when the filter matched nothing: unfiltered list shows no button; a no-match filter shows the empty state + button.SpecBuilderRailClearFilter.test.tsx— clears the filter and restores the full list on click: the input value is actually emptied and both groups return.Full local gates:
npx tsc -b, fullnpm run testwebsite suite (29,175 tests green),i18n-check, eslint, isort/flake8/mypy.Manual verification
Verified the rendered states myself via the asserted-state capture harness (scripted Playwright, gateway-free —
capture/spec-builder-7662.tsx+scripts/capture-spec-builder-7662.mjs): every frame below asserts its state before the shot (translated lead present, reason present, zerosetErrcalls, dialog fits 390px, input emptied on clear).Screenshots / video
More: light theme, 390px viewport, cleared state
Related Issues
Closes #7662
Pattern harvest
Rule candidate: review-prompt
Pattern: "mutation error routed to a surface occluded by the modal that triggered it — error must render inside the focus-trapped layer"
Checklist
feat|fix|docs|refactor|perf|test|chore|ci|build|revert: ...)