Skip to content

feat(auth): Clerk sign-in with dev-only backend session verification, plus streaming reliability and scroll-indicator fixes - #142

Merged
WilliamAGH merged 36 commits into
mainfrom
dev
Jul 30, 2026
Merged

feat(auth): Clerk sign-in with dev-only backend session verification, plus streaming reliability and scroll-indicator fixes#142
WilliamAGH merged 36 commits into
mainfrom
dev

Conversation

@WilliamAGH

Copy link
Copy Markdown
Owner

Summary

Adds Clerk authentication end-to-end — header sign-in/sign-up controls and backend session verification enabled on dev deployments — and ships nine correctness fixes from post-release review and dogfooding: stream failures are now classified by retryability with the original error preserved, the "new content" pill only appears after a genuine scroll-away, and the streaming watchdog can no longer kill healthy slow responses.

Changes

Features

  • Clerk authentication: Users can sign in and sign up from header controls; the backend verifies Clerk sessions on authenticated endpoints, enabled for dev deployments only. A missing Clerk publishable key is treated as a deployment-level switch that disables auth cleanly rather than failing (clerkAuthentication.svelte.ts, Header.svelte, ClerkConfig, ClerkAuthorizedPartyValidator, AuthenticatedUserController, application-dev.properties)
  • Themed auth surfaces: Clerk's prebuilt sign-in/sign-up UI renders in the app's Warm Precision theme instead of default styling (App.svelte, Header.svelte)

