Skip to content

fix(reliability): readiness-gated deploys, stable streamed enrichment text, and secure operational telemetry - #72

Merged
WilliamAGH merged 52 commits into
mainfrom
dev
Jul 13, 2026
Merged

fix(reliability): readiness-gated deploys, stable streamed enrichment text, and secure operational telemetry#72
WilliamAGH merged 52 commits into
mainfrom
dev

Conversation

@WilliamAGH

Copy link
Copy Markdown
Owner

Summary

Coolify now keeps the serving container active until its replacement passes dependency-aware readiness, preventing deployment windows from surfacing gateway failures. This release also preserves partial streamed enrichment prose, makes credential failures startup-blocking, removes duplicate provider-failure noise, narrows operational telemetry, and locks frontend builds to Node 24.15.0.

Changes

Bug Fixes

  • Stable rolling deploys: Replacement containers must pass Qdrant and embedding readiness before cutover, receive a 120-second cold-start allowance with five-second startup probes, and run Java as PID 1 so graceful termination reaches Spring Boot (Dockerfile; src/main/resources/application.properties).
  • Readable partial enrichment streams: Incomplete enrichment directives now render already-streamed prose as Markdown while hiding unfinished directive syntax and a solitary closing brace (frontend/src/lib/services/markdown.ts; frontend/src/lib/services/markdown.test.ts).
  • Accurate embedding health: Recognized provider unavailability changes readiness, while unexpected application defects propagate without overwriting the last completed health observation (EmbeddingModelKeepAlive.probeEmbeddingModel; EmbeddingModelKeepAliveTest).
  • Single provider-failure signal: Intermediate fallback failures remain warnings and terminal failures are emitted once instead of being logged repeatedly through routing, streaming, and chat layers (OpenAIStreamingService; ChatService; OpenAIStreamingServiceTest).
  • Startup credential enforcement: Missing required provider credentials fail application startup even when global lazy initialization is enabled, preventing a misconfigured replacement from accepting traffic (RequiredCredentialValidation; RequiredCredentialValidationTest).
  • Reliable error pages and diagnostics: Direct and forwarded error pages preserve their intended status, unreadable documentation resources return 500, and failure logs retain typed causes without interpolating request/resource-derived values (CustomErrorController; ErrorDocumentationController; associated controller tests).

Security and Observability

  • Narrow operational surface: Liveness, readiness, aggregate health, and Prometheus remain available to deployment and monitoring systems, while all other Actuator routes are denied before they can fall through to the public application chain (SecurityConfig.managementSecurityFilterChain; application.properties).
  • Prometheus-compatible metrics: The runtime now publishes the managed Prometheus registry at /actuator/prometheus for existing monitoring infrastructure (build.gradle.kts; gradle/libs.versions.toml).
  • Structured, lower-noise telemetry: Request failures carry bounded status, source, method, uri, and requestId fields; duplicate AOP pipeline events are removed; verbose Spring AI diagnostics remain development-only (CustomErrorController.logRequestFailure; logback-spring.xml; deleted ProcessingLogger; application-dev.properties).

Tooling

  • Reproducible Node 24 builds: package.json owns Node 24.15.0, and validation fails when .nvmrc, the lockfile, Docker build image, or setup guide drifts from that version (frontend/package.json; frontend/scripts/verify-node-version.mjs; frontend/.nvmrc; Dockerfile; docs/getting-started.md).

Validation

  • Docker build validation and full image build passed.
  • Repository pre-push gate passed: backend build, 302 tests, PMD, SpotBugs, ast-grep rules, frontend lint, Svelte diagnostics, and production frontend build.
  • Development Coolify rollout completed successfully: readiness moved from 503 to 200 before cutover, while public aggregate health and the homepage remained HTTP 200 throughout the observed transition.

Breaking Changes

  • Frontend development and builds now require Node.js 24.15.0.
  • /actuator/info and /actuator/metrics are no longer public; monitoring must use /actuator/prometheus and the health probe endpoints.

Related Issues

None.

Make frontend/package.json the canonical Node runtime owner and bind every environment selector and generated projection to it with an executable parity check.
- Build the frontend with Node 24.15.0 in Docker
- Synchronize nvm, lockfile, and setup documentation
- Fail frontend validation when a projection drifts
Render the current body of an unresolved enrichment as ordinary markdown while withholding a lone terminal brace that represents an incomplete stream close marker.
- Preserve streamed prose instead of leaking directive syntax
- Retain nested valid enrichments and completed content braces
Record recognized provider unavailability in health state while allowing unexpected runtime defects to propagate without overwriting the last completed probe observation.
- Preserve deferred-probe behavior
- Align health documentation and regression coverage
- Remove the broad RuntimeException rethrow finding
Make the immutable renderer final and narrow its PNG regression test to the checked image-read failure it can actually raise, eliminating the SpotBugs subclassing warning without suppressions.
Replace the broad checked-exception declaration in concurrent embedding tests with the interruption, execution, and timeout failures their futures can raise.
Remove low-trust request headers from failure diagnostics, preserve sanitized request context as SLF4J key-value fields, and configure every active Logback encoder to render those fields.
Regression coverage verifies levels, causes, field sanitization, bounded URI logging, and the deployed console pattern.
Narrow the concurrent provider-circuit regression test to the interruption, execution, and timeout failures exposed by Future.get, removing the broad checked-exception declaration.
Remove request-derived resource names from documentation read failures and add MVC coverage for direct, forwarded, invalid, missing, and unreadable error pages.
The read-failure regression verifies the error log carries only its typed cause and no formatting arguments.
Describe the private classloader fixture so the error-documentation regression suite satisfies the repository documentation gate without suppressing PMD.
Expose Prometheus through the Boot-managed registry while separating process liveness from dependency readiness and retaining the aggregate health contract.
- Restrict Actuator access to health probes and Prometheus
- Use the liveness group for container restart decisions
- Preserve readiness checks for Qdrant and the embedding model
- Run Java as PID 1 for reliable container signal handling
Global lazy initialization must not defer required credential failures until the first provider request.
- Mark credential validation as explicitly eager
- Preserve the existing typed startup validation behavior
The AOP logger duplicated pipeline lifecycle events already owned by ChatController and retained disabled and low-value advice.
- Delete the obsolete ProcessingLogger aspect
- Keep the canonical PIPELINE appenders and ChatController events
- Confine verbose application and Spring AI logging to the dev profile
Provider failures were logged repeatedly while propagating through service layers, producing duplicate error events for one request.
- Remove log-and-rethrow handling from ChatService
- Log only terminal completion failures after provider routing is exhausted
- Keep unavailable providers at warning severity until the request boundary
- Cover the unavailable-provider severity contract
Keep the existing container serving traffic until the replacement has completed dependency initialization and can accept application requests.
- Probe Spring Boot readiness instead of process liveness
- Allow 120 seconds for cold embedding and Qdrant startup
- Check readiness every five seconds during startup
Unexposed actuator paths could fall through to the SPA security chain instead of being rejected, obscuring the real management boundary.
- Deny every unmatched /actuator path in the application chain
- Keep aggregate health, probes, and Prometheus available
- Exercise real Boot observability endpoints and denied metrics/info routes
Credential validation must remain excluded from global lazy initialization so missing secrets fail during startup.
- Assert the configuration carries an explicit non-lazy contract
Keep aggregate and readiness health assertions independent from the scheduled embedding warm-up timing.
- Replace the embedding health contributor with a deterministic DOWN state
- Preserve the operational actuator surface checks
CopilotAI review requested due to automatic review settings July 13, 2026 02:40
@coderabbitai

