perf(web): avoid quadratic markdown link scans#18387

merged1
main
GitHub fork: t3code/sweep/markdown-link-scan
opened29d agobySsaphid
View on GitHubTwo-way sync enabledPR last imported 20d ago

Problem

extractMarkdownLinkHrefs runs synchronously over the whole message every time ChatMarkdown renders, including on each streamed update. Its link regex is unanchored and retries its label and destination scans from every opening bracket, so text with many unmatched [ (fenced tool output, checklists, logs) takes quadratic time even when it contains no links. On main, 32,000 unmatched brackets take about 2.2 s per call, and 8,000 unfinished [item lines inside a fence take about 2.2 s. That is enough to freeze the chat view and composer while such a message streams. The server's thread-find indexing uses the same helper.

Change

Replace the regex with a forward scanner that yields the same hrefs:

  • Find a label's first ] once and, on failure, skip the whole label instead of rescanning its suffix from each nested [.
  • Reuse each delimiter search (whitespace, >, ), quote) while later candidates fall inside the run it already found, so unfinished destinations such as [](a[](a… are also linear.

Angle-bracket destinations, quoted titles, links inside fences, and malformed-link fallbacks behave as before.

Scope and approval

Small, focused performance fix for an obvious defect (quadratic work in a hot render path), so it uses the no-prior-issue exception. It touches one helper and its tests.

Verification

  • Per-call timings of the production helper with Node 24, median of 5 samples on a shared Linux host:

    InputmainThis PR
    32,000 unmatched [2,203 ms0.002 ms
    8,000 unfinished [item lines in a fence2,156 ms0.004 ms
    8,000 × [](a (unfinished destinations)785 ms*1.5 ms

    * measured after the label-only change, before the destination scanner was added.

  • Same output as the old regex: 50,000 generated inputs in the worker's check, 400,000 in the reviewer's, and another 200,000 random strings over []()<> "' and newline in my own differential test. All matched.

  • New cases in markdownLinks.test.ts cover the pathological inputs and nested, malformed, angle, and titled links. vp test run packages/shared/src/markdownLinks.test.ts packages/shared/src/threadFindText.test.ts: 159 passed, rerun after rebasing on current main.

  • vp lint on the touched files and typecheck in packages/shared: clean.

  • Independent review by Claude Opus 5.5 (high), two rounds through T3 delegated tasks. Round 1 found the remaining quadratic destination scan, which this PR fixes. Round 2 found no defects.

  • Not measured: end-to-end frame time in a browser. The numbers are for the helper ChatMarkdown calls on every render.

Found by a GPT-6.1 Sol (high) audit and implemented by GPT-6.1 Sol (medium) in T3 Code. Reviewed by Claude Opus 5.5. Coordinated by Claude Opus 5.5 in Claude Code via T3 Code. Agent-authored, sponsored by Alex Southwell.

🤖 Generated with Claude Code

Checks

24 successful · 10 skipped · 1 neutral
All checks passed
9c57ab6

Added 1 commit

  • perf(web): avoid quadratic markdown link scans9c57ab6
Synced from GitHub

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This replaces a regex in the hot markdown-rendering and thread-indexing paths with a stateful forward scanner that changes how malformed, nested, titled, and angle-bracketed links are parsed. The scope is focused and well tested, but the production parser rewrite is nontrivial enough to warrant human review.

You can add or adjust custom eligibility rules. Learn more.

Synced from GitHub

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Path: .coderabbit.config.ts
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 64032302-5aec-4c52-ba27-6de636caaa68

📥 Commits

Reviewing files that changed from the base of the PR and between 2c8e85e6626d62d06a3dd13583f3b8c1b05c1895 and 9c57ab69206fa81e62cc7076012f3e9b39fe8b3e.


📒 Files selected for processing (2)
  • packages/shared/src/markdownLinks.test.ts
  • packages/shared/src/markdownLinks.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 0 remain after this review.



📝 Walkthrough

Walkthrough

extractMarkdownLinkHrefs now uses a cursor-based scanner instead of a global regular expression. The scanner handles angle-bracketed and bare destinations, optional quoted titles, and malformed or incomplete link syntax. Added tests cover these cases and other Markdown inputs.

Changes

Markdown link extraction

Layer / File(s)Summary
Scan link labels and destinations
packages/shared/src/markdownLinks.ts, packages/shared/src/markdownLinks.test.ts
The extractor scans link labels and parses valid destinations with optional quoted titles. It advances past labels when destination parsing fails. Tests add cases for long, incomplete, nested, malformed, and fenced-code syntax, as well as empty destinations and titled angle-bracketed destinations.

Priority: ⬇️ Low

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix

Suggested reviewers: noojuno


Merge Risk: ⚪ Minimal · up to 9c57a

No actionable issue is established; the change is mergeable after normal checks.

Architecture Summary

Architecture risk: 🟡 Medium · up to 9c57a

The change affects 1 system.

Changed systems: packages/shared

Architecture concerns No architecture-level concerns identified.

Review details

Systems and components

  • observed — packages/shared (library) was modified; 2 changed files map to changed impact.

Before / after behavior

  • observed — Modified behavior in packages/shared/src/markdownLinks.test.ts: Added table-driven extraction expectations for oversized and incomplete link syntax, nested and malformed links, fenced code, empty destinations, and titled angle-bracketed destinations.
  • observed — Modified behavior in packages/shared/src/markdownLinks.ts: Adds cached delimiter searches and a link-tail scanner that recognizes a closing parenthesis directly or after a quoted title; an unterminated quote or malformed tail fails to match.
  • observed — Modified behavior in packages/shared/src/markdownLinks.ts: Adds a destination scanner that requires an opening parenthesis, skips whitespace, tries a nonempty angle-bracket destination first, then falls back to a bare destination; returns destination bounds and the link end only when the tail is valid.
  • observed — Modified behavior in packages/shared/src/markdownLinks.ts: extractMarkdownLinkHrefs replaces the global link-matching regex with cursor-based label and destination scanning. It advances past each closing label bracket even on destination failure, and advances to the end of a match on success.

Reliability and maintainability

  • inferred — Risk-relevant change factors for packages/shared: blast_radius_1; blast_radius_3; direct_dependents_1; direct_dependents_2

Pre-merge checks | Passed 4
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title checkPassedThe title clearly and concisely describes the primary change: improving markdown link scanning performance to prevent quadratic behavior.
Description checkPassedThe description covers the problem, implementation, scope and approval rationale, verification results, test coverage, limitations, and agent attribution. It is detailed and aligned with the repositor…
Linked Issues checkPassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes checkPassedCheck skipped because no linked issues were found for this pull request.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.