Bug Fixes

  • "New content" pill no longer appears while auto-following: During streaming, the pill's unseen-content check keyed on scroll geometry only, so content growth during active follow dropped the near-bottom ratio and surfaced the pill with zero user scrolling. The claim now requires follow mode to be disengaged by genuine scroll intent (createScrollAnchor.svelte.ts:495,546)
  • Stream failures classified by retryability: Non-OK HTTP responses and missing-body failures now surface as retryable StreamFailureErrors (429/5xx retryable, other 4xx not) with the original fetch/read error attached as cause and logged — previously network diagnostics were discarded and server errors rendered without the Retry button (sse.ts)
  • Streaming watchdog can no longer kill healthy slow responses: The first-visible-token watchdog was hardcoded at 20s under a 90s request timeout, so any first token landing at 20-90s was terminated as a timeout. The watchdog now derives from the configured request budget (OpenAIStreamingService)
  • Reranker no longer retries on provider timeouts: A coalesced waiter's retry gate matched any TimeoutException, issuing a duplicate billable LLM rerank after genuine provider timeouts; it now fires only for stage-deadline timeouts (RerankerService)
  • Capitalized fence info strings parsed per CommonMark: ```Java / ```C were misclassified as trailing prose (lowercase wasn't), breaking fence structure; any single-word suffix is now an info string regardless of case, while compact title-case prose repair is preserved for genuine cases (MarkdownNormalizer)
  • Markdown repair warning silenced for in-progress streams: The repair log no longer fires on partial streamed content where the closing fence simply hasn't arrived yet (markdown.ts)
  • Ingestion checkpoint reconcile race closed: The abandoned-run reconciler re-reads the checkpoint under the lock before overwriting, so a run completing between the unlocked read and lock acquisition no longer has its COMPLETE checkpoint regressed to PARTIAL (LocalIngestionRunStore)
  • Ingestion trap window closed: Cleanup traps are installed before the app jar is staged, so an INT/TERM can no longer leak a staged-jar temp dir (process_all_to_qdrant.sh)
  • User message bubble height capped: Long user messages no longer expand the bubble indefinitely (MessageBubble.svelte)
  • Model SSOT lint covers comma-form assignments: environment("OPENAI_MODEL", "...") drift in build.gradle.kts is now detected, not just =/: forms (check-chat-model-ssot.rb)

Breaking Changes

None

Test Plan

  • Backend: make build, make test (1003 tests), make lint all green on dev
  • Frontend: 284/284 vitest tests, svelte-check, oxlint/eslint/ast-grep clean
  • New coverage: pill-suppression regression test, SSE retryability/cause assertions, watchdog-follows-request-timeout test, reranker no-retry-on-provider-timeout test, checkpoint TOCTOU test, Clerk validator + endpoint security integration tests
  • Post-merge of the previous release, prod was dogfooded live (report in dogfood-output/2026-07-29-pr141-postdeploy/); the pill fix was verified behaviorally on dev.javachat.ai

Java Chat needs user accounts. Clerk ships no Svelte SDK, so the vanilla
@clerk/clerk-js client is wrapped in a runes composable; the npm ESM build
ships without prebuilt components, so the ui module from @clerk/ui is
passed to Clerk.load() explicitly.
- own the Clerk lifecycle in clerkAuthentication.svelte.ts (module-level
runes state, $state.raw user resource, attachment-based user button)
- lazy-load @clerk/clerk-js and @clerk/ui off the first-paint chunk
(index chunk stays at 229 kB; Clerk loads as its own async chunk)
- surface sign-in/sign-up buttons and the signed-in user button in Header
- type VITE_CLERK_PUBLISHABLE_KEY on ImportMetaEnv; ignore .env.local
variants so clerk env pull output stays untracked
The SPA now signs users in with Clerk, so the API needs to verify those
sessions. Verification is deliberately scoped to dev deployments: only the
dev profile (selected by the existing SPRING_PROFILE env var, default prod)
carries the resource-server properties, so production javachat.ai has no
Clerk configuration and /api/me deterministically denies every request
until launch there.
- add spring-boot-starter-oauth2-resource-server to the web bundle
- create the Clerk JwtDecoder only when the JWKS property exists (dev
profile); built from the JWKS URI so startup never makes a network call
- validate the azp claim against app.clerk.authorized-parties per Clerk's
manual JWT verification guide (ClerkAuthorizedPartyValidator)
- expose GET /api/me returning the verified Clerk user id; all existing
routes stay anonymous
- integration tests cover both postures: dev-shaped (401 anonymous, 200
verified) and production-shaped (403 always); live-verified against the
running app in both profiles
…switch
Production builds intentionally omit VITE_CLERK_PUBLISHABLE_KEY until
Clerk launches on javachat.ai. A missing key now means auth is disabled
for that deployment: controls stay hidden with a console note instead of
raising an error toast at every production page load.
Clerk work here spans the Svelte frontend, the Spring resource server, and
the Clerk dashboard, so every agent session was re-deriving the same API
surface from scratch. Vendoring the upstream skills gives each session and
machine one pinned reference instead.
skills-lock.json records the upstream source and content hash of each skill,
so the bundle is reproducible and drift is detectable.
- Add .agents/skills covering the Clerk core, backend API, CLI, custom UI,
organizations, billing, webhooks, testing, and per-framework guides
- Add skills-lock.json pinning every skill by source and sha256
The Clerk sign-in modal and user button rendered in the SDK stock dark
theme, clashing with the app palette and typography wherever they appeared.
- Load @clerk/ui/themes alongside clerk-js and pass an appearance built on
the Clerk dark base theme
- Map every Clerk color, font, and radius variable to a global.css design
token so the stylesheet stays the sole palette owner
- Override colorModalBackdrop to match the MobileChatDrawer ::backdrop; the
cream neutral would otherwise wash out the page
[CS1g] permits 0, 1, and -1, but the comparison rule matched literal
source text against ^(0|1)$. A long comparison carries the Java width
suffix, so remainingPreparationNanos <= 0L in SseSupport was reported
as a magic literal even though it compares against zero.
Making the suffix optional keeps the allowlist at the same three values
while covering their long forms; 5L is still rejected.
- Allow an optional l/L suffix on the 0/1 allowlist
- Add rule tests covering 0L, 1L, 0l, and a rejected 5L
lint-ast guards ast-grep with require_cmd but calls ruby bare, so a
machine without ruby fails the chat-model SSOT step with a bare
command not found and make Error 127, giving no hint that ruby is a
lint dependency or how to get it. The pre-push hook runs make lint,
so that dead end blocks every push on a fresh checkout.
- Guard ruby with require_cmd, matching the ast-grep and lefthook
entries, so the failure names the tool and the install command
HTTP and transport failures reached views as bare Errors without the retryable flag or details that server-sent error events already carry, so the UI could not decide whether to offer a retry.
- wrap non-OK responses, missing bodies, and fetch/read failures in StreamFailureError with retryable derived from the HTTP status (429 and 5xx retry, other 4xx do not)
- log the original transport error and attach it as the error cause so network diagnostics survive
The chat-model SSOT check only matched KEY=value and KEY: value forms, so a stale model passed as environment("OPENAI_MODEL", "gpt-5") escaped the scan.
- extend OPENAI_MODEL_ASSIGNMENT to also match the comma-separated environment() call form
- cover both stale and canonical comma-form strings in the self-test
The staging directory was created by mktemp before the INT/TERM traps existed, so a signal arriving during staging leaked the temporary directory and skipped the PID-file cleanup.
- install the signal traps before mktemp so cleanup always owns the staging directory
- guard the cleanup function against an unset staging directory for the pre-staging failure path
A closing fence written as ```Java was split into a fence plus trailing prose because the info-string check keyed on letter case, but per CommonMark an info-string fence only opens a block and can never close one, regardless of case.
- drop the uppercase heuristic so a lone word after the fence marker is always an info string
- cover capitalized and lowercase single-word suffixes in the normalizer tests
…timeout
A waiter with remaining stage budget retried whenever the owner failure chain held any TimeoutException, including a genuine provider transport timeout preserved by preserveProviderTimeout, issuing a second billable rerank call the provider had already charged time for.
- walk the TimeoutException cause chain for OkHttp transport-timeout markers and exclude those failures from retry
- split provider-timeout detection into containsProviderTransportTimeout shared by both call sites
- cover the stage-deadline retry and the provider-timeout no-retry paths with dedicated tests
…hdog
The visible-output watchdog owned a hardcoded 20-second deadline while the SDK request timeout follows the configured budget, so a healthy response whose first visible token lands late inside the request budget (for example with non-none reasoning effort) was killed early.
- derive the watchdog deadline from the same streamingRequestTimeout() the SDK request timeout uses
- cover the configured-timeout derivation with a virtual-time test
Reconcile-after-abandon used a checkpoint snapshot read before the run lock was acquired, so an owner that persisted a terminal checkpoint in between had its COMPLETE state regressed back to PARTIAL by the reconciliation write.
- re-read the durable checkpoint under the lock and return the fresher state when it changed
- cover the race with an ObjectMapper hook that completes the run between the two reads
…al info strings
Treating every single-word fence suffix as an info string broke the compact-prose repair: a closing fence written as ```Done was left attached instead of being split into fence plus prose. The uppercase heuristic is restored for closing-fence candidates, while a real opening fence keeps its info string because a later structural closing fence preserves the line as literal code.
- restore the capitalized-word check limited to the attached-closing-fence repair case
- cover capitalized info strings on attached opening fences and title-case prose on closing fences in both test classes
…reams
The dev-only repair warning fired on every streamed chunk whenever an enrichment marker was present, because appending the synthetic closing fence for an unfinished stream counted as a repair, drowning out genuine diagnostics.
- skip the warning when the normalization difference is append-only (the in-progress stream case); mid-text repairs still warn
- cover the silent in-progress case with a regression test
A very long pasted message (stack trace, log) rendered as an unbounded bubble many viewports tall, pushing the rest of the conversation out of reach.
- limit the user bubble to 40vh with internal scrolling so long pastes stay readable in context
@coderabbitai

coderabbitaiBot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Too many files!

This PR contains 180 files, which is 80 over the limit of 100.

To get a review, narrow the scope:
• coderabbit review --committed # exclude uncommitted changes
• coderabbit review --dir # limit to a subdirectory
• coderabbit review --base # compare against a closer base

Upgrade to a paid plan to raise the limit.

This review couldn't start because sufficient usage credits or metered capacity aren't available. Add credits or update usage-based reviews in the billing tab, then retry.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 5dcccff6-02ab-4955-96ff-5e75592791d7

📥 Commits

Reviewing files that changed from the base of the PR and between 6cf56e0 and 050dbb4.

⛔ Files ignored due to path filters (2)
  • frontend/package-lock.json is excluded by !**/package-lock.json
  • frontend/public/fonts/Fraunces-Variable.ttf is excluded by !**/*.ttf
📒 Files selected for processing (181)
  • .agents/skills/clerk-android/SKILL.md
  • .agents/skills/clerk-android/references/custom.md
  • .agents/skills/clerk-android/references/prebuilt.md
  • .agents/skills/clerk-backend-api/SKILL.md
  • .agents/skills/clerk-backend-api/evals/evals.json
  • .agents/skills/clerk-backend-api/scripts/api-specs-context.sh
  • .agents/skills/clerk-backend-api/scripts/execute-request.sh
  • .agents/skills/clerk-backend-api/scripts/extract-endpoint-detail.sh
  • .agents/skills/clerk-backend-api/scripts/extract-tag-endpoints.sh
  • .agents/skills/clerk-backend-api/scripts/extract-tags.js
  • .agents/skills/clerk-billing/SKILL.md
  • .agents/skills/clerk-billing/evals/evals.json
  • .agents/skills/clerk-billing/references/b2b-patterns.md
  • .agents/skills/clerk-billing/references/b2c-patterns.md
  • .agents/skills/clerk-billing/references/billing-components.md
  • .agents/skills/clerk-billing/references/billing-webhooks.md
  • .agents/skills/clerk-cli/SKILL.md
  • .agents/skills/clerk-cli/references/agent-mode.md
  • .agents/skills/clerk-cli/references/auth.md
  • .agents/skills/clerk-cli/references/recipes.md
  • .agents/skills/clerk-custom-ui/SKILL.md
  • .agents/skills/clerk-custom-ui/core-2/custom-sign-in.md
  • .agents/skills/clerk-custom-ui/core-2/custom-sign-up.md
  • .agents/skills/clerk-custom-ui/core-3/custom-sign-in.md
  • .agents/skills/clerk-custom-ui/core-3/custom-sign-up.md
  • .agents/skills/clerk-custom-ui/core-3/show-component.md
  • .agents/skills/clerk-expo/SKILL.md
  • .agents/skills/clerk-expo/evals/evals.json
  • .agents/skills/clerk-expo/references/custom-flows.md
  • .agents/skills/clerk-expo/references/prebuilt-components.md
  • .agents/skills/clerk-expo/references/protected-routes.md
  • .agents/skills/clerk-expo/references/recipes.md
  • .agents/skills/clerk-expo/references/setup.md
  • .agents/skills/clerk-expo/references/sso-and-native-auth.md
  • .agents/skills/clerk-nextjs-patterns/SKILL.md
  • .agents/skills/clerk-nextjs-patterns/evals/evals.json
  • .agents/skills/clerk-nextjs-patterns/references/api-routes.md
  • .agents/skills/clerk-nextjs-patterns/references/caching-auth.md
  • .agents/skills/clerk-nextjs-patterns/references/middleware-strategies.md
  • .agents/skills/clerk-nextjs-patterns/references/server-actions.md
  • .agents/skills/clerk-nextjs-patterns/references/server-vs-client.md
  • .agents/skills/clerk-nextjs-patterns/templates/nextjs-basic-auth/app/layout.tsx
  • .agents/skills/clerk-nextjs-patterns/templates/nextjs-basic-auth/app/page.tsx
  • .agents/skills/clerk-nextjs-patterns/templates/nextjs-basic-auth/package.json
  • .agents/skills/clerk-nextjs-patterns/templates/nextjs-basic-auth/proxy.ts
  • .agents/skills/clerk-nextjs-patterns/templates/nextjs-basic-auth/tsconfig.json
  • .agents/skills/clerk-orgs/SKILL.md
  • .agents/skills/clerk-orgs/evals/evals.json
  • .agents/skills/clerk-orgs/references/enterprise-sso.md
  • .agents/skills/clerk-orgs/references/invitations.md
  • .agents/skills/clerk-orgs/references/nextjs-patterns.md
  • .agents/skills/clerk-orgs/references/roles-permissions.md
  • .agents/skills/clerk-react-patterns/SKILL.md
  • .agents/skills/clerk-react-patterns/evals/evals.json
  • .agents/skills/clerk-react-patterns/references/custom-flows.md
  • .agents/skills/clerk-react-patterns/references/hooks.md
  • .agents/skills/clerk-react-patterns/references/protected-routes.md
  • .agents/skills/clerk-react-patterns/references/router-integration.md
  • .agents/skills/clerk-react-patterns/templates/react-basic-auth/index.html
  • .agents/skills/clerk-react-patterns/templates/react-basic-auth/package.json
  • .agents/skills/clerk-react-patterns/templates/react-basic-auth/src/App.tsx
  • .agents/skills/clerk-react-patterns/templates/react-basic-auth/src/main.tsx
  • .agents/skills/clerk-react-patterns/templates/react-basic-auth/tsconfig.json
  • .agents/skills/clerk-react-patterns/templates/react-basic-auth/vite.config.ts
  • .agents/skills/clerk-react-router-patterns/SKILL.md
  • .agents/skills/clerk-react-router-patterns/evals/evals.json
  • .agents/skills/clerk-react-router-patterns/references/loaders-actions.md
  • .agents/skills/clerk-react-router-patterns/references/protected-routes.md
  • .agents/skills/clerk-react-router-patterns/references/ssr-auth.md
  • .agents/skills/clerk-react-router-patterns/templates/react-router-basic-auth/app/app.css
  • .agents/skills/clerk-react-router-patterns/templates/react-router-basic-auth/app/root.tsx
  • .agents/skills/clerk-react-router-patterns/templates/react-router-basic-auth/app/routes.ts
  • .agents/skills/clerk-react-router-patterns/templates/react-router-basic-auth/app/routes/home.tsx
  • .agents/skills/clerk-react-router-patterns/templates/react-router-basic-auth/package.json
  • .agents/skills/clerk-react-router-patterns/templates/react-router-basic-auth/react-router.config.ts
  • .agents/skills/clerk-react-router-patterns/templates/react-router-basic-auth/vite.config.ts
  • .agents/skills/clerk-setup/SKILL.md
  • .agents/skills/clerk-setup/evals/evals.json
  • .agents/skills/clerk-swift/SKILL.md
  • .agents/skills/clerk-swift/references/custom.md
  • .agents/skills/clerk-swift/references/prebuilt.md
  • .agents/skills/clerk-tanstack-patterns/SKILL.md
  • .agents/skills/clerk-tanstack-patterns/evals/evals.json
  • .agents/skills/clerk-tanstack-patterns/references/loaders.md
  • .agents/skills/clerk-tanstack-patterns/references/router-guards.md
  • .agents/skills/clerk-tanstack-patterns/references/server-functions.md
  • .agents/skills/clerk-tanstack-patterns/references/vinxi-server.md
  • .agents/skills/clerk-tanstack-patterns/templates/tanstack-basic-auth/package.json
  • .agents/skills/clerk-tanstack-patterns/templates/tanstack-basic-auth/src/router.tsx
  • .agents/skills/clerk-tanstack-patterns/templates/tanstack-basic-auth/src/routes/__root.tsx
  • .agents/skills/clerk-tanstack-patterns/templates/tanstack-basic-auth/src/routes/index.tsx
  • .agents/skills/clerk-tanstack-patterns/templates/tanstack-basic-auth/src/start.ts
  • .agents/skills/clerk-tanstack-patterns/templates/tanstack-basic-auth/tsconfig.json
  • .agents/skills/clerk-tanstack-patterns/templates/tanstack-basic-auth/vite.config.ts
  • .agents/skills/clerk-testing/SKILL.md
  • .agents/skills/clerk-vue-patterns/SKILL.md
  • .agents/skills/clerk-vue-patterns/evals/evals.json
  • .agents/skills/clerk-vue-patterns/references/composables.md
  • .agents/skills/clerk-vue-patterns/references/pinia-integration.md
  • .agents/skills/clerk-vue-patterns/references/vue-router-guards.md
  • .agents/skills/clerk-vue-patterns/templates/vue-basic-auth/index.html
  • .agents/skills/clerk-vue-patterns/templates/vue-basic-auth/package.json
  • .agents/skills/clerk-vue-patterns/templates/vue-basic-auth/src/App.vue
  • .agents/skills/clerk-vue-patterns/templates/vue-basic-auth/src/main.ts
  • .agents/skills/clerk-vue-patterns/templates/vue-basic-auth/src/style.css
  • .agents/skills/clerk-vue-patterns/templates/vue-basic-auth/vite.config.ts
  • .agents/skills/clerk-webhooks/SKILL.md
  • .agents/skills/clerk-webhooks/evals/evals.json
  • .agents/skills/clerk-webhooks/references/frameworks.md
  • .agents/skills/clerk/SKILL.md
  • .dockerignore
  • .gitignore
  • Dockerfile
  • Makefile
  • config/ast-grep-tests/java-no-magic-literals-comparison-test.yml
  • config/ast-grep/java-no-magic-literals.yml
  • docs/configuration.md
  • docs/getting-started.md
  • docs/retrieval-pipeline.md
  • frontend/package.json
  • frontend/public/fonts/Fraunces-OFL.txt
  • frontend/src/App.svelte
  • frontend/src/lib/components/AssistantMarkdownBody.svelte
  • frontend/src/lib/components/GuidedLessonHeader.svelte
  • frontend/src/lib/components/Header.svelte
  • frontend/src/lib/components/Header.test.ts
  • frontend/src/lib/components/LearnView.svelte
  • frontend/src/lib/components/LearnView.test.ts
  • frontend/src/lib/components/MessageBubble.svelte
  • frontend/src/lib/composables/clerkAuthentication.svelte.ts
  • frontend/src/lib/composables/createScrollAnchor.svelte.test.ts
  • frontend/src/lib/composables/createScrollAnchor.svelte.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/styles/global.css
  • frontend/src/vite-env.d.ts
  • frontend/vite.config.ts
  • gradle/libs.versions.toml
  • scripts/lint/check-chat-model-ssot.rb
  • scripts/process_all_to_qdrant.sh
  • skills-lock.json
  • src/main/java/com/williamcallahan/javachat/adapters/in/web/security/ClerkAuthorizedPartyValidator.java
  • src/main/java/com/williamcallahan/javachat/application/search/JavaApiMethodSelector.java
  • src/main/java/com/williamcallahan/javachat/config/AppProperties.java
  • src/main/java/com/williamcallahan/javachat/config/ClerkConfig.java
  • src/main/java/com/williamcallahan/javachat/config/ModelConfiguration.java
  • src/main/java/com/williamcallahan/javachat/config/SecurityConfig.java
  • src/main/java/com/williamcallahan/javachat/service/CitationCandidateRanker.java
  • src/main/java/com/williamcallahan/javachat/service/HybridSearchService.java
  • src/main/java/com/williamcallahan/javachat/service/OpenAIStreamingService.java
  • src/main/java/com/williamcallahan/javachat/service/OpenAiRequestFactory.java
  • src/main/java/com/williamcallahan/javachat/service/QdrantRetrievalConstraintBuilder.java
  • src/main/java/com/williamcallahan/javachat/service/QueryEncodingServices.java
  • src/main/java/com/williamcallahan/javachat/service/RerankerService.java
  • src/main/java/com/williamcallahan/javachat/service/RetrievalService.java
  • src/main/java/com/williamcallahan/javachat/service/ingestion/LocalIngestionRunStore.java
  • src/main/java/com/williamcallahan/javachat/service/markdown/MarkdownNormalizer.java
  • src/main/java/com/williamcallahan/javachat/web/AuthenticatedUserController.java
  • src/main/java/com/williamcallahan/javachat/web/AuthenticatedUserResponse.java
  • src/main/resources/application-dev.properties
  • src/main/resources/application-prod.properties
  • src/main/resources/application.properties
  • src/test/java/com/williamcallahan/javachat/adapters/in/web/security/ClerkAuthorizedPartyValidatorTest.java
  • src/test/java/com/williamcallahan/javachat/application/search/JavaApiMethodSelectorTest.java
  • src/test/java/com/williamcallahan/javachat/service/CitationCandidateRankerTest.java
  • src/test/java/com/williamcallahan/javachat/service/EnrichmentServiceCacheTest.java
  • src/test/java/com/williamcallahan/javachat/service/HybridSearchServiceTest.java
  • src/test/java/com/williamcallahan/javachat/service/MarkdownPreprocessingTest.java
  • src/test/java/com/williamcallahan/javachat/service/OpenAIStreamingServiceTest.java
  • src/test/java/com/williamcallahan/javachat/service/OpenAiRequestFactoryTest.java
  • src/test/java/com/williamcallahan/javachat/service/QdrantRetrievalConstraintBuilderTest.java
  • src/test/java/com/williamcallahan/javachat/service/RerankerServiceTest.java
  • src/test/java/com/williamcallahan/javachat/service/RetrievalServiceCitationTest.java
  • src/test/java/com/williamcallahan/javachat/service/RetrievalServiceTest.java
  • src/test/java/com/williamcallahan/javachat/service/ingestion/LocalIngestionRunStoreTest.java
  • src/test/java/com/williamcallahan/javachat/service/markdown/MarkdownNormalizerTest.java
  • src/test/java/com/williamcallahan/javachat/web/AuthenticatedUserControllerTest.java
  • src/test/java/com/williamcallahan/javachat/web/AuthenticatedUserEndpointClerkDisabledIntegrationTest.java
  • src/test/java/com/williamcallahan/javachat/web/AuthenticatedUserEndpointSecurityIntegrationTest.java

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.


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.

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

ℹ️ 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 +45 to +50
const publishableKey = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY;
if (!publishableKey) {
// Deliberate per-deployment switch, not an error: production builds omit
// VITE_CLERK_PUBLISHABLE_KEY until Clerk launches there, so auth controls
// stay hidden. Dev deployments and local .env.local provide the key.
console.info("Clerk authentication disabled: no VITE_CLERK_PUBLISHABLE_KEY in this 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.

P1 Badge Approve and inject the Clerk key during the image build

In the Docker/Coolify deployment path, Vite resolves this value during npm run build, but the frontend stage in Dockerfile:14-29 neither declares/passes VITE_CLERK_PUBLISHABLE_KEY nor mounts it into the build step. Supplying the variable only to the runtime container therefore leaves it undefined in the generated bundle, takes this early return, and hides authentication on dev—the only deployed profile that enables the backend verifier. Obtain the required approval for this new environment-driven setting and make the approved value available to the frontend build.

AGENTS.md reference: AGENTS.md:L103-L103

Useful? React with 👍 / 👎.

Comment on lines +71 to +76
clerkClient = loadingClient;
clerkClient.addListener((clerkResources) => {
clerkAuthentication.signedInUser = clerkResources.user ?? null;
});
clerkAuthentication.signedInUser = clerkClient.user ?? null;
clerkAuthentication.isLoaded = true;

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 Exercise backend verification from the signed-in client

Even when Clerk loads and sign-in succeeds, this listener only mirrors the SDK user into UI state. A repo-wide frontend search finds no call to /api/me, no getToken() call, and no Authorization: Bearer attachment, so the SPA never traverses the new JWT decoder, issuer, or azp validation path; backend misconfiguration can coexist with a header that appears successfully signed in. Send a session token to the authenticated endpoint as part of the sign-in slice so the advertised backend verification is actually exercised.

AGENTS.md reference: AGENTS.md:L72-L72

Useful? React with 👍 / 👎.

WilliamAGHand others added 7 commits July 29, 2026 11:21
…115)
Co-authored-by: detail-app[bot] <180357370+detail-app[bot]@users.noreply.github.com>
#130)
Co-authored-by: detail-app[bot] <180357370+detail-app[bot]@users.noreply.github.com>
#143)
Co-authored-by: detail-app[bot] <180357370+detail-app[bot]@users.noreply.github.com>
…#144)
Co-authored-by: detail-app[bot] <180357370+detail-app[bot]@users.noreply.github.com>
Co-authored-by: detail-app[bot] <180357370+detail-app[bot]@users.noreply.github.com>
detail-appBotand others added 8 commits July 29, 2026 12:03
Co-authored-by: detail-app[bot] <180357370+detail-app[bot]@users.noreply.github.com>
Co-authored-by: William Callahan <william@williamcallahan.com>
The Docker frontend stage COPY frontend/ . would otherwise bake a developer's frontend/.env.local into the image, so image contents silently depended on the builder's machine. The Clerk publishable key also had no declared ARG, leaving auth-enabled images unbuildable.
- Add **/.env and **/.env.* to .dockerignore so local env files never enter the build context
- Declare VITE_CLERK_PUBLISHABLE_KEY as a build-time ARG so Vite inlines the public publishable key during the frontend build
Production had no Clerk resource server, so /api/me denied every request
and a deploy of javachat.ai could not verify session tokens. A prod
profile now binds the clerk.javachat.ai instance; the runtime image
already activates it via SPRING_PROFILE=prod.
- add application-prod.properties with issuer, JWKS, and azp origin
- update dev-only Clerk comments in application.properties and SecurityConfig
- reword the Clerk-disabled test javadoc to the unconfigured-environment posture
Safari renders Fraunces' small-optical-size J design as a broken stub hook
at text sizes, so every serif surface follows the brand title onto Times
New Roman. A local @font-face with size-adjust 107% keeps the rendered size
matched to Fraunces (cap-height 105.7%, x-height 107.8% of its OS/2
metrics).
- repoint --font-serif at metric-adjusted local Times New Roman
- drop the Fraunces-only opsz variation pins from lesson, markdown, and
guided-header styles
- delete the unused vendored Fraunces variable font and its license
Startup hard-failed unless OPENAI_MODEL was exactly gpt-5.4, and request
construction inspected model names to gate reasoning and temperature. The
shared gateway owns upstream capability translation, so the client only
needs a non-blank alias and forwards the universal reasoning-effort
vocabulary unconditionally.
- replace the fixed gpt-5.4 startup check with a non-blank alias check
- drop ModelConfiguration.isGpt5Family and model-name inspection
- leave app.llm.reasoning-effort unset by default to keep gateway defaults
- align configuration and getting-started docs with the alias contract
OpenAiRequestFactory gated reasoning and temperature wiring on GPT-5
family name matching, which contradicts the gateway owning capability
translation. Apply the configured temperature and optional reasoning
effort uniformly for any gateway alias and rename the input token budget
off the gpt-5.4 name.
The app served no CSP, and enabling Clerk in production widens the
script surface. SecurityConfig now applies a mandatory policy bound
via app.content-security-policy; each profile allows its own Clerk
frontend-API origin alongside analytics and Google Fonts sources.
- add baseline, dev, and prod policy strings to the property files
- emit the Simple Analytics guard as a built asset so script-src
stays 'self' without inline hash pinning
- fail fast on a blank policy in AppProperties validation
- assert the header on every response in the security integration test
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@WilliamAGH