Skip to content

fix(runtime): reliable Gemma routing, streaming recovery, embeddings, and ingestion - #71

Merged
WilliamAGH merged 11 commits into
mainfrom
dev
Jul 12, 2026
Merged

fix(runtime): reliable Gemma routing, streaming recovery, embeddings, and ingestion#71
WilliamAGH merged 11 commits into
mainfrom
dev

Conversation

@WilliamAGH

Copy link
Copy Markdown
Owner

Summary

Stabilizes Java Chat on the regular Gemma gateway alias and hardens the adjacent retrieval, rendering, ingestion, and error paths exposed during live dogfood.

Changes

  • raises structured reranking to the gateway-required 4,000-token reasoning budget and threads the caller timeout into the SDK request
  • retries a single shared gateway endpoint exactly once when a transient stream fails before any answer text
  • suppresses malformed enrichment control markers while preserving valid cards, prose, code fences, and ordinary braces
  • removes multiplied embedding retries, uses SDK Retry-After behavior, and keeps background probes out of active foreground work
  • makes repository ingestion deterministic, repairs incomplete vector coverage, and recreates asynchronous Qdrant operations per retry
  • publishes provider circuit transitions atomically and honors provider availability windows
  • keeps downstream failure details out of public API responses and cancels upstream work after SSE disconnect

Verification

  • full pre-push gate passed: frontend production build, Svelte checks, JVM build, 293 tests, SpotBugs/PMD, and lint
  • dev deployment completed on 6512b39
  • live dev prompt completed in one HTTP stream with no retry, no control-marker leakage, and no browser/network errors

Notes

The production generation model remains the regular gemma-4-26b-a4b alias. Structured reranking is capability-routed by the gateway to a compatible binding under that same alias.

Make the shell pipeline the sole owner of collision-safe collection names and require its resolved name at the Java boundary. Run ingestion from an immutable staged JAR so concurrent builds cannot mutate the active archive.
- Preserve canonical names for existing simple repository owners
- Reject identity mismatches before synchronization
- Stage and validate the executable application archive
Prune and force-reindex tracked files whenever their stored Qdrant point coverage is incomplete, even when the local fingerprint has not changed.
- Verify stored point counts before skipping unchanged files
- Rebuild missing vectors through the strict reindex path
Create every Qdrant future inside the retry callback so transient failures start a fresh upsert, delete, or count operation.
- Reissue failed asynchronous calls on every retry
- Cover all three operations with transient-failure tests
Synchronize circuit state transitions so availability checks cannot observe an open circuit before its retry deadline is published.
- Initialize the retry deadline to a valid instant
- Publish rate-limit state in one synchronized transition
- Exercise concurrent opens and availability checks
Wait for the latest constrained rate-limit bucket and preserve provider-declared reset deadlines across repeated failures. Verify caller cancellation leaves an otherwise healthy primary provider eligible.
- Select the longest positive reset window
- Remove compounding application backoff from persistent state
- Cover cancellation-aware provider routing
Return stable client-safe messages and diagnostics without exposing servlet messages, provider bodies, headers, filesystem paths, or exception text.
- Limit diagnostics to exception type and numeric status
- Derive public messages from trusted status values
- Cover secret-bearing downstream and servlet failures
Reference-count the shared text and heartbeat stream so cancellation of both subscribers disconnects the upstream request.
- Preserve the two-subscriber rendezvous
- Verify client disposal cancels the shared upstream
CopilotAI review requested due to automatic review settings July 12, 2026 23:06
@coderabbitai

coderabbitaiBot commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Improved streaming responses with safer provider retry and fallback behavior.
    • Added more reliable embedding availability checks during active processing.
    • Increased reranking capacity and applied configured request timeouts.
  • Bug Fixes

    • Improved Markdown enrichment handling for incomplete or malformed markers.
    • Fixed source-code reindexing when stored embeddings are incomplete.
    • Improved rate-limit recovery and transient vector operation retries.
    • Standardized GitHub repository collection naming and validation.
  • Security

    • Error responses now omit sensitive downstream exception details.

Walkthrough

This PR updates GitHub ingestion identity handling, markdown enrichment parsing, application JAR staging, provider retries and timeouts, embedding probes, rate-limit state, ingestion recovery, web error sanitization, and SSE cancellation behavior, with expanded regression and concurrency tests.

Changes

Application behavior updates

