Replace text-foreground (white in dark mode) with text-slate-900 (dark)
on the Lighthouse AI button. The gradient background (green→cyan) is
always light, so dark text ensures readable contrast in both themes.
The search input is now always visible in all tables. Removed:
- Collapsed state (icon-only button with hover to expand)
- isExpanded state and shouldStayExpanded logic
- handleMouseEnter/handleMouseLeave/handleIconClick handlers
- Transition animations for expand/collapse
The input starts at w-64 (or w-[28rem] with badge) and stays visible.
Focus/blur still tracked for border styling only.
Change DataTableSearch uncontrolled mode from debounce-on-keystroke to
commit-on-Enter for ALL tables (findings, providers, scans, etc.).
- handleChange now only updates the display value (internalValue)
without triggering URL updates or showing a loading spinner
- onKeyDown Enter handler commits the search: updates URL params
(prefixed or filter[search]) and resets to page 1
- Removes all debounce logic from the uncontrolled path — typing is
instant feedback, searching is explicit user intent via Enter
- Controlled mode with onSearchCommit unchanged (already Enter-only)
Replace useEffect-based refresh on resourceSearch change with
imperative queueMicrotask(() => inlineRef.current?.refresh()) in the
onSearchCommit callback. This follows the project convention of
avoiding useState+useEffect patterns that mirror props.
The hook uses refs (filtersRef.current) which update on render but
don't auto-trigger fetches. queueMicrotask ensures the state update
from setResourceSearch is flushed before refresh reads the new filters.
When onSearchCommit is provided, the loading spinner should only appear
after Enter is pressed (actual search commit), not on every keystroke.
- Skip setIsLoading(true) in handleChange when onSearchCommit is present
- Cancel pending debounce timer when Enter fires (prevents stale
onSearchChange from firing 500ms after commit)
- Sync display state via onSearchChange on Enter to keep input and
committed state consistent
- Fix pre-existing React.ReactNode namespace usage in data-table.tsx
6 tests passing (1 new test for debounce cancellation on Enter).
Address 6 UX items from Pepe's product review:
- Lighthouse AI: change 'View This Finding' to 'Analyze This Finding
With Lighthouse AI'
- Remediation headings: shorten 'Terraform Command' to 'Terraform' and
'CloudFormation Command' to 'CloudFormation' for cleaner labels
- Search on Enter: split resourceSearch into input display state and
committed search state, fire actual search only on Enter key.
Remove resourceSearch from InlineResourceContainer key to prevent
full remount on every keystroke
- Muted icon: add 'Muted' text indicator with VolumeX icon in resource
row status cell for muted findings
- Risk section: wrap in danger-styled container with border-danger and
bg-bg-fail-secondary background for visual emphasis
- Section headings: upgrade Risk/Description labels from text-xs to
text-sm font-semibold. Add border separator after Description section
Also fix pre-existing React.ReactNode namespace usage in data-table.tsx
(replaced with named import type).
18 tests across 3 test suites (2 new, 1 extended). All passing.
Judgment Day: 1 round, 0 CRITICAL, warnings documented as follow-ups.
Known follow-ups:
- Spinner fires on keystroke in commit-on-Enter mode (cosmetic)
- Debounce timer not cancelled on Enter (edge case, no crash)
- Remediation card labels still text-xs (inconsistent with overview)
Address 3 product blockers from Pepe's review:
- Add sort=-status to getFindingGroupResources and
getLatestFindingGroupResources so FAIL resources appear before PASS
in the drill-down list (sort kept as future-proofing even when
filter[status]=FAIL makes it a no-op for now)
- Add filter[status]=FAIL to both resource endpoints so drill-down
only shows impacted resources, matching the count in the group header
- Add filter[status]=FAIL to createResourceFindingResolutionUrl so
muting a group only affects active FAIL findings, not all historical
- Use .set() after appendSanitizedProviderFilters to guarantee
hardcoded FAIL wins even if caller passes filter[status] in filters
(prevents duplicate filter[status] params with undefined API behavior)
- Apply symmetric .set() pattern in findings-by-resource.ts for
consistency
26 tests (10 new). All passing.
Judgment Day: 2 rounds, 0 discrepancies remaining.
- Replace `any` with `unknown` + type guards in finding-groups and
findings-by-resource adapters (4 adapter functions hardened)
- Add unmount cleanup for AbortController in use-resource-detail-drawer
to prevent memory leaks on mid-flight unmount
- Add try/catch/finally error handling for onBeforeOpen in floating mute
button to prevent permanent spinner state on network errors
- Replace hardcoded colors (border-gray-300, bg-white, text-slate-950)
with semantic tokens (border-default-200, bg-background, text-foreground)
for dark mode compatibility
- Change non-accessible `<p onClick>` to `<button>` with w-full and
type=button in column-finding-groups for keyboard accessibility
- Fix region flag mapping: asia-east1 (Taiwan) now correctly shows flag_tw
instead of flag_hk by adding specific rule before Hong Kong regex
- Fix import type usage in adapter files (value imports preserved for
runtime constants like FINDINGS_ROW_TYPE)
- Restore pageRef.current = 1 in refresh() for error path safety
43 new tests across 7 test files. 50 total tests passing.
TypeScript: 0 errors. ESLint: 0 errors.
Judgment Day: 2 rounds, 0 discrepancies remaining.
- Fix SSRF path traversal: encode checkId with encodeURIComponent in
getFindingGroupResources and getLatestFindingGroupResources URL paths
- Fix SSR crash: add isMounted guard for createPortal(document.body) in
InlineResourceContainer to prevent document access during server render
- Fix invalid Tailwind: replace mt-[-10] (no unit) with -mt-2.5 scale token
- Fix unbounded page[size]: cap at 500 via Math.min in URL builder as
defense-in-depth alongside existing chunking
- Fix pageRef race condition: move page counter increment inside fetchPage
after successful non-aborted response, remove redundant pre-set from
refresh() to prevent permanent page skip on concurrent abort
26 tests across 4 test suites (2 new, 2 extended). All passing.
TypeScript: 0 errors in changed files. ESLint: 0 errors.
Claude Code validation: PASSED.
Pre-existing healthcheck failures (.next/types, @codemirror) unrelated.