Skip to content

feat(#6645): use AND semantics for docs site multi-word search - #6799

Merged
maruiz93 merged 5 commits into
mainfrom
agent/6645-docs-search-and-semantics
Sep 2, 2026
Merged

feat(#6645): use AND semantics for docs site multi-word search#6799
maruiz93 merged 5 commits into
mainfrom
agent/6645-docs-search-and-semantics

Conversation

@fullsend-ai-coder

Copy link
Copy Markdown
Contributor

Summary

Switches the vendored docs site search (VPLocalSearchBox.vue) from OR to AND semantics for multi-word queries, so searching for eval scenario returns only pages containing both terms. Adds exact-phrase support: wrapping a query in double quotes ("eval scenario") post-filters results to pages where the phrase appears adjacent.

Changes

  • docs/.vitepress/theme/searchQuery.ts — new utility module with parseSearchQuery() (extracts double-quoted phrases) and textContainsPhrases() (case-insensitive substring check)
  • docs/.vitepress/theme/components/VPLocalSearchBox.vue — passes combineWith: 'AND' to index.search(); parses query for quoted phrases; adds loadPageText() helper that renders a page module to plain text; post-filters results for exact phrase matches when quotes are present
  • docs/.vitepress/theme/searchQuery.test.ts — 17 unit tests covering query parsing edge cases and phrase matching

Testing

  • vitest run passes (44 tests, 3 test files)
  • prettier --check passes on all changed files
  • stylelint passes on the Vue component
  • Single-word queries are unaffected (AND/OR is equivalent for one term)
  • Scope filtering (matchesActiveScopes) is preserved — the filter function is merged into the same search options object

Closes #6645

Post-script verification

  • Branch is not main/master (agent/6645-docs-search-and-semantics)
  • Secret scan passed (gitleaks — 92c1d6498c16ee9fa490150a6405c1bb974aff4a..HEAD)
  • PR body secret scan passed (gitleaks — no-git)

@github-actions

github-actions Bot commented Aug 31, 2026

Copy link
Copy Markdown

Site preview

Preview: https://dd3ff2d6-site.fullsend-ai.workers.dev

Commit: 71224d0e046ff98db029126b889a9b463dd589c0

@fullsend-ai-review

fullsend-ai-review Bot commented Aug 31, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 2:13 PM UTC · Completed 2:27 PM UTC

Commit: f65eed5 · View workflow run →

Runtime: claude · Model: opus → claude-opus-4-6 · Effort: high · Cost: $3.72

@codecov

codecov Bot commented Aug 31, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@fullsend-ai-review fullsend-ai-review Bot added the risk/low PR risk: low label Aug 31, 2026
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 31, 2026

Copy link
Copy Markdown

Risk Assessment: moderate (2/5)

Details

Score unchanged from prior assessment at 2 (moderate). Tier 1 signals are identical: 4 files, 274 lines, medium blast radius, 0.25 test ratio, bot author. Tier 2 shows mild multi-author contention on doc-site.md (4 authors in 90d) and moderate co-commit coupling, but no regression history or revert signals. Tier 3 confirms tight alignment with issue #6645. Composite (T1=1.5, T2=2.0, T3=1.33 weighted 1.62) rounds to 2.

Previous run

Risk Assessment: moderate (2/5)

Details

Score increased from 1 to 2 vs prior assessment due to medium blast radius, moderate test-file ratio (0.25), and multi-author contention on doc-site.md (5 distinct authors in 90d). The PR remains well-scoped and aligned with issue #6645, but the composite math (T1=1.5, T2=2.5, T3=1.33 → 1.77 rounds to 2) places it at moderate rather than low.

Previous run (2)

Risk Assessment: low (1/5)

Details

Small, well-scoped docs-site search enhancement by a known bot author with good test coverage, stable file history, and full alignment with issue #6645 acceptance criteria. All tier signals remain consistent with prior assessment.

Previous run (3)

Risk Assessment: low (1/5)

Details

Small, well-scoped docs-site search enhancement by a known bot author with good test coverage, stable file history, and full alignment with issue #6645's acceptance criteria. Tier 1 signals slightly elevated (4 files, 271 lines, medium blast radius) but offset by zero protected paths, zero security-sensitive files, no dependency changes, and strong Tier 2/3 signals. Composite rounds to 1, consistent with prior assessment.

Previous run (4)

Risk Assessment: low (1/5)

Details

Small, well-scoped docs-site search enhancement by a known bot author with good test coverage, stable file history, and full alignment with issue #6645's acceptance criteria. Re-review: all Tier 1 signals unchanged from prior assessment; score remains 1.

Previous run (5)

Risk Assessment: low (1/5)

Details

Small, well-scoped docs-site search enhancement by a known bot author with good test coverage, stable file history, and full alignment with a recent issue's acceptance criteria.

@fullsend-ai-review

fullsend-ai-review Bot commented Aug 31, 2026

Copy link
Copy Markdown

Review

Findings

Low

  • [edge-case] docs/.vitepress/theme/searchQuery.ts:29filterByPhrases joins titles, title, and text with a single space separator. A phrase could match across field boundaries — for example, if a parent title ends with "eval" and the page title starts with "scenario", the phrase "eval scenario" would match due to the space join, even though it does not appear contiguously in any single field. The test "matches phrases spanning title and text content" explicitly validates this behavior, so the author is aware.

  • [edge-case] docs/.vitepress/theme/components/VPLocalSearchBox.vue:82 — Adding "text" to storeFields causes MiniSearch to store the full page text of every indexed document in memory. For a large docs site this could meaningfully increase the search index size in the browser. This is necessary for phrase filtering to work without async page fetching, but worth monitoring if the docs site grows significantly.

Previous run

Review

Findings

Low

  • [edge-case] docs/.vitepress/theme/components/VPLocalSearchBox.vue:82 — Adding "text" to storeFields causes MiniSearch to store the full page text of every indexed document in memory. For a large docs site this could meaningfully increase the search index size in the browser. This is necessary for phrase filtering to work without async page fetching, but worth monitoring if the docs site grows significantly.

  • [variable declaration convention] docs/.vitepress/theme/components/VPLocalSearchBox.vue:197let searchResults is declared with let but is never reassigned. The existing codebase consistently uses const for non-reassigned bindings.
    Remediation: Change let searchResults to const searchResults.


Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR
Previous run (2)

Review

Findings

Low

  • [logic-error] docs/.vitepress/theme/components/VPLocalSearchBox.vue:191 — The .slice(0, 16) result cap is applied before filterByPhrases. When the query contains quoted phrases, exact-phrase matches beyond position 16 in MiniSearch's ranked results are discarded before phrase filtering runs.
    Remediation: Move the .slice(0, 16) to after filterByPhrases, or increase the pre-filter cap when phrases are present (e.g., .slice(0, phrases.length > 0 ? 100 : 16)).

  • [edge-case] docs/.vitepress/theme/searchQuery.ts:49filterByPhrases silently keeps results that have no content (empty title, titles, and text) via the if (!content.trim()) return true guard. This is graceful degradation — if stored text is unavailable, the result is preserved rather than incorrectly discarded.


Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR
Previous run (3)

Review

Findings

Low

  • [edge-case] docs/.vitepress/theme/searchQuery.ts:49filterByPhrases silently keeps results that have no content (empty title, titles, and text) via the if (!content.trim()) return true guard. If MiniSearch fails to populate text in storeFields for certain documents — e.g., due to an index format change in a VitePress upgrade — those results would bypass phrase filtering entirely without any visible indication. The behavior is documented as graceful degradation, tested, and is a reasonable tradeoff, but the failure mode is silent.
Previous run (4)

Looks good to me

Previous run (5)

Review

Findings

Low

  • [edge-case] docs/.vitepress/theme/searchQuery.ts:25 — When two quoted phrases appear without whitespace between them (e.g., "foo bar""baz qux"), the regex replacement concatenates the trailing word of the first phrase with the leading word of the second in the query string (producing foo barbaz qux). This feeds a non-existent fused token to MiniSearch AND search, causing it to return zero results even though both phrases exist on the page.
    Remediation: Pad the replacement with a space: return " " + trimmed (the outer .trim() already strips leading/trailing whitespace), or normalize consecutive spaces in the final query.

  • [type-annotation-style] docs/.vitepress/theme/components/VPLocalSearchBox.vue:183 — The variable searchOpts is typed as Record<string, unknown>. A narrower type like { combineWith: string; filter?: (r: SearchResult) => boolean } would be more precise and catch typos at compile time.
    Remediation: Consider typing searchOpts more precisely: const searchOpts: { combineWith: string; filter?: (r: SearchResult) => boolean } = { combineWith: "AND" };

  • [missing-documentation] docs/doc-site.md:28docs/doc-site.md describes the search modal's scope-based filtering behavior but does not mention the new AND semantics for multi-word queries or the exact-phrase matching feature for quoted strings. These are user-visible behavioral changes to the same search component documented in this file.
    Remediation: Add a bullet point describing the search query behavior: multi-word queries use AND semantics (all terms must appear on a page), and double-quoted substrings are matched as exact phrases.


Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-coder

fullsend-ai-coder Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Finished Fix · ✅ Success · Started 2:29 PM UTC · Completed 2:35 PM UTC

Commit: f65eed5 · View workflow run →

Runtime: claude · Model: opus → claude-opus-4-6 · Effort: high · Cost: $1.26

fullsend-ai-coder Bot added a commit that referenced this pull request Aug 31, 2026
- Fix adjacent quoted phrases fusing tokens by padding replacements
  with spaces and normalizing whitespace in the final query string
  (e.g. "foo bar""baz qux" now produces "foo bar baz qux")
- Narrow searchOpts type from Record<string, unknown> to an explicit
  { combineWith: string; filter?: ... } for compile-time safety
- Document AND semantics and exact-phrase matching in docs/doc-site.md
- Add test for adjacent quoted phrases edge case

Addresses review feedback on #6799
@fullsend-ai-coder

Copy link
Copy Markdown
Contributor Author

🔧 Fix agent — iteration 1 (bot-triggered)

Addressed all 3 review findings: fixed adjacent-quotes edge case in parseSearchQuery with space-padded replacement and whitespace normalization, narrowed searchOpts type annotation for compile-time safety, and documented AND/phrase search behavior in doc-site.md.

Fixed (3):

  1. adjacent quoted phrases fuse tokens in parseSearchQuery (docs/.vitepress/theme/searchQuery.ts): Padded regex replacement with spaces and normalized consecutive whitespace in the final query string so adjacent quoted phrases like '"foo bar""baz qux"' produce 'foo bar baz qux' instead of 'foo barbaz qux'. Added a test covering this edge case.
  2. searchOpts typed as Record<string, unknown> (docs/.vitepress/theme/components/VPLocalSearchBox.vue): Narrowed type from Record<string, unknown> to { combineWith: string; filter?: (r: SearchResult) => boolean } for compile-time safety and typo detection.
  3. missing documentation for AND semantics and exact-phrase matching (docs/doc-site.md): Added a bullet point in the 'How it works' section documenting that multi-word queries use AND semantics and double-quoted substrings enable exact-phrase matching.

Tests: passed

Next steps:

  • /fs-review — request a re-review of the changes
  • /fs-fix <your instruction> — run another fix pass with specific guidance
  • Push commits directly — review re-runs automatically on push
    Updated by fullsend fix agent

@fullsend-ai-review

fullsend-ai-review Bot commented Aug 31, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 2:37 PM UTC · Completed 2:53 PM UTC

Commit: 1034193 · View workflow run →

Runtime: claude · Model: opus → claude-opus-4-6 · Effort: high · Cost: $4.66

@fullsend-ai-review
fullsend-ai-review Bot dismissed their stale review August 31, 2026 14:53

Superseded by updated review

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-review

fullsend-ai-review Bot commented Aug 31, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 4:47 PM UTC · Completed 5:01 PM UTC

Commit: 6a93355 · View workflow run →

Runtime: claude · Model: opus → claude-opus-4-6 · Effort: high · Cost: $3.51

fullsend-ai-review[bot]

This comment was marked as outdated.

maruiz93 pushed a commit that referenced this pull request Sep 1, 2026
- Fix adjacent quoted phrases fusing tokens by padding replacements
  with spaces and normalizing whitespace in the final query string
  (e.g. "foo bar""baz qux" now produces "foo bar baz qux")
- Narrow searchOpts type from Record<string, unknown> to an explicit
  { combineWith: string; filter?: ... } for compile-time safety
- Document AND semantics and exact-phrase matching in docs/doc-site.md
- Add test for adjacent quoted phrases edge case

Addresses review feedback on #6799
@maruiz93
maruiz93 force-pushed the agent/6645-docs-search-and-semantics branch from 6a93355 to 0934808 Compare September 1, 2026 10:14
@fullsend-ai-review

fullsend-ai-review Bot commented Sep 1, 2026

Copy link
Copy Markdown

🤖 Review · ⚠️ Cancelled · Started 10:16 AM UTC · Ended 10:21 AM UTC

Commit: 0934808 · View workflow run →

fullsend-ai-coder Bot added a commit that referenced this pull request Sep 1, 2026
Change `let searchResults` to `const searchResults` to follow
the codebase convention of using const for non-reassigned bindings.

Addresses #6799
@fullsend-ai-coder

Copy link
Copy Markdown
Contributor Author

🔧 Fix agent — iteration 4 (bot-triggered)

Addressed both review findings: changed let to const for the non-reassigned searchResults variable, and recorded a reasoned disagreement on the observational storeFields memory finding since no remediation was suggested.

Fixed (1):

  1. let searchResults should be const since it is never reassigned (docs/.vitepress/theme/components/VPLocalSearchBox.vue): Changed let searchResults to const searchResults to follow the codebase convention of using const for non-reassigned bindings.

Disagreed (1):

  1. storeFields includes text, increasing search index memory usage: The reviewer explicitly notes this is necessary for phrase filtering to work without async page fetching and is an observational advisory, not an actionable finding. No remediation was suggested. Monitoring is appropriate but no code change is warranted.

Tests: passed

Next steps:

  • /fs-review — request a re-review of the changes
  • /fs-fix <your instruction> — run another fix pass with specific guidance
  • Push commits directly — review re-runs automatically on push
    Updated by fullsend fix agent

@fullsend-ai-review

fullsend-ai-review Bot commented Sep 1, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 11:23 AM UTC · Completed 11:37 AM UTC

Commit: 1420981 · View workflow run →

Runtime: claude · Model: opus → claude-opus-4-6 · Effort: high · Cost: $4.29

@fullsend-ai-review
fullsend-ai-review Bot dismissed stale reviews from themself September 1, 2026 11:37

Superseded by updated review

@fullsend-ai-review fullsend-ai-review Bot added the ready-for-merge All reviewers approved — ready to merge label Sep 1, 2026
fullsend-ai-coder Bot and others added 5 commits September 2, 2026 16:39
Multi-word queries in the vendored VPLocalSearchBox now require all
terms to appear on the same page (combineWith: 'AND') instead of
matching any term independently (the MiniSearch default of OR).

Quoted phrases like "eval scenario" trigger exact-phrase
post-filtering: after the AND search, page modules are rendered
and their plain text is checked for the adjacent phrase. Results
whose page text cannot be loaded are kept (graceful degradation).

Changes:
- searchQuery.ts: parseSearchQuery() extracts double-quoted
  phrases; textContainsPhrases() checks case-insensitive
  substring matches.
- VPLocalSearchBox.vue: search call uses combineWith 'AND',
  parses query for phrases, post-filters via loadPageText().
- searchQuery.test.ts: 17 unit tests covering query parsing
  and phrase matching.

Closes #6645
- Fix adjacent quoted phrases fusing tokens by padding replacements
  with spaces and normalizing whitespace in the final query string
  (e.g. "foo bar""baz qux" now produces "foo bar baz qux")
- Narrow searchOpts type from Record<string, unknown> to an explicit
  { combineWith: string; filter?: ... } for compile-time safety
- Document AND semantics and exact-phrase matching in docs/doc-site.md
- Add test for adjacent quoted phrases edge case

Addresses review feedback on #6799
loadPageText() silently failed for every page because VitePress
components need runtime context not provided in the detached render.
The catch returned "" and graceful degradation kept all results,
making the phrase filter a no-op.

Replace with filterByPhrases() that checks against the text already
stored in the MiniSearch index (added "text" to storeFields). This
is synchronous, testable, and doesn't depend on page rendering.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move .slice(0, 16) after filterByPhrases so exact-phrase matches
beyond MiniSearch rank 16 are not silently discarded before phrase
filtering runs.

Addresses #6799

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Change `let searchResults` to `const searchResults` to follow
the codebase convention of using const for non-reassigned bindings.

Addresses #6799
@maruiz93
maruiz93 force-pushed the agent/6645-docs-search-and-semantics branch from 1420981 to 71224d0 Compare September 2, 2026 14:40
@fullsend-ai-review

fullsend-ai-review Bot commented Sep 2, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 2:42 PM UTC · Completed 2:59 PM UTC

Commit: 71224d0 · View workflow run →

Runtime: claude · Model: opus → claude-opus-4-6 · Effort: high · Cost: $4.07

@maruiz93
maruiz93 added this pull request to the merge queue Sep 2, 2026
Merged via the queue into main with commit 7495cdc Sep 2, 2026
22 checks passed
@maruiz93
maruiz93 deleted the agent/6645-docs-search-and-semantics branch September 2, 2026 14:58
@fullsend-ai-review

Copy link
Copy Markdown

Review skipped — this PR is already merged.

The /fs-review command only reviews open PRs/MRs.

Posted by fullsend post-review check

@fullsend-ai-retro

fullsend-ai-retro Bot commented Sep 2, 2026

Copy link
Copy Markdown

🤖 Finished Retro · ✅ Success · Started 3:00 PM UTC · Completed 3:16 PM UTC

Commit: 71224d0 · View workflow run →

Runtime: claude · Model: opus → claude-opus-4-6 · Effort: high · Cost: $5.24

@fullsend-ai-retro

Copy link
Copy Markdown

Retro: PR #6799 — AND semantics for docs site multi-word search

This fully agent-driven PR (code → review → fix, all automated) went through 7 review runs and 3 fix runs over ~49 hours before merge, costing ~$33 in total agent compute. The code agent produced a solid initial implementation, but the review-fix loop was inefficient: roughly 37% of cost went to low or zero-value activities.

Key findings

  1. Review nondeterminism on a real bug. Reviews 2 and 3 both approved commit 20303e8. Review 4, running on the exact same commit ~17.5 hours later, caught a genuine logic bug (.slice(0, 16) applied before filterByPhrases(), silently discarding phrase matches ranked beyond position 16). This means the review agent has a non-trivial false-negative rate for correctness bugs — a single pass is not sufficient for reliable detection. Filing a proposal.

  2. Style nits triggering full fix-review cycles. Review 5 used CHANGES_REQUESTED for let vs const (a pure style convention), costing $4.79 in fix + re-review. The review agent should distinguish severity levels when choosing its verdict — APPROVE with comments for style/observational findings, CHANGES_REQUESTED only for substantive issues. Filing a proposal.

  3. Autonomy readiness evidence. Human reviewers (maruiz93, rh-hemartin) added zero original findings. All 6 human inline comments were confirmations that bot findings had been addressed. The agent caught 2 real logic bugs, 2 style issues, 1 docs gap, and 2 architectural observations. This is strong evidence for agents docs: replace /implement with /code in bugfix workflow guide #427 (docs-only PR autonomy). Not filing a new proposal since docs: replace /implement with /code in bugfix workflow guide #427 tracks this.

Existing issues with new evidence

Proposals filed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-human Agent loop needs human intervention ready-for-merge All reviewers approved — ready to merge ready-for-review Triggers review agent dispatch risk/moderate PR risk: moderate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Docs search: multi-word queries match each term independently, making overloaded terms unsearchable

2 participants