coderabbitaiBot commented Jul 13, 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: 04947a6a-d1b5-4107-85b4-cb6868d22529

📥 Commits

Reviewing files that changed from the base of the PR and between b6576cd and 8552b2c.

📒 Files selected for processing (4)
  • src/main/java/com/williamcallahan/javachat/service/OpenAIStreamingService.java
  • src/main/java/com/williamcallahan/javachat/service/StreamingAttemptContext.java
  • src/test/java/com/williamcallahan/javachat/service/OpenAIStreamingServiceTest.java
  • src/test/java/com/williamcallahan/javachat/service/StreamingAttemptContextTest.java
📝 Walkthrough

Walkthrough

The PR updates container readiness and Prometheus metrics, synchronizes frontend Node.js to 24.15.0, improves streaming markdown enrichment handling, refactors backend logging with structured fields and bounded values, introduces terminal streaming failure reporting to prevent duplicate alerts, and adds comprehensive test coverage for the new error handling patterns.

Changes

Runtime observability and deployment

Layer / File(s)Summary
Container readiness and Prometheus metrics
Dockerfile, build.gradle.kts, gradle/libs.versions.toml
Adds BuildKit syntax, updates frontend image, probes Coolify readiness endpoint, executes Java with exec, and enables Prometheus metrics via Micrometer registry.
Actuator endpoint security
src/main/java/.../SecurityConfig.java, src/main/resources/application.properties
Restricts Actuator routes to health/liveness/readiness/prometheus endpoints, denies other actuator paths in app chain, configures health-group membership.
Startup validation and structured logging
src/main/java/.../RequiredCredentialValidation.java, src/main/resources/*, src/test/java/.../RequiredCredentialValidationTest.java
Makes credential validation eager, renders key-value pairs in console and pipeline appenders, adjusts Spring AI logging level, and verifies annotation behavior.

Frontend toolchain, markdown streaming, and component refactoring

Layer / File(s)Summary
Node.js version synchronization
frontend/.nvmrc, frontend/package.json, frontend/scripts/verify-node-version.mjs, Dockerfile, docs/getting-started.md, .github/workflows/build.yml
Aligns Node.js to 24.15.0 across configuration files and adds a validation script that enforces cross-file consistency during build.
Streaming markdown enrichment parsing
frontend/src/lib/services/markdown.ts, frontend/src/lib/services/markdown.test.ts
Markdown tokenizer retains unresolved enrichment content as prose, renderer parses it via the selected parser instance, and streaming-specific close detection avoids premature marker acceptance.
Assistant markdown rendering and message bubble refactoring
frontend/src/lib/components/AssistantMarkdownBody.svelte, frontend/src/lib/components/MessageBubble.svelte, frontend/src/lib/components/LearnView.svelte
New AssistantMarkdownBody component encapsulates markdown rendering, streaming cursor animation, syntax highlighting, and Java detection; MessageBubble delegates to it; LearnView updates its import for Java detection.
Java language detection extraction
frontend/src/lib/services/javaLanguageDetection.ts
Java keyword detection is extracted into a dedicated service for use by multiple components.
SSE streaming and payload validation
frontend/src/lib/services/sse.ts, frontend/src/lib/services/sse.test.ts
SSE parser enforces explicit event types with per-type schema validation, rejects invalid events via error callback and stream termination, and no longer tolerates legacy raw-text fallbacks.
CSRF error parsing and schema consolidation
frontend/src/lib/services/csrf.ts, frontend/src/lib/validation/schemas.ts, src/main/java/.../SseSupport.java
CSRF uses dedicated readCsrfError helper with JSON content-type check and schema validation; text-event schema renamed to TextChunkSchema; backend SSE support uses same record name.
Font licensing and typography updates
frontend/public/fonts/Fraunces-OFL.txt, src/main/resources/static/fonts/Fraunces-OFL.txt, frontend/src/styles/global.css
Fraunces OFL license files added; font-face declarations reorganized with variable weight and font-display swap settings.

Backend streaming failure reporting and error diagnostics

Layer / File(s)Summary
Streaming failure interfaces and context
src/main/java/.../ReportedStreamingFailure.java, src/main/java/.../StreamingFailureReporter.java, src/main/java/.../CompletionRequestConfiguration.java
ReportedStreamingFailure marks terminal failures with alerts already emitted; StreamingFailureReporter delegates terminal reporting; CompletionRequestConfiguration encapsulates completion constraints with validation.
OpenAI streaming failure exception and reporting
src/main/java/.../OpenAiStreamingFailureException.java, src/main/java/.../OpenAiStreamingFailureReporter.java
OpenAiStreamingFailureException logs bounded terminal alert with failure classification, implements ReportedStreamingFailure, and avoids exposing secrets via header/message extraction.
OpenAI streaming completion refactoring
src/main/java/.../OpenAIStreamingService.java
Completion requests use CompletionRequestConfiguration; terminal failures wrapped via reporter; retry tracks empty vs non-empty deltas; provider fallback recorded before eligibility; provider unavailability downgrades to WARN.
Structured log value sanitization
src/main/java/.../StructuredLogValue.java
New utility bounds and sanitizes strings for Logback key-value rendering by truncating, replacing unsafe characters, and converting nulls.
Custom error controller structured logging
src/main/java/.../CustomErrorController.java, src/test/java/.../CustomErrorControllerTest.java
Error logging uses structured LoggingEventBuilder with bounded field values; log level determined by status and terminal streaming context; cause attachment is conditional.
Chat and Guided Learning controller error handling
src/main/java/.../ChatController.java, src/main/java/.../GuidedLearningController.java
Both controllers detect ReportedStreamingFailure in cause chain, unwrap upstream, and emit structured logs only when no terminal context exists; request body validation added.
Error documentation controller refactoring
src/main/java/.../ErrorDocumentationController.java, src/test/java/.../ErrorDocumentationControllerTest.java
Controller uses renamed variables for clarity; comprehensive test suite validates direct/forwarded/missing documents with log capture and custom ClassLoader for failure simulation.
Embedding probe availability handling
src/main/java/.../EmbeddingModelKeepAlive.java, src/test/java/.../EmbeddingModelKeepAliveTest.java
Probe distinguishes provider unavailability from unexpected failures; catches only unavailability exceptions; health documentation reflects availability-specific transitions.
HTTP status and constant extraction
src/main/java/.../QdrantIndexInitializer.java, src/main/java/.../RateLimitService.java, src/main/java/.../HtmlContentExtractor.java, src/main/java/.../GitHubRepositoryIdentityResolver.java, config/make/common.mk, docs/configuration.md
Uses HttpStatusCode API, HttpStatus constants, and named constants for extraction thresholds; Qdrant compose file path moved to infra/ directory.
Markdown AST citation and ordered marker utilities
src/main/java/.../MarkdownAstUtils.java, src/main/java/.../OrderedMarkerScanner.java, src/main/java/.../MarkdownNormalizer.java, src/test/java/.../MarkdownAstUtilsTest.java, src/test/java/.../MarkdownNormalizerTest.java
Citation handling uses explicit loop with LinkRef unresolution; numeric ordered marker detection centralized; delimiter-position tracking enables proper whitespace validation; MarkdownNormalizer delegates detection.
Markdown controller request validation
src/main/java/.../MarkdownController.java
Adds @Valid annotation to MarkdownRenderRequest parameters for all three rendering endpoints.
Open Graph renderer cleanup
src/main/java/.../OpenGraphImageRenderer.java, src/test/java/.../OpenGraphImageRendererTest.java
Renderer marked final; test narrows exception type to IOException.

Test infrastructure and assertions

Layer / File(s)Summary
Application observability and security tests
src/test/java/.../JavaChatApplicationTests.java
Mocks EmbeddingModelKeepAlive health, configures readiness groups, validates Actuator endpoint exposure and Prometheus output via MockMvc.
Streaming failure exception and controller tests
src/test/java/.../OpenAiStreamingFailureExceptionTest.java, src/test/java/.../OpenAIStreamingServiceTest.java, src/test/java/.../ChatControllerStreamingFailureTest.java, src/test/java/.../GuidedLearningControllerStreamingFailureTest.java
Tests verify bounded alert logging without secret leakage, failure kind mapping, and that controllers unwrap terminal failures to avoid duplicate ERROR logs.
Error controller and log value tests
src/test/java/.../CustomErrorControllerTest.java, src/test/java/.../StructuredLogValueTest.java, src/test/java/.../ErrorDocumentationControllerTest.java
Validates structured log fields, console rendering, conditional causes, and sanitization behavior; covers direct/forwarded/missing documents.
Service, constant, and utility tests
src/test/java/.../RateLimitServiceTest.java, src/test/java/.../GitHubRepositoryIdentityResolverTest.java, src/test/java/.../MarkdownAstUtilsTest.java, src/test/java/.../MarkdownNormalizerTest.java, src/test/java/.../CompletionRequestConfigurationTest.java
Tests verify HTTP status constants, path validation, citation marker boundaries, numeric header indentation, and completion configuration factories.
Concurrency test exception declarations
src/test/java/.../OpenAiCompatibleEmbeddingClientTest.java, src/test/java/.../ProviderCircuitStateTest.java
Narrows throws clauses from broad Exception to specific timeout/execution exception types.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
participant Coolify
participant Docker
participant Actuator
participant HealthChecks
participant Prometheus
Coolify->>Docker: evaluate container health
Docker->>Actuator: GET /actuator/health/readiness
Actuator->>HealthChecks: evaluate readinessState<br/>qdrant + embeddingModelKeepAlive
HealthChecks-->>Actuator: readiness UP/DOWN
Actuator-->>Docker: readiness response
Docker-->>Coolify: pass/fail healthcheck
Prometheus->>Actuator: GET /actuator/prometheus
Actuator-->>Prometheus: jvm/custom metrics
Loading

Possibly related PRs

Suggested labels:refactor

Poem

Readiness glows softly bright,
Metrics stream through Prometheus light. 🟡
Node ascending to twenty-four,
Markdown prose enriches more.
Logs find structure, no secrets spill—
A tidier stack, by architect's will.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 26.63% 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 matches the main themes of the PR: readiness-gated deploys, streamed enrichment fixes, and telemetry hardening.
Description check✅ PassedThe description is directly aligned with the changeset and summarizes the deployment, streaming, observability, and tooling updates.
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.

@WilliamAGHWilliamAGH added bug Something isn't working enhancement New feature or request java Pull requests that update java code javascript Pull requests that update javascript code labels Jul 13, 2026
@coderabbitaicoderabbitaiBot added the refactor Code refactoring label Jul 13, 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

This PR strengthens deployment reliability and operational safety by gating container cutover on dependency-aware readiness, tightening public actuator exposure, reducing duplicate provider-failure noise, and making streamed enrichment output more resilient during partial renders. It also improves observability via structured key/value logging and ensures reproducible frontend builds by pinning and validating a single Node.js version across repo touchpoints.

Changes:

  • Gate rolling deployments on /actuator/health/readiness, expose only operational actuator endpoints (health probes + Prometheus), and add Prometheus registry runtime support.
  • Stabilize partial streamed enrichment rendering and reduce noisy/duplicated provider-failure logging while preserving root causes.
  • Enforce Node.js 24.15.0 consistently via package.json engines plus a repo-wide validation script.

Reviewed changes

Copilot reviewed 29 out of 31 changed files in this pull request and generated no comments.

Show a summary per file
FileDescription
src/test/java/com/williamcallahan/javachat/web/OpenGraphImageRendererTest.javaNarrows test exception signature to IOException.
src/test/java/com/williamcallahan/javachat/web/ErrorDocumentationControllerTest.javaAdds coverage for forwarded-status semantics and safe failure logging.
src/test/java/com/williamcallahan/javachat/web/CustomErrorControllerTest.javaUpdates assertions for structured logging fields and bounded/safe metadata.
src/test/java/com/williamcallahan/javachat/service/ProviderCircuitStateTest.javaRefines declared checked exceptions for concurrent test behavior.
src/test/java/com/williamcallahan/javachat/service/OpenAIStreamingServiceTest.javaAdds validation that “providers unavailable” is logged at request boundary severity.
src/test/java/com/williamcallahan/javachat/service/OpenAiCompatibleEmbeddingClientTest.javaRefines declared checked exceptions for concurrency tests.
src/test/java/com/williamcallahan/javachat/service/EmbeddingModelKeepAliveTest.javaUpdates expectations so unexpected probe failures propagate without flipping health state.
src/test/java/com/williamcallahan/javachat/JavaChatApplicationTests.javaAdds MockMvc actuator surface assertions (allowed probes/prometheus; forbid metrics/info).
src/test/java/com/williamcallahan/javachat/config/RequiredCredentialValidationTest.javaAsserts credential validation remains eager even with global lazy init.
src/main/resources/logback-spring.xmlAdds %kvp to patterns and reduces Spring AI logging noise outside dev.
src/main/resources/application.propertiesRestricts exposed actuator endpoints; enables liveness/readiness probe groups.
src/main/resources/application-dev.propertiesRestores Spring AI debug logging for dev profile only.
src/main/java/com/williamcallahan/javachat/web/OpenGraphImageRenderer.javaMakes renderer final (deterministic, cached startup rendering).
src/main/java/com/williamcallahan/javachat/web/ErrorDocumentationController.javaAvoids logging request/resource-derived interpolation while preserving HTTP status on forwards.
src/main/java/com/williamcallahan/javachat/web/CustomErrorController.javaSwitches request-failure logging to structured key/value fields with safe bounding.
src/main/java/com/williamcallahan/javachat/service/OpenAIStreamingService.javaDemotes “providers unavailable” to WARN and removes duplicate streaming-failure error logs.
src/main/java/com/williamcallahan/javachat/service/EmbeddingModelKeepAlive.javaTreats recognized provider unavailability as readiness-impacting; propagates unexpected defects.
src/main/java/com/williamcallahan/javachat/service/ChatService.javaRemoves redundant error log + rethrow on streaming errors to avoid duplicate signals.
src/main/java/com/williamcallahan/javachat/logging/ProcessingLogger.javaRemoves high-noise pipeline logging aspect.
src/main/java/com/williamcallahan/javachat/config/SecurityConfig.javaDenies non-operational actuator routes and whitelists only probes + Prometheus.
src/main/java/com/williamcallahan/javachat/config/RequiredCredentialValidation.javaForces eager startup validation via @Lazy(false).
gradle/libs.versions.tomlAdds Micrometer Prometheus registry coordinate.
build.gradle.ktsAdds Prometheus registry as a runtime dependency.
frontend/src/lib/services/markdown.tsPreserves streamed prose for incomplete enrichments while hiding unfinished directive syntax.
frontend/src/lib/services/markdown.test.tsAdds regression test for partial enrichment rendering.
frontend/scripts/verify-node-version.mjsAdds repo-wide Node version projection verification (package.json/lockfile/.nvmrc/Dockerfile/docs).
frontend/package.jsonPins Node engine to 24.15.0 and wires node-version validation into npm run validate.
frontend/package-lock.jsonUpdates lockfile engine projection to 24.15.0.
frontend/.nvmrcPins dev Node version to v24.15.0.
docs/getting-started.mdDocuments Node.js 24.15.0 requirement.
DockerfileUpdates builder image to Node 24.15.0; readiness-gated healthcheck; execs Java as PID 1.
Files not reviewed (1)
  • frontend/package-lock.json: Generated file

💡 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.

🧹 Nitpick comments (7)
frontend/src/lib/services/markdown.ts (1)

350-354: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Optional: extract shared marked.parse options into a constant.

The options object { async: false, gfm: true, breaks: false } is duplicated between the unresolved path (line 350) and the resolved path (line 381). A shared constant would keep them in lockstep if they ever need to change together.

♻️ Optional refactor
+const ENRICHMENT_PARSE_OPTIONS = { async: false, gfm: true, breaks: false } as const;+
return marked.parse(normalizeMarkdownForStreaming(unresolvedContent), {
- async: false,- gfm: true,- breaks: false,- });+ ...ENRICHMENT_PARSE_OPTIONS,+ });

And similarly at line 381:

 const innerHtml = marked.parse(normalizedContent, {
- async: false,- gfm: true,- breaks: false, // Preserve fence detection accuracy- });+ ...ENRICHMENT_PARSE_OPTIONS, // Preserve fence detection accuracy+ });
🤖 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 350 - 354, In the
markdown parsing flow, extract the duplicated { async: false, gfm: true, breaks:
false } options from both the unresolved and resolved paths into one shared
constant, then pass that constant to each marked.parse call so the
configurations remain synchronized.
src/main/resources/application.properties (1)

147-150: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Clean up now-dead /actuator/info configuration (lines 151–154).

Line 147 narrowed exposure to health,prometheus, removing info and metrics. The remaining management.info.* and info.* properties on lines 151–154 are now dead configuration — they configure an endpoint that is no longer web-exposed.

Per the Delete Unused guideline, remove these lines. Additionally, management.info.env.enabled=true (line 152) is a latent security risk: if info is ever re-added to the exposure list, environment properties (potentially including sensitive values) would be exposed without review.

🧹 Proposed cleanup
 management.endpoint.health.group.readiness.include=readinessState,qdrant,embeddingModelKeepAlive
-management.info.build.enabled=true-management.info.env.enabled=true-info.application.name=${spring.application.name}-info.deployment.commit=${SOURCE_COMMIT:unknown}
# Reduce Qdrant client warning verbosity via logging
🤖 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/resources/application.properties` around lines 147 - 150, Remove the
unused management.info.* and info.* properties from application.properties,
including management.info.env.enabled, while preserving the health, probe, and
prometheus exposure settings.

Source: Coding guidelines

Dockerfile (1)

97-101: 🩺 Stability & Availability | 🔵 Trivial

Readiness probe now gates on external dependencies — confirm cascading-failure risk is acceptable.

The HEALTHCHANGE probes /actuator/health/readiness, which includes qdrant and embeddingModelKeepAlive per application.properties line 150. If either external service is temporarily unavailable, the container is marked unhealthy and may be restarted by the orchestrator, potentially causing cascading failures during transient outages.

The --start-period=120s gives a reasonable warmup window, and --retries=3 at 30s intervals means ~90s of sustained failure before restart. Confirm this behavior aligns with your Coolify rolling-cutover expectations.

The exec java entrypoint correctly makes Java PID 1 for proper signal handling — LGTM on that change.

🤖 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 `@Dockerfile` around lines 97 - 101, Confirm whether Coolify should restart
containers when external dependencies reported by /actuator/health/readiness,
including qdrant and embeddingModelKeepAlive, are unavailable for roughly 90
seconds after the 120-second startup period. If cascading restarts during
transient outages are unacceptable, update the Docker HEALTHCHECK to use an
application-liveness endpoint or otherwise exclude dependency readiness while
preserving the existing exec java entrypoint.
src/main/java/com/williamcallahan/javachat/service/OpenAIStreamingService.java (1)

154-158: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Inconsistent log level for "LLM providers unavailable" between streaming and completion paths.

streamResponse logs at WARN (line 157) while complete logs at ERROR (line 242) for the same "no providers available" condition. The streaming path test explicitly defers error severity to the request boundary, but the completion path logs ERROR immediately — which may cause duplicate logging if the subscriber also handles the error. Consider aligning complete to WARN for consistency with the "logged once" principle.

♻️ Suggested alignment for complete
 String unavailableReason =
"LLM providers unavailable - active provider is rate limited or misconfigured";
- log.error("[LLM] {}", unavailableReason);+ log.warn("[LLM] {}", unavailableReason);
return Mono.error(new IllegalStateException(unavailableReason));
🤖 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/OpenAIStreamingService.java`
around lines 154 - 158, Update the no-provider handling in complete to log the
same unavailable-provider condition at WARN rather than ERROR, matching
streamResponse and preserving the existing error propagation behavior.
src/main/java/com/williamcallahan/javachat/web/ErrorDocumentationController.java (1)

71-82: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Log message no longer identifies which documentation file failed to read.

log.error("Failed to read error documentation page", exception) drops documentationFilename entirely. If reading one of several error pages fails in production, the log gives no way to tell which resource (not-found.html, validation-failed.html, etc.) was affected without inspecting the stack trace. The companion test only forbids a formatting argument (getArgumentArray() is null) — concatenating the filename directly into the message string keeps that contract while preserving diagnosability.

🔍 Proposed fix to restore filename context without adding a format argument
 } catch (IOException exception) {
- log.error("Failed to read error documentation page", exception);+ log.error("Failed to read error documentation page: " + documentationFilename, exception);
return ResponseEntity.internalServerError().build();
}
🤖 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/ErrorDocumentationController.java`
around lines 71 - 82, Update the IOException handler in serveHtmlFile to include
documentationFilename directly in the log message while continuing to pass
exception as the throwable and without adding formatting arguments. Preserve the
existing error context and response behavior.
src/main/java/com/williamcallahan/javachat/web/CustomErrorController.java (1)

93-94: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Redundant isApiRequest(requestUri) call.

handleError computes isApiRequest at line 93, then logRequestFailure recomputes the same value at line 114 via resolveFailureLogLevel(statusCode, isApiRequest(requestUri)). Pass the already-computed boolean through instead.

♻️ Proposed fix to avoid recomputing isApiRequest
- boolean isApiRequest = isApiRequest(requestUri);- logRequestFailure(request, statusCode, requestUri, errorExceptionAttribute);+ boolean isApiRequest = isApiRequest(requestUri);+ logRequestFailure(request, statusCode, requestUri, errorExceptionAttribute, isApiRequest);
- private void logRequestFailure(HttpServletRequest request, int statusCode, String requestUri, Object exception) {+ private void logRequestFailure(+ HttpServletRequest request, int statusCode, String requestUri, Object exception, boolean apiRequest) {
String method = safeLogField(request.getMethod());
String canonicalUri = safeLogField(requestUri.split("[?#]", 2)[0]);
String safeRequestId = safeLogField(request.getRequestId());
String source = safeLogField(request.getAttribute(RequestDispatcher.ERROR_SERVLET_NAME));
- LoggingEventBuilder requestFailureLog = log.atLevel(- resolveFailureLogLevel(statusCode, isApiRequest(requestUri)))+ LoggingEventBuilder requestFailureLog = log.atLevel(resolveFailureLogLevel(statusCode, apiRequest))
.setMessage("Request failed")

Also applies to: 107-126

🤖 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/CustomErrorController.java`
around lines 93 - 94, Update handleError and logRequestFailure to pass the
already-computed isApiRequest boolean through the call chain, and change
resolveFailureLogLevel invocation to use that parameter instead of calling
isApiRequest(requestUri) again. Preserve the existing status-code and logging
behavior.
src/test/java/com/williamcallahan/javachat/web/ErrorDocumentationControllerTest.java (1)

44-62: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Log-capture boilerplate is duplicated (and slightly inconsistent) across three test files.

This file, OpenAIStreamingServiceTest.java, and CustomErrorControllerTest.java each hand-roll the same Logger/ListAppender@BeforeEach/@AfterEach wiring. This file additionally saves/restores isAdditive(), which the other two don't — a real behavioral divergence (whether the captured logger's events also flow to the root/console appender during tests). Worth extracting a small shared JUnit extension (e.g., LogCaptureExtension) to keep this consistent and DRY.

As per coding guidelines, "Avoid redundant code. Reuse code where appropriate and consistent with clean code principles (DRY principle)" and "New abstractions must earn reuse—extend existing code first; only add new type/helper when it removes real duplication (Earn Reuse)."

🤖 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/test/java/com/williamcallahan/javachat/web/ErrorDocumentationControllerTest.java`
around lines 44 - 62, Extract the duplicated logger/ListAppender lifecycle from
captureControllerLogs and stopCapturingControllerLogs,
OpenAIStreamingServiceTest, and CustomErrorControllerTest into a shared JUnit
LogCaptureExtension. Ensure the extension consistently saves and restores logger
additivity, attaches and detaches the appender, and starts/stops and clears it,
then update all three tests to use the shared extension and remove their local
wiring.

Source: Coding guidelines

🤖 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.
Nitpick comments:
In `@Dockerfile`:
- Around line 97-101: Confirm whether Coolify should restart containers when
external dependencies reported by /actuator/health/readiness, including qdrant
and embeddingModelKeepAlive, are unavailable for roughly 90 seconds after the
120-second startup period. If cascading restarts during transient outages are
unacceptable, update the Docker HEALTHCHECK to use an application-liveness
endpoint or otherwise exclude dependency readiness while preserving the existing
exec java entrypoint.
In `@frontend/src/lib/services/markdown.ts`:
- Around line 350-354: In the markdown parsing flow, extract the duplicated {
async: false, gfm: true, breaks: false } options from both the unresolved and
resolved paths into one shared constant, then pass that constant to each
marked.parse call so the configurations remain synchronized.
In
`@src/main/java/com/williamcallahan/javachat/service/OpenAIStreamingService.java`:
- Around line 154-158: Update the no-provider handling in complete to log the
same unavailable-provider condition at WARN rather than ERROR, matching
streamResponse and preserving the existing error propagation behavior.
In `@src/main/java/com/williamcallahan/javachat/web/CustomErrorController.java`:
- Around line 93-94: Update handleError and logRequestFailure to pass the
already-computed isApiRequest boolean through the call chain, and change
resolveFailureLogLevel invocation to use that parameter instead of calling
isApiRequest(requestUri) again. Preserve the existing status-code and logging
behavior.
In
`@src/main/java/com/williamcallahan/javachat/web/ErrorDocumentationController.java`:
- Around line 71-82: Update the IOException handler in serveHtmlFile to include
documentationFilename directly in the log message while continuing to pass
exception as the throwable and without adding formatting arguments. Preserve the
existing error context and response behavior.
In `@src/main/resources/application.properties`:
- Around line 147-150: Remove the unused management.info.* and info.* properties
from application.properties, including management.info.env.enabled, while
preserving the health, probe, and prometheus exposure settings.
In
`@src/test/java/com/williamcallahan/javachat/web/ErrorDocumentationControllerTest.java`:
- Around line 44-62: Extract the duplicated logger/ListAppender lifecycle from
captureControllerLogs and stopCapturingControllerLogs,
OpenAIStreamingServiceTest, and CustomErrorControllerTest into a shared JUnit
LogCaptureExtension. Ensure the extension consistently saves and restores logger
additivity, attaches and detaches the appender, and starts/stops and clears it,
then update all three tests to use the shared extension and remove their local
wiring.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: abf52f5a-a8c5-4921-9969-2e8e6e303895

📥 Commits

Reviewing files that changed from the base of the PR and between b60790a and fb1351f.

⛔ Files ignored due to path filters (1)
  • frontend/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (30)
  • Dockerfile
  • build.gradle.kts
  • docs/getting-started.md
  • frontend/.nvmrc
  • frontend/package.json
  • frontend/scripts/verify-node-version.mjs
  • frontend/src/lib/services/markdown.test.ts
  • frontend/src/lib/services/markdown.ts
  • gradle/libs.versions.toml
  • src/main/java/com/williamcallahan/javachat/config/RequiredCredentialValidation.java
  • src/main/java/com/williamcallahan/javachat/config/SecurityConfig.java
  • src/main/java/com/williamcallahan/javachat/logging/ProcessingLogger.java
  • src/main/java/com/williamcallahan/javachat/service/ChatService.java
  • src/main/java/com/williamcallahan/javachat/service/EmbeddingModelKeepAlive.java
  • src/main/java/com/williamcallahan/javachat/service/OpenAIStreamingService.java
  • src/main/java/com/williamcallahan/javachat/web/CustomErrorController.java
  • src/main/java/com/williamcallahan/javachat/web/ErrorDocumentationController.java
  • src/main/java/com/williamcallahan/javachat/web/OpenGraphImageRenderer.java
  • src/main/resources/application-dev.properties
  • src/main/resources/application.properties
  • src/main/resources/logback-spring.xml
  • src/test/java/com/williamcallahan/javachat/JavaChatApplicationTests.java
  • src/test/java/com/williamcallahan/javachat/config/RequiredCredentialValidationTest.java
  • src/test/java/com/williamcallahan/javachat/service/EmbeddingModelKeepAliveTest.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/web/CustomErrorControllerTest.java
  • src/test/java/com/williamcallahan/javachat/web/ErrorDocumentationControllerTest.java
  • src/test/java/com/williamcallahan/javachat/web/OpenGraphImageRendererTest.java
💤 Files with no reviewable changes (1)
  • src/main/java/com/williamcallahan/javachat/logging/ProcessingLogger.java

An ambiguous two-brace suffix could consume a Java closing brace while
assistant content was still streaming. Make enrichment closure state-aware
and isolate assistant markdown rendering from message chrome.
- use separate complete and streaming Marked instances
- cover partial delimiters and literal-brace completion
- extract assistant rendering below the component size limit
Tests should load their defaults only through the test profile without
placing fake credentials in property resources.
- activate the test profile for Gradle test tasks
- supply the fake token only to the unit-test process
- move test defaults into application-test.properties
The existing dependency ranges now resolve to newer compatible frontend test and build tooling.
- record the current npm dependency graph
- keep declared package ranges unchanged
The bundled display font lacked its license and no longer matched the intended current distribution.
- update the canonical frontend font asset
- include the SIL Open Font License
- keep the generated Spring static projection byte-aligned
The client accepted missing event types and malformed payloads as display text, hiding server protocol defects and potentially exposing raw error content.
- name the shared text chunk identically at producer and consumer
- reject unsupported or invalid SSE events at the parser boundary
- cover valid and malformed stream payloads end to end
Provider lifecycle output can include empty text deltas before a terminal upstream failure, but no user-visible text has been emitted at that point.
- mark first output only for non-empty text chunks
- retain the pre-text retry path after empty deltas
- cover the terminal second-attempt context
Host and User-Agent values are client-controlled, high-cardinality fields that obscure request-failure alerts and trigger static-analysis warnings.
- retain method, canonical URI, source, and request ID
- omit host and User-Agent from structured request failures
- lock the reduced field inventory in controller tests
The test profile is a task-wide execution invariant, so repeating it across every Spring test class created duplicate configuration ownership.
- restore the single Gradle test-profile owner
- remove repeated class-level profile declarations
Numeric heading detection duplicated ordered-list marker parsing and could drift from the canonical CommonMark boundary.
- delegate numeric marker recognition to OrderedMarkerScanner
- expose the delimiter position required for whitespace validation
- cover three-digit headings and four-digit non-headings
The semantic-quality changes introduced named boundary rules that need behavior-level regression coverage.
- reject non-rate-limit OpenAI statuses
- reject incomplete GitHub repository paths
- preserve four-digit text while stripping supported citation markers
Semantic naming rules must cover the authored Vite and Vitest configuration without scanning dependency trees.
- include both frontend config entrypoints
- retain the source-only node_modules exclusion
Generic response and payload names obscured which values belong to the CSRF retry protocol.
- name request, response, parse, and validation values by boundary role
- preserve the single ingress validation path and retry behavior
The canonical parser used generic names that obscured wire text, decoded events, and validated stream contracts.
- use one StreamText name across schema and parser
- name buffering and validation state by SSE role
- cover malformed provider and JSON-shaped text events
CSS font-face rules are top-level at-rules and must not be nested inside the root selector.
- move the Fraunces declaration before :root
- preserve the variable font axes and fallback stack
The first rate-limit test retained a stale generic call-site name after the fixture was made intent-revealing.
- call the domain-specific rateLimitService fixture consistently
The backend names each emitted text payload TextChunk, while the frontend had introduced a second name for the same governed shape.
- use TextChunk for the canonical frontend schema and inferred type
- update the sole SSE parser consumer without adding an alias
Flexmark parses bracketed citation numbers as reference nodes, so text-only cleanup left unresolved numeric markers in rendered output.
- unlink unresolved numeric citation references at the AST boundary
- preserve defined numeric reference links
- traverse safely while unlinking nodes
Keep both the raw drain and Vector-derived typed events active for Java Chat
until direct application telemetry reaches parity.

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

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

148-157: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider including the exception cause in structured error logs for debuggability.

Both error handlers use log.atError() with structured key-value fields but don't attach the exception itself via .setCause(error). Without the cause, production logs will show the exception type but not the stack trace or message, making root-cause diagnosis harder.

💡 Proposed improvement: add setCause to both log statements
 // In streamLesson error handler (lines 149-156):
log.atError()
.setMessage("Guided lesson content stream error")
.addKeyValue(
"lessonSlug",
StructuredLogValue.bounded(slug, MAX_GUIDED_LOG_FIELD_LENGTH)
.text())
.addKeyValue("exceptionType", error.getClass().getSimpleName())
+ .setCause(error)
.log();
// In streamGuidedResponse error handler (lines 312-323):
log.atError()
.setMessage("Guided streaming error")
.addKeyValue(
"sessionId",
StructuredLogValue.bounded(sessionId, MAX_GUIDED_LOG_FIELD_LENGTH)
.text())
.addKeyValue(
"lessonSlug",
StructuredLogValue.bounded(lessonSlug, MAX_GUIDED_LOG_FIELD_LENGTH)
.text())
.addKeyValue("exceptionType", error.getClass().getSimpleName())
+ .setCause(error)
.log();

Also applies to: 309-328

🤖 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/GuidedLearningController.java`
around lines 148 - 157, Update both GuidedLearningController error handlers,
including the block using “Guided lesson content stream error” and the
corresponding handler around the second referenced block, to attach the caught
exception with setCause(error) before log(). Preserve the existing structured
fields and filtering behavior.
frontend/src/lib/components/MessageBubble.svelte (1)

174-185: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Merge the two .user-text rule blocks.

Now that the wrap rule is scoped solely to .user-text, it can be merged with the adjacent .user-text typography block instead of existing as two separate rule sets for the same selector.

🧹 Proposed consolidation
- /* Wrap long unbroken user strings such as URLs. */- .user-text {- overflow-wrap: break-word;- word-break: break-word;- }-- /* User text */- .user-text {- font-size: var(--text-base);- line-height: var(--leading-relaxed);- margin: 0;- }+ /* User text: wraps long unbroken strings such as URLs. */+ .user-text {+ overflow-wrap: break-word;+ word-break: break-word;+ font-size: var(--text-base);+ line-height: var(--leading-relaxed);+ margin: 0;+ }
🤖 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/components/MessageBubble.svelte` around lines 174 - 185,
Merge the adjacent `.user-text` CSS blocks in MessageBubble.svelte into one
rule, preserving all existing wrapping and typography declarations and their
current behavior.
src/main/java/com/williamcallahan/javachat/service/OpenAIStreamingService.java (1)

319-324: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Hoist the ReportedStreamingFailure unwrap into a shared helper.

This same findInCauseChain(...).map(upstreamFailure).orElse(...) unwrap sequence is duplicated verbatim in ChatController.onErrorResume. Since this behavior belongs to the ReportedStreamingFailure type itself, consider adding a static helper there (e.g., ReportedStreamingFailure.unwrapUpstream(Throwable)) and having both call sites use it, keeping the invariant in one place.

// In ReportedStreamingFailure.javastaticThrowableunwrapUpstream(Throwablefailure) {
returnfindInCauseChain(failure).map(ReportedStreamingFailure::upstreamFailure).orElse(failure);
}
🤖 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/OpenAIStreamingService.java`
around lines 319 - 324, Move the duplicated cause-chain unwrapping logic into a
shared static helper on ReportedStreamingFailure, such as
unwrapUpstream(Throwable), preserving the original fallback behavior. Update
OpenAIStreamingService.isRecoverableStreamingFailure and
ChatController.onErrorResume to call this helper instead of repeating
findInCauseChain(...).map(...).orElse(...).
🤖 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 @.github/workflows/build.yml:
- Around line 14-19: Add an explicit least-privilege permissions block to the
frontend job, alongside runs-on and defaults, granting only the permissions
required by its workflow steps and setting all other token scopes to none.
- Around line 21-22: Update the actions/checkout step in the build workflow to
set persist-credentials to false, ensuring checkout credentials are not retained
during the job.
In `@frontend/src/lib/services/javaLanguageDetection.ts`:
- Around line 32-36: Update the keyword detection in the codeBlocks iteration to
match JAVA_KEYWORDS as whole words rather than substrings, replacing the current
codeText.includes check with word-boundary-aware matching. Preserve the existing
behavior of assigning JAVA_LANGUAGE_CLASS when any keyword matches.
In `@frontend/src/styles/global.css`:
- Around line 7-19: Update the font-family declaration in the `@font-face` rule by
removing the quotes around the Fraunces name, preserving the existing font
source and variation settings.
In
`@src/main/java/com/williamcallahan/javachat/web/GuidedLearningController.java`:
- Around line 309-332: Update the stream error response in the guided streaming
handler to use a fixed user-friendly message, matching the existing “Lesson
content stream failed” behavior in streamLesson. Remove the upstreamFailure
class name from the userFacingMessage passed to sseSupport.streamErrorEvent;
keep retryable handling unchanged, and only include exception details through
diagnosticDetails if that parameter is already supported.
---
Nitpick comments:
In `@frontend/src/lib/components/MessageBubble.svelte`:
- Around line 174-185: Merge the adjacent `.user-text` CSS blocks in
MessageBubble.svelte into one rule, preserving all existing wrapping and
typography declarations and their current behavior.
In
`@src/main/java/com/williamcallahan/javachat/service/OpenAIStreamingService.java`:
- Around line 319-324: Move the duplicated cause-chain unwrapping logic into a
shared static helper on ReportedStreamingFailure, such as
unwrapUpstream(Throwable), preserving the original fallback behavior. Update
OpenAIStreamingService.isRecoverableStreamingFailure and
ChatController.onErrorResume to call this helper instead of repeating
findInCauseChain(...).map(...).orElse(...).
In
`@src/main/java/com/williamcallahan/javachat/web/GuidedLearningController.java`:
- Around line 148-157: Update both GuidedLearningController error handlers,
including the block using “Guided lesson content stream error” and the
corresponding handler around the second referenced block, to attach the caught
exception with setCause(error) before log(). Preserve the existing structured
fields and filtering behavior.
🪄 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: 850cfda7-1858-40bd-abaa-563e8c251b84

