') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); fix: prefer canonical Javadoc type pages by WilliamAGH · Pull Request #113 · WilliamAGH/java-chat · GitHub
Skip to content

fix: prefer canonical Javadoc type pages - #113

Merged
WilliamAGH merged 4 commits into
mainfrom
dev
Jul 18, 2026
Merged

fix: prefer canonical Javadoc type pages#113
WilliamAGH merged 4 commits into
mainfrom
dev

Conversation

@WilliamAGH

@WilliamAGHWilliamAGH commented Jul 18, 2026

Copy link
Copy Markdown
Owner

Summary

  • derive Java API package identity from manifest-governed canonical source URLs
  • validate Java 25 package names through one framework-free domain value type
  • demote auxiliary class-use and root redirect pages behind canonical type pages
  • derive member-anchor packages from canonical URLs instead of stale persisted metadata

Root cause

Unqualified selectors originally matched only the final filename, so auxiliary class-use/List.html and root redirects could receive the canonical type-page tier. The first package-aware correction trusted persisted Qdrant package metadata, but live dev data still contained an older module-prefixed form such as java.base.java.util. That prevented canonical pages from being promoted and could also produce incorrect same-package member anchors.

The final implementation uses the canonical Java API URL as the single runtime package source. JavaPackageName owns Java 25 package validation; JavaPackageExtractor owns manifest URL-to-package projection; ranking and anchor generation no longer read the persisted package field.

Verification

  • 42 focused package, selector, extractor, ranker, citation, and canonicalizer tests
  • 665 backend tests
  • frontend production build
  • PMD and SpotBugs main/test
  • frontend lint, type-aware lint, AST rules, and Svelte checks
  • signed commits and repository hooks
  • live dev regression check against pre-existing indexed metadata

CopilotAI review requested due to automatic review settings July 18, 2026 15:30
@coderabbitai

coderabbitaiBot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@WilliamAGH, you've reached your PR review limit, so we couldn't start this review.

Next review available in:27 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 18759e84-dc10-4160-be54-0ebfb146f128

📥 Commits

Reviewing files that changed from the base of the PR and between 5d30e34 and 7228282.

📒 Files selected for processing (11)
  • src/main/java/com/williamcallahan/javachat/application/search/JavaApiMethodSelector.java
  • src/main/java/com/williamcallahan/javachat/domain/javaapi/JavaPackageName.java
  • src/main/java/com/williamcallahan/javachat/service/CitationCandidateRanker.java
  • src/main/java/com/williamcallahan/javachat/service/RetrievalService.java
  • src/main/java/com/williamcallahan/javachat/service/ingestion/JavaPackageExtractor.java
  • src/main/java/com/williamcallahan/javachat/util/JavadocLinkResolver.java
  • src/test/java/com/williamcallahan/javachat/application/search/JavaApiMethodSelectorTest.java
  • src/test/java/com/williamcallahan/javachat/domain/javaapi/JavaPackageNameTest.java
  • src/test/java/com/williamcallahan/javachat/service/CitationCandidateRankerTest.java
  • src/test/java/com/williamcallahan/javachat/service/RetrievalServiceCitationTest.java
  • src/test/java/com/williamcallahan/javachat/service/ingestion/JavaPackageExtractorTest.java
📝 Walkthrough

Walkthrough

JavaDoc type-page matching now accepts candidate package metadata, validates canonical package names, and matches package-aware URL suffixes. Citation ranking and retrieval tests now supply and verify package metadata alongside Java API page URLs.

Changes

JavaDoc citation matching

Layer / File(s)Summary
Package-aware selector matching
src/main/java/.../JavaApiMethodSelector.java, src/test/.../JavaApiMethodSelectorTest.java
matchesJavadocPath now accepts candidate package metadata, validates canonical package names, and tests qualified and unqualified selector behavior.
Candidate ranking integration
src/main/java/.../CitationCandidateRanker.java, src/test/.../CitationCandidateRankerTest.java
Ranking passes package metadata to selector matching and tests ordering across type, class-use, root, malformed, and missing-metadata candidates.
Retrieval candidate validation
src/test/.../RetrievalServiceTest.java
Retrieval fixtures construct Java API candidates from package names and page filenames while populating package metadata.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

Suggested reviewers:copilot

Poem

Packages align in a JavaDoc stream,
URLs now match the selector’s dream.
Candidates rank, the paths agree,
Metadata joins the citation spree.
Tests light the way with a compiler’s gleam.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly matches the main change: preferring canonical Javadoc type pages.
Description check✅ PassedThe description is directly related to the changeset and explains the selector, ranking, and fixture updates.
Docstring Coverage✅ PassedNo functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dev

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR tightens Java API Javadoc citation selection/ranking so canonical type pages (e.g., java/util/List.html) are prioritized over auxiliary pages (e.g., class-use/List.html or root redirects), by requiring trustworthy package metadata for unqualified selectors while keeping fully-qualified selectors path-driven.

Changes:

  • Update JavaApiMethodSelector matching so unqualified selectors only match when the candidate provides a canonical Java package name; qualified selectors continue to match by URL path.
  • Update citation ranking to pass Qdrant package metadata into selector matching.
  • Expand/adjust test fixtures and add new tests covering canonical-vs-auxiliary ordering and metadata-absent/incorrect scenarios.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Show a summary per file
FileDescription
src/test/java/com/williamcallahan/javachat/service/RetrievalServiceTest.javaUpdates citation fixture builder to include canonical Qdrant package metadata and URL construction inputs.
src/test/java/com/williamcallahan/javachat/service/CitationCandidateRankerTest.javaAdds tests for canonical type-page prioritization and qualified-selector behavior when package metadata is missing/wrong; updates helper builders to include package metadata.
src/test/java/com/williamcallahan/javachat/application/search/JavaApiMethodSelectorTest.javaAdjusts selector matching tests to the new matchesJavadocPath(path, candidatePackage) signature and adds coverage for canonical-package requirement.
src/main/java/com/williamcallahan/javachat/service/CitationCandidateRanker.javaThreads candidate package metadata into type-page matching to support the new selector behavior.
src/main/java/com/williamcallahan/javachat/application/search/JavaApiMethodSelector.javaImplements the new matching rules: qualified selectors ignore candidate metadata; unqualified selectors require canonical package metadata.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@src/main/java/com/williamcallahan/javachat/application/search/JavaApiMethodSelector.java`:
- Around line 133-159: Replace the manual validation loop in
isCanonicalJavaPackageName with SourceVersion.isName using the appropriate
latest supported source version, while preserving the existing null, blank, and
surrounding-whitespace checks. Ensure reserved words and literals such as class,
true, false, and null are rejected as package names.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4d1b2232-34c2-425e-9343-0e4f6fd6e75b

📥 Commits

Reviewing files that changed from the base of the PR and between 9d727d8 and 5d30e34.

📒 Files selected for processing (5)
  • src/main/java/com/williamcallahan/javachat/application/search/JavaApiMethodSelector.java
  • src/main/java/com/williamcallahan/javachat/service/CitationCandidateRanker.java
  • src/test/java/com/williamcallahan/javachat/application/search/JavaApiMethodSelectorTest.java
  • src/test/java/com/williamcallahan/javachat/service/CitationCandidateRankerTest.java
  • src/test/java/com/williamcallahan/javachat/service/RetrievalServiceTest.java

@WilliamAGH
WilliamAGH merged commit 76420a2 into mainJul 18, 2026
7 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@WilliamAGH