Layer / File(s)Summary
Canonical GitHub identity and ingestion wiring
docs/github-repository-ingestion.md, scripts/lib/github_identity.sh, scripts/process_github_repo.sh, src/main/java/.../GitHubRepoProcessor.java, src/test/java/.../GitHub*Test.java
Canonical collection naming and validation are centralized in the shell identity helpers, required collection metadata is enforced, and batch-sync coverage is expanded.
Robust enrichment marker parsing
frontend/src/lib/services/markdown.ts, frontend/src/lib/services/markdown.test.ts
Unresolved, nested, malformed, and fenced-code enrichment markers receive deterministic parsing and rendering behavior.
Application JAR staging
scripts/lib/common_qdrant.sh, scripts/process_github_repo.sh
The application JAR is copied, archive-validated, permission-hardened, and cleaned up after ingestion.
Bounded provider attempts and completion configuration
src/main/java/.../OpenAIStreamingService.java, StreamingAttemptContext.java, RerankerService.java, related tests
Streaming retries use bounded attempts, and completion requests carry explicit output-budget, JSON, and timeout configuration.
Embedding admission and retry execution
src/main/java/.../OpenAiCompatibleEmbeddingClient.java, EmbeddingModelKeepAlive.java, related tests
Foreground embedding activity defers probes, embedding execution uses single-attempt handling, and tier-specific retry behavior is tested.
Rate-limit and provider circuit state
src/main/java/.../ProviderCircuitState.java, RateLimitHeaderParser.java, RateLimitState.java, related tests
Circuit state is synchronized, reset windows use the latest positive header value, and provider reset times remain stable across repeated failures.
Qdrant retry and file reindex recovery
src/main/java/.../HybridVectorService.java, SourceCodeFileIngestionProcessor.java, related tests
Retry attempts recreate asynchronous Qdrant operations, while incomplete point coverage causes strict pruning and reindexing.
Safe web errors and cancellable SSE
src/main/java/.../web/*, related tests
Error responses avoid downstream secrets and shared SSE streams cancel their upstream subscription when clients disconnect.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related issues

Possibly related PRs

Suggested labels:enhancement, java, refactor

Suggested reviewers:copilot

Poem

Markers mend and streams unwind,
Providers retry, then realign.
Safe errors guard each byte,
Embeddings probe at gentler light.
JARs stand still, permissions tight—
Tests keep watch through day and night.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 22.97% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly summarizes the main runtime and reliability changes across routing, streaming, embeddings, and ingestion.
Description check✅ PassedThe description is directly related to the PR and accurately covers the key stabilization and error-handling changes.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 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.

@coderabbitaicoderabbitaiBot added enhancement New feature or request java Pull requests that update java code refactor Code refactoring labels Jul 12, 2026

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

Stabilizes Java Chat’s LLM gateway usage (Gemma alias), strengthens pre-text streaming recovery, and hardens adjacent ingestion/embeddings and client-facing error-safety behaviors that surfaced during dogfooding.

Changes:

  • Adds bounded pre-text streaming retry logic and related routing/circuit-state concurrency guarantees.
  • Reworks embedding and Qdrant retry behavior to avoid duplicated retry loops and ensure retries re-create async operations.
  • Tightens public error responses (no downstream details) and improves frontend enrichment-marker parsing resilience.

Reviewed changes

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

Show a summary per file
FileDescription
src/main/java/com/williamcallahan/javachat/web/SseSupport.javaShares SSE upstream via refCount(2) so upstream cancels when both consumers cancel.
src/test/java/com/williamcallahan/javachat/web/SseSupportTest.javaAdds coverage asserting upstream cancellation behavior with merged text/heartbeat streams.
src/main/java/com/williamcallahan/javachat/web/IngestionController.javaRemoves downstream exception messages from ingestion API responses; standardizes validation failures.
src/test/java/com/williamcallahan/javachat/web/IngestionControllerTest.javaVerifies MVC rejects oversized ingestion requests early and omits downstream secrets.
src/main/java/com/williamcallahan/javachat/web/ExceptionResponseBuilder.javaMakes exception diagnostics client-safe (type + HTTP status only).
src/test/java/com/williamcallahan/javachat/web/ExceptionResponseBuilderTest.javaEnsures headers/bodies/provider fields are not leaked in exception diagnostics.
src/main/java/com/williamcallahan/javachat/web/CustomErrorController.javaReturns stable API error messages and avoids exposing servlet error text.
src/test/java/com/williamcallahan/javachat/web/CustomErrorControllerTest.javaAdds tests preventing servlet error-message exposure and handling non-Exception throwables.
src/main/java/com/williamcallahan/javachat/web/BaseController.javaStops propagating exception messages in generic 500 responses.
src/main/java/com/williamcallahan/javachat/service/StreamingAttemptContext.javaIntroduces bounded attempt model (single-provider retry once; multi-provider try each once).
src/test/java/com/williamcallahan/javachat/service/StreamingAttemptContextTest.javaCovers bounded attempt sequencing rules for single vs multi-provider routing.
src/main/java/com/williamcallahan/javachat/service/OpenAIStreamingService.javaImplements “retry before first text” semantics and threads caller-owned timeouts into requests.
src/test/java/com/williamcallahan/javachat/service/OpenAIStreamingServiceTest.javaAdds routing assertions ensuring caller cancellation doesn’t penalize provider eligibility.
src/main/java/com/williamcallahan/javachat/service/RerankerService.javaRaises reranker completion budget and passes timeout through to streaming service completion call.
src/test/java/com/williamcallahan/javachat/service/RerankerServiceTest.javaVerifies reranker uses configured budget and timeout wiring.
src/main/java/com/williamcallahan/javachat/service/RateLimitHeaderParser.javaChooses latest positive reset bucket and simplifies reset-candidate selection.
src/test/java/com/williamcallahan/javachat/service/RateLimitHeaderParserTest.javaAdds coverage for choosing the latest positive reset window.
src/main/java/com/williamcallahan/javachat/service/RateLimitState.javaRecords provider-declared reset times without additional exponential backoff.
src/test/java/com/williamcallahan/javachat/service/RateLimitStateTest.javaEnsures repeated failures preserve reset times and track consecutive failures.
src/main/java/com/williamcallahan/javachat/service/ProviderCircuitState.javaSynchronizes state transitions to avoid partial visibility during concurrent checks/updates.
src/test/java/com/williamcallahan/javachat/service/ProviderCircuitStateTest.javaStress-tests concurrent transition + availability checks for atomicity.
src/main/java/com/williamcallahan/javachat/service/OpenAiCompatibleEmbeddingClient.javaRemoves app-level embedding retries; defers probes during active foreground embedding; tier-specific SDK retry policy.
src/test/java/com/williamcallahan/javachat/service/OpenAiCompatibleEmbeddingClientTest.javaUpdates retry expectations and adds concurrency/probe-deferral and SDK retry budget tests.
src/main/java/com/williamcallahan/javachat/service/EmbeddingModelKeepAlive.javaTreats probe deferral as non-failure and preserves last completed health observation.
src/test/java/com/williamcallahan/javachat/service/EmbeddingModelKeepAliveTest.javaAdds coverage for deferred probes preserving prior health details.
src/main/java/com/williamcallahan/javachat/service/HybridVectorService.javaRecreates Qdrant async operations per retry to avoid re-awaiting a failed future.
src/test/java/com/williamcallahan/javachat/service/HybridVectorServiceTest.javaVerifies retried Qdrant operations start fresh async calls.
src/main/java/com/williamcallahan/javachat/service/ingestion/SourceCodeFileIngestionProcessor.javaForces reindex when markers exist but point coverage is missing; prunes prior points before re-upsert.
src/test/java/com/williamcallahan/javachat/service/ingestion/SourceCodeFileIngestionProcessorTest.javaAdds coverage for “unchanged but missing points” forcing reindex and collection-name consistency.
src/main/java/com/williamcallahan/javachat/domain/ingestion/GitHubRepositoryIdentity.javaRemoves Java-side canonical collection naming in favor of script-owned canonicalization.
src/test/java/com/williamcallahan/javachat/domain/ingestion/GitHubRepositoryIdentityTest.javaUpdates identity tests to focus on canonical key/URL only.
src/test/java/com/williamcallahan/javachat/domain/ingestion/GitHubCollectionNamingScriptTest.javaAdds tests for canonical collection naming/validation implemented in shell script.
src/main/java/com/williamcallahan/javachat/cli/GitHubRepoProcessor.javaRequires explicit GITHUB_COLLECTION_NAME env var (no Java fallback naming).
scripts/lib/github_identity.shDefines canonical collection naming boundary and adds require_canonical_collection_name.
scripts/process_github_repo.shUses canonical-name requirement helper and stages JAR to prevent mutation while running.
scripts/lib/common_qdrant.shAdds stage_app_jar helper to copy + lock down runnable JAR.
docs/github-repository-ingestion.mdDocuments script as canonical owner for collection-name validation and index inventory.
frontend/src/lib/services/markdown.tsHardens enrichment-marker parsing to suppress malformed markers while preserving braces/code fences.
frontend/src/lib/services/markdown.test.tsAdds regression tests for stray braces, truncated markers, nesting recovery, and fenced-code preservation.

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

Comment on lines +60 to +65
Instant parsedResetTime = parser.parseResetInstant(headers).orElseThrow();
long remainingResetSeconds =
Duration.between(Instant.now(), parsedResetTime).getSeconds();

assertTrue(remainingResetSeconds >= 58, "Expected the 60-second token reset window");
}

@chatgpt-codex-connectorchatgpt-codex-connectorBot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:6512b3929e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +341 to 343
if (!attemptContext.hasNextAttempt()
|| emittedTextChunk.get()
|| !providerRoutingService.isStreamingFallbackEligible(streamingFailure)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Do not retry a rate-limited single provider immediately

When only one provider is configured, the new attempt model allows a second attempt against the same provider. Since isStreamingFallbackEligible treats HTTP 429/rate-limit failures as retryable and executeStreamingRequest records the rate-limit before this branch, a stream that receives a Retry-After/429 before text immediately calls the same provider again instead of honoring the newly opened availability window. This spends an extra rate-limited request and can keep failing users during provider throttling; only retry same-provider failures that are not rate-limit windows, or reselect/check provider availability before retrying.

Useful? React with 👍 / 👎.

Comment on lines 255 to 257
RequestOptions requestOptions = RequestOptions.builder()
.timeout(completeTimeout())
.timeout(completeTimeout(configuration.requestTimeout()))
.build();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep caller timeouts bounded across provider fallback

This timeout is applied separately to each provider attempt inside the fallback loop. For callers like RerankerService that pass rerankerTimeout expecting the whole LLM rerank to be bounded (the previous code wrapped completeJsonObject(...).timeout(rerankerTimeout)), a primary request can consume the full timeout and then a fallback provider can consume another full timeout, so a 45s reranker budget can block for about 90s. Track a deadline/remaining duration across the loop or keep an outer Reactor timeout.

Useful? React with 👍 / 👎.

@WilliamAGH
WilliamAGH merged commit b60790a into mainJul 12, 2026
4 checks passed

@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: 2

🧹 Nitpick comments (3)
src/main/java/com/williamcallahan/javachat/web/IngestionController.java (1)

62-73: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Server logs no longer capture any exception detail either — full diagnosability is lost.

Now that the client response omits exception.getMessage(), these log.error(...) calls become the only remaining diagnostic trail — but they only log exception.getClass().getSimpleName(), never the exception itself. Without the message or stack trace, on-call engineers get no more insight from the logs than the sanitized client response gives. CustomErrorController.logRequestFailure in this same PR already does it right by passing the exception as the trailing SLF4J arg (auto-triggers stack trace logging) — worth mirroring that here.

🔍 Proposed fix: log the exception, not just its class name
 } catch (IOException ioException) {
- log.error(- "IO error during ingestion (exception type: {})",- ioException.getClass().getSimpleName());+ log.error(+ "IO error during ingestion (exception type: {})",+ ioException.getClass().getSimpleName(),+ ioException);
return buildIngestionError(HttpStatus.INTERNAL_SERVER_ERROR, "Failed to ingest documents", ioException);
} catch (RuntimeException runtimeException) {
- log.error(- "Unexpected error during ingestion (exception type: {})",- runtimeException.getClass().getSimpleName());+ log.error(+ "Unexpected error during ingestion (exception type: {})",+ runtimeException.getClass().getSimpleName(),+ runtimeException);
return buildIngestionError(
HttpStatus.INTERNAL_SERVER_ERROR, "Failed to perform ingestion", runtimeException);
}

Apply the same pattern to the ingestLocal catch blocks (IOException/RuntimeException).

Also applies to: 88-100

🤖 Prompt for 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.
In `@src/main/java/com/williamcallahan/javachat/web/IngestionController.java`
around lines 62 - 73, Update the IOException and RuntimeException catch blocks
in ingestLocal to pass the caught exception as the trailing argument to
log.error, matching CustomErrorController.logRequestFailure and preserving the
existing contextual messages.
src/main/java/com/williamcallahan/javachat/service/EmbeddingModelKeepAlive.java (1)

104-108: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Tidbit: precompute the log-safe model name once.

modelName is final, yet the \r/\n escaping is now recomputed in three places (recordDeferred, recordSuccess, recordFailure). Since it never changes, computing it once in the constructor keeps things DRY and shaves a little work off every probe.

♻️ Proposed refactor
 private final EmbeddingClient embeddingClient;
private final String modelName;
+ private final String logSafeModelName;
private final LongSupplier nanoTime;
 EmbeddingModelKeepAlive(EmbeddingClient embeddingClient, LongSupplier nanoTime) {
this.embeddingClient = Objects.requireNonNull(embeddingClient, "embeddingClient");
this.modelName = Objects.requireNonNull(embeddingClient.modelName(), "embeddingClient.modelName");
+ this.logSafeModelName = modelName.replace("\r", "\\r").replace("\n", "\\n");
this.nanoTime = Objects.requireNonNull(nanoTime, "nanoTime");
}
 private void recordDeferred(long probeDurationMillis) {
- String logSafeModelName = modelName.replace("\r", "\\r").replace("\n", "\\n");
log.atDebug().log(() -> "event=embedding_model_probe_deferred outcome=deferred model=" + logSafeModelName
+ " durationMs=" + probeDurationMillis + " reason=foreground_embedding_active");
}

Then drop the local re-derivation in recordSuccess/recordFailure too.

🤖 Prompt for 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.
In
`@src/main/java/com/williamcallahan/javachat/service/EmbeddingModelKeepAlive.java`
around lines 104 - 108, Precompute the escaped model name once during
construction of EmbeddingModelKeepAlive and store it in a field, using the final
modelName value. Update recordDeferred, recordSuccess, and recordFailure to
reuse that field and remove their local CR/LF escaping.
frontend/src/lib/services/markdown.ts (1)

212-220: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Minor: precompute the opening prefixes once and short-circuit on {.

readEnrichmentOpening rebuilds `{{${kind}:}` strings and re-derives Object.keys(ENRICHMENT_KINDS) on every call — and it's now called for every non-fenced character position inside findEnrichmentClose's scan loop (line 281). A quick src[index] !== "{" guard plus a module-level precomputed prefix list would cut a lot of wasted work without changing behavior.

♻️ Proposed refactor
+const ENRICHMENT_OPENING_PREFIXES = Object.keys(ENRICHMENT_KINDS).map((kind) => ({+ kind,+ prefix: `{{${kind}:`,+}));+
function readEnrichmentOpening(src: string, index: number): EnrichmentOpening | null {
- for (const kind of Object.keys(ENRICHMENT_KINDS)) {- const opening = `{{${kind}:`;- if (src.startsWith(opening, index)) {- return { kind, length: opening.length };- }+ if (src[index] !== "{") {+ return null;+ }+ for (const { kind, prefix } of ENRICHMENT_OPENING_PREFIXES) {+ if (src.startsWith(prefix, index)) {+ return { kind, length: prefix.length };+ }
}
return null;
}

Also applies to: 281-284

🤖 Prompt for 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.
In `@frontend/src/lib/services/markdown.ts` around lines 212 - 220, Optimize
readEnrichmentOpening by adding an early return when src[index] is not "{", and
move the Object.keys(ENRICHMENT_KINDS)-derived opening prefixes into a
module-level precomputed collection. Reuse those prefixes during matching
instead of rebuilding strings and enumerating kinds on each call, preserving the
existing EnrichmentOpening result behavior for valid openings.
🤖 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 `@frontend/src/lib/services/markdown.ts`:
- Around line 330-331: Rename the local variable content in the rewritten
function to a domain-specific name that reflects the extracted markdown segment,
and update all references to it within that function. Leave the raw extraction
logic unchanged.
In `@scripts/lib/github_identity.sh`:
- Around line 115-120: Update the segment encoding logic used by the canonical
collection name flow in github_identity.sh, including the transformed-name
handling around encoded_owner_segment and encoded_name_segment, so transformed
segments cannot match any legal raw repository name such as a hash-suffix form.
Replace the ambiguous h<8hex> alias shape with a syntactically disjoint encoding
(or encode every segment), preserve deterministic canonical naming, and add a
regression test covering the openai/java_chat versus openai/java-chat-h<digest>
collision.
---
Nitpick comments:
In `@frontend/src/lib/services/markdown.ts`:
- Around line 212-220: Optimize readEnrichmentOpening by adding an early return
when src[index] is not "{", and move the Object.keys(ENRICHMENT_KINDS)-derived
opening prefixes into a module-level precomputed collection. Reuse those
prefixes during matching instead of rebuilding strings and enumerating kinds on
each call, preserving the existing EnrichmentOpening result behavior for valid
openings.
In
`@src/main/java/com/williamcallahan/javachat/service/EmbeddingModelKeepAlive.java`:
- Around line 104-108: Precompute the escaped model name once during
construction of EmbeddingModelKeepAlive and store it in a field, using the final
modelName value. Update recordDeferred, recordSuccess, and recordFailure to
reuse that field and remove their local CR/LF escaping.
In `@src/main/java/com/williamcallahan/javachat/web/IngestionController.java`:
- Around line 62-73: Update the IOException and RuntimeException catch blocks in
ingestLocal to pass the caught exception as the trailing argument to log.error,
matching CustomErrorController.logRequestFailure and preserving the existing
contextual messages.
🪄 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: 3617a078-eb73-43b7-b9e0-67eb3a26b3b2

📥 Commits

Reviewing files that changed from the base of the PR and between bd128e9 and 6512b39.

📒 Files selected for processing (39)
  • docs/github-repository-ingestion.md
  • frontend/src/lib/services/markdown.test.ts
  • frontend/src/lib/services/markdown.ts
  • scripts/lib/common_qdrant.sh
  • scripts/lib/github_identity.sh
  • scripts/process_github_repo.sh
  • src/main/java/com/williamcallahan/javachat/cli/GitHubRepoProcessor.java
  • src/main/java/com/williamcallahan/javachat/domain/ingestion/GitHubRepositoryIdentity.java
  • src/main/java/com/williamcallahan/javachat/service/EmbeddingModelKeepAlive.java
  • src/main/java/com/williamcallahan/javachat/service/HybridVectorService.java
  • src/main/java/com/williamcallahan/javachat/service/OpenAIStreamingService.java
  • src/main/java/com/williamcallahan/javachat/service/OpenAiCompatibleEmbeddingClient.java
  • src/main/java/com/williamcallahan/javachat/service/ProviderCircuitState.java
  • src/main/java/com/williamcallahan/javachat/service/RateLimitHeaderParser.java
  • src/main/java/com/williamcallahan/javachat/service/RateLimitState.java
  • src/main/java/com/williamcallahan/javachat/service/RerankerService.java
  • src/main/java/com/williamcallahan/javachat/service/StreamingAttemptContext.java
  • src/main/java/com/williamcallahan/javachat/service/ingestion/SourceCodeFileIngestionProcessor.java
  • src/main/java/com/williamcallahan/javachat/web/BaseController.java
  • src/main/java/com/williamcallahan/javachat/web/CustomErrorController.java
  • src/main/java/com/williamcallahan/javachat/web/ExceptionResponseBuilder.java
  • src/main/java/com/williamcallahan/javachat/web/IngestionController.java
  • src/main/java/com/williamcallahan/javachat/web/SseSupport.java
  • src/test/java/com/williamcallahan/javachat/domain/ingestion/GitHubCollectionNamingScriptTest.java
  • src/test/java/com/williamcallahan/javachat/domain/ingestion/GitHubRepositoryIdentityTest.java
  • src/test/java/com/williamcallahan/javachat/service/EmbeddingModelKeepAliveTest.java
  • src/test/java/com/williamcallahan/javachat/service/HybridVectorServiceTest.java
  • src/test/java/com/williamcallahan/javachat/service/OpenAIStreamingServiceTest.java
  • src/test/java/com/williamcallahan/javachat/service/OpenAiCompatibleEmbeddingClientTest.java
  • src/test/java/com/williamcallahan/javachat/service/ProviderCircuitStateTest.java
  • src/test/java/com/williamcallahan/javachat/service/RateLimitHeaderParserTest.java
  • src/test/java/com/williamcallahan/javachat/service/RateLimitStateTest.java
  • src/test/java/com/williamcallahan/javachat/service/RerankerServiceTest.java
  • src/test/java/com/williamcallahan/javachat/service/StreamingAttemptContextTest.java
  • src/test/java/com/williamcallahan/javachat/service/ingestion/SourceCodeFileIngestionProcessorTest.java
  • src/test/java/com/williamcallahan/javachat/web/CustomErrorControllerTest.java
  • src/test/java/com/williamcallahan/javachat/web/ExceptionResponseBuilderTest.java
  • src/test/java/com/williamcallahan/javachat/web/IngestionControllerTest.java
  • src/test/java/com/williamcallahan/javachat/web/SseSupportTest.java

Comment on lines 330 to +331
const content = src.slice(contentStart, closeIndex);
const raw = src.slice(0, closeIndex + 2);
const raw = src.slice(0, closeIndex + ENRICHMENT_CLOSE.length);

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.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Rename the new content local — it's a banned generic noun.

Line 330 introduces const content = ... inside a function that was rewritten in this diff. Per the repo's naming guideline, content is explicitly banned as a variable name, and edits touching this exact code should fix rather than perpetuate it.

As per coding guidelines, "These names and close variants are prohibited as variable, parameter, or field names: ... content, ... Use domain-specific names (Banned Generic Nouns)" and "When editing code that uses banned or generic names, rename them in the same edit ... (Legacy Fix-on-Touch)."

🐛 Proposed fix
- const content = src.slice(contentStart, closeIndex);+ const enrichmentContent = src.slice(contentStart, closeIndex);
const raw = src.slice(0, closeIndex + ENRICHMENT_CLOSE.length);
return {
type: "enrichment",
raw,
kind: opening.kind,
- content: content.trim(),+ content: enrichmentContent.trim(),
resolved: true,
};
📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
constcontent=src.slice(contentStart,closeIndex);
constraw=src.slice(0,closeIndex+2);
constraw=src.slice(0,closeIndex+ENRICHMENT_CLOSE.length);
constenrichmentContent=src.slice(contentStart,closeIndex);
constraw=src.slice(0,closeIndex+ENRICHMENT_CLOSE.length);
return{
type: "enrichment",
raw,
kind: opening.kind,
content: enrichmentContent.trim(),
resolved: true,
};
🤖 Prompt for 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.
In `@frontend/src/lib/services/markdown.ts` around lines 330 - 331, Rename the
local variable content in the rewritten function to a domain-specific name that
reflects the extracted markdown segment, and update all references to it within
that function. Leave the raw extraction logic unchanged.

Source: Coding guidelines

Comment on lines +115 to +120
local repository_boundary="-"
if [[ ! "$REPOSITORY_OWNER" =~ ^[a-z0-9]+$ ]]; then
repository_boundary="_"
fi
CANONICAL_COLLECTION_NAME="github-${encoded_owner_segment}${repository_boundary}${encoded_name_segment}"
}

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.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash# Inspect the hash representation and the segment encoder before selecting# a backward-compatible disambiguation scheme.
rg -n -A20 -B3 \
'short_sha256\(|encode_collection_segment\(|CANONICAL_COLLECTION_NAME=' \
scripts/lib/github_identity.sh

Repository: WilliamAGH/java-chat

Length of output: 2877


Avoid hash-suffix aliases in collection segments.scripts/lib/github_identity.sh:58-75,115-120 emits sanitized_segment-h<8hex> for transformed names, and that shape is still a legal raw repository name. That lets openai/java_chat collide with openai/java-chat-h<digest> and share one collection, mixing vectors and prune operations. Make transformed segments syntactically disjoint from legal raw names (or encode every segment) and add a regression test.

🤖 Prompt for 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.
In `@scripts/lib/github_identity.sh` around lines 115 - 120, Update the segment
encoding logic used by the canonical collection name flow in github_identity.sh,
including the transformed-name handling around encoded_owner_segment and
encoded_name_segment, so transformed segments cannot match any legal raw
repository name such as a hash-suffix form. Replace the ambiguous h<8hex> alias
shape with a syntactically disjoint encoding (or encode every segment), preserve
deterministic canonical naming, and add a regression test covering the
openai/java_chat versus openai/java-chat-h<digest> collision.

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

Labels

enhancementNew feature or requestjavaPull requests that update java coderefactorCode refactoring

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@WilliamAGH