perf(web): hoist transcript search normalization out of filter loop - #972
Conversation
|
👋 Jules, reporting for duty! I'm here to lend a hand with this pull request. When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down. I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job! For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with New to Jules? Learn more at jules.google/docs. For security, I will only act on instructions from the user who triggered this task. |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
🔍 PR Validation✅ Current validation passed. |
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Snapshot WarningsEnsure that dependencies are being submitted on PR branches and consider enabling retry-on-snapshot-warnings. See the documentation for more information and troubleshooting advice. Scanned FilesNone |
Agent Completion Truth Gate: BLOCKEDReasons: Machine-readable verdict{
"details": {
"invalid_fields": [
"issue.number",
"policy.agent_login",
"policy.run_id"
]
},
"reasons": [
"invalid_payload"
],
"verdict": "blocked"
} |
The perf change touches only two React components; the regenerated lockfile (1391 lines of transitive dependency churn) was unrelated scope-creep. Restoring package-lock.json to main keeps this PR a minimal, reviewable performance change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HP1GUE359AT6A6Tg4Wgqc9
Automated review — code is clean; the "Large PR" warning is a false alarmAdversarially reviewed the actual diff on head Real change (vs. current merge-base): 2 files, +17/−7.
No correctness, security, or performance regressions found in the changed code. On the " Still blocking (not code issues):
Merge decision and the truth-gate resolution require a human maintainer; this comment is review evidence only. Generated by Claude Code |
Issue #908 marks "Null and empty search behavior is preserved" as an accepted criterion, but nothing enforced it. The perf work landed via merged PR #972 (ded0ecc) and is correct; however both components carrying it had zero test coverage: - InteractiveTranscript.tsx - 0 tests - TranscriptViewer.tsx - 0 tests Both ship in production via dashboard/panels.tsx. The refactor *added* the null guards (`seg.text ? ... : false`, `part ? ... : ''`) because nullish text was a live risk - so the guards were load-bearing from day one, yet a future refactor could drop them and every check would stay green. This is not a competing implementation. Behavior is byte-for-byte preserved; the logic is only relocated so it can be tested. Approach: extract the pure search logic into src/lib/transcript-search.ts and test it in vitest's `node` environment. apps/web/vitest.config.ts deliberately avoids jsdom ("Add a jsdom project later if/when component rendering tests land"), so introducing jsdom/RTL would be an unasked-for architectural change. Precedent for the extraction already exists: src/lib/timestamp.ts is shared pure logic imported by these same two components. Rejected alternative: replicating the predicate inside the test. That validates a copy rather than the shipped code - a vacuous test. Covered invariants (16 tests): - empty / null / undefined query returns ALL segments, never zero - nullish seg.text is a non-match, never a thrown TypeError - case-insensitive matching in both directions - speaker + search compose as AND - regex metacharacters are escaped ("a.b" must not match "axb") - invalid raw patterns ( "(", "[" ) do not throw - highlight regex omits /g so .test() cannot desync via lastIndex - query is lowercased exactly ONCE per pass, not once per segment That last one asserts the actual performance property PR #972 shipped, so a regression to per-segment normalization now fails the suite instead of silently costing N allocations per keystroke. Non-vacuous by negative control - each guard reverted individually in the shipped module: NC-1 drop `seg.text ?` guard -> 1 failed / 15 passed NC-2 drop empty-query guard -> 2 failed / 14 passed NC-3 drop regex escaping -> 2 failed / 14 passed NC-4 add /g flag -> 1 failed / 15 passed NC-5 normalize inside the loop -> 1 failed / 15 passed NC-6 drop null short-circuit -> 1 failed / 15 passed restore -> 16 passed A/B against a stashed baseline (apps/web, ambient AI_GATEWAY_API_KEY unset): baseline 44 files / 245 passed / 0 failed with changes 45 files / 261 passed / 0 failed Zero new failures. tsc --noEmit and eslint both clean. Refs #908, #972 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
💡 What: Extracted
.toLowerCase()calls outside of.filterand.maploops inInteractiveTranscript.tsxandTranscriptViewer.tsx, adding short-circuit boolean checks and null-safety guards.🎯 Why: To prevent redundant memory allocations and costly string operations on every React re-render (like keystroke updates in a search box), improving main-thread performance for large transcript datasets.
📊 Impact: Removes N
toLowerCase()allocations per keystroke update while hardening the application against unexpected null or undefined strings.🔬 Measurement: Exact head
a67c01db16fb7a9000a736dddfed924522a5e078completed CI, Coverage, CodeQL, Security Scan, Secret Scan, and Dependency Review successfully; E2E was repository-skipped. Zero inline review threads exist. Verified READY preview:dpl_FMc3BKd92aTFCpyvMaxYruEpE9mb.Program reconciliation — 2026-07-24
ded0eccf3f94b4e6e8f9316fdf7ee73d9fb55bf4and reports no runtime-error clusters in the preceding 24 hours.Execution receipt:
google-labs-jules[bot]/15414235492514692486groupthinking/eventrelay-blocker-watch-20260724T1609Zbolt/optimize-text-filtering-15414235492514692486/ perf(web): hoist transcript search normalization out of filter loop #9722026-07-24T15:20:58Z2026-07-24T16:11:57Za67c01db16fb7a9000a736dddfed924522a5e078ded0eccf3f94b4e6e8f9316fdf7ee73d9fb55bf4PR created automatically by Jules for task 15414235492514692486 started by @groupthinking