📥 Commits

Reviewing files that changed from the base of the PR and between fb1351f and b6576cd.

⛔ Files ignored due to path filters (3)
  • frontend/package-lock.json is excluded by !**/package-lock.json
  • frontend/public/fonts/Fraunces-Variable.ttf is excluded by !**/*.ttf
  • src/main/resources/static/fonts/Fraunces-Variable.ttf is excluded by !**/*.ttf
📒 Files selected for processing (53)
  • .dockerignore
  • .github/workflows/build.yml
  • Dockerfile
  • build.gradle.kts
  • config/make/common.mk
  • docs/configuration.md
  • frontend/package.json
  • frontend/public/fonts/Fraunces-OFL.txt
  • frontend/src/lib/components/AssistantMarkdownBody.svelte
  • frontend/src/lib/components/LearnView.svelte
  • frontend/src/lib/components/MessageBubble.svelte
  • frontend/src/lib/services/csrf.ts
  • frontend/src/lib/services/javaLanguageDetection.ts
  • frontend/src/lib/services/markdown.test.ts
  • frontend/src/lib/services/markdown.ts
  • frontend/src/lib/services/sse.test.ts
  • frontend/src/lib/services/sse.ts
  • frontend/src/lib/validation/schemas.ts
  • frontend/src/styles/global.css
  • infra/docker-compose-qdrant.yml
  • src/main/java/com/williamcallahan/javachat/adapters/out/llm/openai/OpenAiStreamingFailureException.java
  • src/main/java/com/williamcallahan/javachat/adapters/out/llm/openai/OpenAiStreamingFailureReporter.java
  • src/main/java/com/williamcallahan/javachat/application/completion/CompletionRequestConfiguration.java
  • src/main/java/com/williamcallahan/javachat/application/streaming/ReportedStreamingFailure.java
  • src/main/java/com/williamcallahan/javachat/application/streaming/StreamingFailureReporter.java
  • src/main/java/com/williamcallahan/javachat/config/QdrantIndexInitializer.java
  • src/main/java/com/williamcallahan/javachat/service/HtmlContentExtractor.java
  • src/main/java/com/williamcallahan/javachat/service/OpenAIStreamingService.java
  • src/main/java/com/williamcallahan/javachat/service/RateLimitService.java
  • src/main/java/com/williamcallahan/javachat/service/ingestion/GitHubRepositoryIdentityResolver.java
  • src/main/java/com/williamcallahan/javachat/service/markdown/MarkdownAstUtils.java
  • src/main/java/com/williamcallahan/javachat/service/markdown/MarkdownNormalizer.java
  • src/main/java/com/williamcallahan/javachat/service/markdown/OrderedMarkerScanner.java
  • src/main/java/com/williamcallahan/javachat/support/StructuredLogValue.java
  • src/main/java/com/williamcallahan/javachat/web/ChatController.java
  • src/main/java/com/williamcallahan/javachat/web/CustomErrorController.java
  • src/main/java/com/williamcallahan/javachat/web/GuidedLearningController.java
  • src/main/java/com/williamcallahan/javachat/web/MarkdownController.java
  • src/main/java/com/williamcallahan/javachat/web/SseSupport.java
  • src/main/resources/static/fonts/Fraunces-OFL.txt
  • src/test/java/com/williamcallahan/javachat/JavaChatApplicationTests.java
  • src/test/java/com/williamcallahan/javachat/adapters/out/llm/openai/OpenAiStreamingFailureExceptionTest.java
  • src/test/java/com/williamcallahan/javachat/application/completion/CompletionRequestConfigurationTest.java
  • src/test/java/com/williamcallahan/javachat/service/OpenAIStreamingServiceTest.java
  • src/test/java/com/williamcallahan/javachat/service/RateLimitServiceTest.java
  • src/test/java/com/williamcallahan/javachat/service/ingestion/GitHubRepositoryIdentityResolverTest.java
  • src/test/java/com/williamcallahan/javachat/service/markdown/MarkdownAstUtilsTest.java
  • src/test/java/com/williamcallahan/javachat/service/markdown/MarkdownNormalizerTest.java
  • src/test/java/com/williamcallahan/javachat/support/StructuredLogValueTest.java
  • src/test/java/com/williamcallahan/javachat/web/ChatControllerStreamingFailureTest.java
  • src/test/java/com/williamcallahan/javachat/web/CustomErrorControllerTest.java
  • src/test/java/com/williamcallahan/javachat/web/GuidedLearningControllerStreamingFailureTest.java
  • src/test/resources/application-test.properties
💤 Files with no reviewable changes (3)
  • infra/docker-compose-qdrant.yml
  • src/test/java/com/williamcallahan/javachat/JavaChatApplicationTests.java
  • src/test/resources/application-test.properties
🚧 Files skipped from review as they are similar to previous changes (2)
  • Dockerfile
  • src/main/java/com/williamcallahan/javachat/web/CustomErrorController.java

Comment thread.github/workflows/build.yml
Comment thread.github/workflows/build.yml
Comment threadfrontend/src/lib/services/javaLanguageDetection.ts
Comment threadfrontend/src/styles/global.css
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bugSomething isn't workingenhancementNew feature or requestjavaPull requests that update java codejavascriptPull requests that update javascript coderefactorCode refactoring

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@WilliamAGH