lisa/feat/bch-1312-suggestions-ui#264
Conversation
gusfcarvalho
left a comment
There was a problem hiding this comment.
Findings inline (1 Medium, 4 Low; CI green, no blockers). Headline: the run poller stops permanently on any transient poll error (useDashboardSuggestions.ts:265), silently freezing generation progress — that's the one worth fixing; the rest are nil-guards, two dead exports, and a vacuous negative test. Nice work on the fail-closed config gate and the prior self-review passes. Out of scope: the duplicate inventory:index route in router/index.ts already exists on main.
gusfcarvalho
left a comment
There was a problem hiding this comment.
All review feedback from the prior pass is addressed in d79b459 and verified on the incremental diff:
- Poller transient-error halt (Medium) — the
catchnow only stops on the newstopOnPollErroropt-in (default off), so a flaky poll no longer freezes generation progress. Nice touch adding a regression test (keeps polling after a transient poll error) that genuinely fails under the old behavior. - SSP
/fullnil-guards (Low) —?.metadata?.titleand?.controlImplementation?.implementedRequirementsboth hardened. - Dead exports (Low x2) —
getSuggestionIdandbuildDashboardSuggestionRunEndpointremoved (correct call to drop rather than switch accept/reject to uuid). - Vacuous negative test (Low) — positive enabled-path test added, asserting refreshLabelSets/refreshHistorySuggestions/pollLatest are called; both sides of the gate now covered.
CI green (test + type-check) on the new HEAD, all threads resolved, no regressions in the diff. LGTM.
Optional non-blocking nit (no need to action): stopOnPollError has no caller passing true yet — fine as a deliberate opt-in hook, just flagging it's currently unexercised in that direction.
|
PR approved. Marking as ready for e2e. |
gusfcarvalho
left a comment
There was a problem hiding this comment.
Reopening with product feedback from the maintainer that supersedes my earlier approval (code itself is sound; this is about placement and UX).
Findings inline. Headline: (1) the AI dashboard-suggestions entry point belongs in the per-control Control Implementation editor next to the component-suggestions feature / 'Evidence Linking' (ControlStatementImplementation.vue ~1407/1451), where the SSP is already in context — not as a separate button + SSP-picker on the global /dashboards page; and (2) when AI isn't configured the entry point is hidden silently — it should show an explicit 'AI is not configured, can't generate suggestions' message instead. The existing disabled-state copy in SuggestionsView is unreachable in practice and conflates 'off' with 'not configured'.
gusfcarvalho
left a comment
There was a problem hiding this comment.
Round 2 of maintainer UX feedback. Findings inline. Headline: (1) the evidence label-set selector should show a human Label Set Title (like the 'Create New Dashboard' evidence picker), not the raw hash; (2) the controls selector should show each control's title (not just the ID) and which profile(s) it belongs to. Note for (2): control titles need a catalog resolve (pattern exists in CreateFormView), and profile membership isn't in the SSP /full payload — it needs the bound profiles resolved — so this is a scoping call, flagged inline.
gusfcarvalho
left a comment
There was a problem hiding this comment.
Findings inline. Headline: the scope dialog's AI-call estimate is a raw controls×label-sets product and over-reports massively (e.g. 1424 vs ~1) because it ignores server chunking — switch it to the new POST .../dashboard-suggestions/preview endpoint from API PR 420, which returns the real chunked plannedCalls (plus resolved control/label-set counts). Contract + suggested wiring in the thread.
gusfcarvalho
left a comment
There was a problem hiding this comment.
Incremental review of 3a06fda..6986e73. All prior threads + the maintainer's feature feedback are addressed and verified in code: entry point relocated to the Control Implementation editor (global picker removed), explicit 'AI not configured' message, human label-set titles, control options now show title + profile membership (resolved-with-catalogs field names confirmed against RiskDetailView), and the cost estimate is wired to the new POST /dashboard-suggestions/preview endpoint — debounced with a stale-request guard, server plannedCalls drives the copy and the large-run gate, 422 empty-scope handled. Tests are solid (incl. the 16x89 -> 1 regression test) and CI is green. Approving. Two Low, non-blocking nits inline (redundant catalog fan-out; missing onUnmounted timer cleanup) — take or leave.
gusfcarvalho
left a comment
There was a problem hiding this comment.
One more UX refinement (maintainer request). Findings inline. Headline: bring the Controls scope selector up to the same standard as the Evidence-Linking selector — a two-line option where the main value is ID - Title and the sub-line is the Catalog, keeping profile membership as a chip. Needs controlOptions to emit structured options and the catalog title to be captured per control (currently discarded in the catalog walk).
gusfcarvalho
left a comment
There was a problem hiding this comment.
Two issues from live testing (reproduced via Playwright against the running stack). Headline: (1) functional bug — control title/catalog/profile resolution is case-sensitive, so the 12/29 SSP controls whose control-ids are stored lowercase render as bare ids (gd.sec.c08); all 12 resolve under case-insensitive matching. (2) the label-set dropdown rows still don't show the title as the main line (only the selected chips do) — bring them up to the Evidence-Linking standard. Details + the fix inline.
Signed-off-by: Gustavo Carvalho <gustavo.carvalho@container-solutions.com>
Signed-off-by: Gustavo Carvalho <gustavo.carvalho@container-solutions.com>
gusfcarvalho
left a comment
There was a problem hiding this comment.
Findings inline (A+B). The "matched evidence" count always renders 0 because pendingGroups reads suggestion.evidenceCount, a field the API never returns — the real count lives on the already-fetched labelSets array and must be joined by labelSetHash. Same root cause leaves the group label chips empty (suggestion.labels vs API labelSet).
Requested changes:
- A — Join
pendingGroupstolabelSetsby hash forevidenceCount, and source label chips from the matched label-set (SuggestionsView.vuependingGroups). - B — Make the "N matched evidence" span a router-link to the evidence index pre-filtered by the group's labels (
group.labels.join(' and ')), so users can see the exact matching evidence streams. - Companion — add
camelcaseStopPaths: ['data.labelSet']to the pending/history suggestion fetches to preserve_-prefixed label keys for thelabelSetfallback.
No API change required for A+B; an optional follow-up could enrich the suggestion response with evidenceCount server-side so the UI need not depend on a second endpoint.
gusfcarvalho
left a comment
There was a problem hiding this comment.
A+B implemented correctly in f56e6cd and all threads resolved — approving.
- A —
pendingGroupsnow joins the fetchedlabelSetsbylabelSetHashvia alabelSetByHashcomputed;evidenceCountcomes frommatched?.evidenceCountand label chips frommatched?.labels(withsuggestion.labelSetstale-fallback). The phantomsuggestion.evidenceCount/Math.maxpath is gone. - B — the count is now a
RouterLinktoevidence:indexfiltered bygroup.labels.join(' and '), withsampleTitlessurfaced as a tooltip. - Companion —
camelcaseStopPaths: ['data.labelSet']added to both the pending and history fetches, preserving_-prefixed keys for the fallback.
Verified locally: SuggestionsView.spec.ts passes (5/5, including the 7 matched evidence count and the _agent=scanner and env=prod filter link), and vue-tsc is clean on the touched files.
|
PR approved. Marking as ready for e2e. |
automated implementation by lisa.