Skip to content

feat: implement crud operation for codebase - #5

Merged
Meet-08 merged 1 commit into
mainfrom
feat/codebase
Aug 5, 2026
Merged

feat: implement crud operation for codebase#5
Meet-08 merged 1 commit into
mainfrom
feat/codebase

Conversation

@Meet-08

@Meet-08 Meet-08 commented Aug 5, 2026

Copy link
Copy Markdown
Owner

This pull request primarily focuses on improving documentation consistency and code quality in the frontend project. The most significant changes include formatting and style corrections in the frontend/README.md and frontend/specs/auth.md, a minor ESLint configuration update, the addition of a new package dependency, and a small tweak to the Prettier config.

Documentation improvements:

Code quality and configuration:

  • Updated ESLint configuration to ignore the .output/** directory in frontend/eslint.config.js.
  • Fixed formatting and export style in frontend/prettier.config.js for consistency.

Dependencies:

  • Added @microsoft/fetch-event-source as a new dependency in frontend/package.json.

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features
    • Added a codebase dashboard to import, search, edit, delete, refresh, and reindex repositories.
    • Added repository status, file counts, lifecycle details, and progress feedback.
    • Added streaming codebase chat with Markdown responses, citations, quick prompts, cancellation, and copyable code.
    • Added authenticated navigation and access protection for codebase features.
  • Documentation
    • Expanded API documentation for codebase management and chat streaming.
  • Style
    • Standardized formatting across frontend components and documentation.

Walkthrough

The change adds authenticated codebase management, repository indexing controls, and SSE-based code chat. It adds backend services and endpoints, frontend schemas, hooks, routes, dialogs, dashboard components, chat rendering, configuration updates, API specifications, and broad formatting changes.

Changes

Codebase feature

Layer / File(s) Summary
Contracts and chat API
frontend/src/features/codebase/{schemas,types,api,hooks}/*, frontend/src/features/codebase/components/chat-*
Adds typed codebase contracts, CRUD APIs, cancellable SSE streaming, React Query hooks, citation rendering, and Markdown chat messages.
Frontend dashboard and dialogs
frontend/src/routes/_app/codebases/*, frontend/src/features/codebase/components/codebase-*.tsx
Adds codebase listing, filtering, statistics, import, edit, delete, reindex, and chat navigation flows.
Authenticated routes
frontend/src/routes/_app/*, frontend/src/routeTree.gen.ts
Adds the authenticated application layout, codebase routes, chat route, and generated route metadata.
Backend management
server/src/main/java/com/meet/server/feature/codebase/*, server/src/main/java/com/meet/server/feature/user/*, server/src/main/java/com/meet/server/feature/repositoryfile/*
Adds codebase listing, updates, deletion, reindexing, ownership checks, file cleanup, user locking, response mapping, and Git cleanup retries.
Backend chat streaming
server/src/main/java/com/meet/server/feature/chat/*
Adds an authenticated SSE chat controller and service with conversation memory, citations, completion events, and error events.
Configuration and specifications
server/build.gradle, server/src/main/resources/application.yaml, frontend/specs/codebase.md, server/specs/codebase.md
Switches AI and Redis configuration and documents codebase management and chat APIs.
Formatting and support
frontend/src/components/ui/*, frontend/src/routes/_auth/*, frontend/README.md, frontend/specs/auth.md, frontend/package.json
Applies formatting changes, adds .output to ESLint ignores, and adds the SSE client dependency.

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

Possibly related PRs

Poem

A rabbit streams replies through the night,
With citations tucked neat and bright.
Codebases queue, update, and chat,
Routes hop forward—just like that.
Secure Redis guards the trail,
And little carrots mark the tale.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 1.64% 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 name Status Explanation
Title check ✅ Passed The title clearly identifies the main codebase CRUD feature implemented by the changeset.
Description check ✅ Passed The description accurately covers several frontend documentation and configuration changes in the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/codebase

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 Biome (2.5.6)
frontend/src/styles.css

File contains syntax errors that prevent linting: Line 3: Tailwind-specific syntax is disabled.; Line 7: Tailwind-specific syntax is disabled.; Line 120: Tailwind-specific syntax is disabled.; Line 367: Tailwind-specific syntax is disabled.


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.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 30

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@frontend/specs/codebase.md`:
- Around line 49-60: Update the Responses and Errors sections in
frontend/specs/codebase.md lines 49-60 and server/specs/codebase.md lines 49-60
to document separate 400 response examples: retain field-level data for
validation failures and show INVALID_CLONE_URL as a domain error with data:
null. Apply the same distinct examples and descriptions in both files.
- Around line 75-93: Update the PATCH codebase contract in
frontend/specs/codebase.md lines 75-93 and server/specs/codebase.md lines 76-94
so changing branch cannot leave indexed data silently stale: explicitly reject
branch changes, queue a reindex, or mark the codebase stale and prevent chat
from using that index until refreshed. Keep both specifications consistent.

In `@frontend/src/features/codebase/api/codebase.api.ts`:
- Around line 121-145: In the message handler’s JSON parsing within the
onmessage callback, remove the content, message, and text object-payload
branches because ChatService emits serialized string values for this event;
retain parsing that unwraps JSON string literals and the raw-data fallback.
- Around line 67-93: Update streamChatApi and its startStream flow to refresh an
expired access token, not only a missing one, and retry the SSE connection once
after a 401 response using the refreshed token. Preserve the existing
AbortController and callback behavior, and ensure the implementation cannot
retry indefinitely; keep the doc comment accurate with the resulting refresh
behavior.
- Around line 154-156: Update the done branch in the SSE event handling switch
to JSON-decode event.data before passing it to callbacks.onDone, matching the
parsing behavior of the message, citations, and error branches. Ensure onDone
receives the raw chat ID without surrounding JSON string quotes.

In `@frontend/src/features/codebase/components/chat-message-item.tsx`:
- Around line 159-163: Update handleCopy to handle both synchronous clipboard
access errors and rejected writeText promises, setting copied only after a
successful write and preventing unhandled rejections. Track the reset timeout
and clear it when replaced or when the component unmounts, using the component’s
existing lifecycle hooks and imports.

In `@frontend/src/features/codebase/components/citation-badge.tsx`:
- Around line 27-30: Update the disclosure button in citation-badge’s button
element to include an aria-expanded state tied to the existing expanded value
managed by setExpanded. Keep the current toggle behavior unchanged, and ensure
the button reflects whether the details panel is open or closed for assistive
technologies.

In `@frontend/src/features/codebase/components/codebase-card.tsx`:
- Around line 75-88: Update formatDate to validate the Date created from dateStr
before calling toLocaleDateString, returning null when the timestamp is invalid;
retain the existing formatted output for valid dates and the null handling for
missing values.

In `@frontend/src/features/codebase/components/codebase-chat-container.tsx`:
- Around line 148-177: Update the messages scroll container wrapping
chatMessages and the streaming/error content to include role="log" and
aria-live="polite". Add an accessible name to the textarea near the input
controls using aria-label or an associated visually hidden label, while
preserving its existing placeholder and behavior.
- Around line 42-49: Update scrollToBottom and its useEffect so streaming
updates do not restart smooth scrolling: use auto behavior while isStreaming is
true, and skip scrolling when the chat container is not already near the bottom.
Preserve smooth scrolling for eligible non-streaming message updates and use the
existing refs to determine bottom proximity.
- Around line 91-96: Update handleKeyDown to skip preventDefault and handleSend
when e.nativeEvent.isComposing is true, while preserving the existing
Enter-without-Shift submission behavior outside IME composition.
- Around line 52-68: The transition handling in codebase-chat-container’s
useEffect is committing aborted streaming content after handleClearChat clears
the chat. Add a reset-specific flag in the clear/reset path, set it when abort()
is triggered by handleClearChat, and have the effect skip the
prevStreamingRef.current true-to-false commit when that flag is set. Keep the
existing append behavior for normal stream completion in the same effect and
continue updating prevStreamingRef.current as the source of truth for streaming
state.

In `@frontend/src/features/codebase/components/codebase-edit-dialog.tsx`:
- Around line 32-38: Update the reset useEffect in codebase-edit-dialog.tsx so
it depends on the codebase identity fields instead of the full codebase object.
Keep the existing reset behavior when isOpen is true, but key the effect off
codebase.id (and any other stable identity fields needed) so refetches from
useCodebases do not overwrite in-progress form edits; leave setName, setBranch,
and setFormError behavior unchanged.

In `@frontend/src/features/codebase/components/codebase-import-dialog.tsx`:
- Around line 81-85: Replace the hand-rolled overlays with the shared accessible
dialog or project UI dialog primitive across
frontend/src/features/codebase/components/codebase-import-dialog.tsx lines
81-85, frontend/src/features/codebase/components/codebase-delete-dialog.tsx
lines 22-26, and
frontend/src/features/codebase/components/codebase-edit-dialog.tsx lines 83-87.
Ensure each dialog supports dialog semantics, aria-modal, Escape-to-close, and
focus trapping; focus the Repository Name input in the import dialog, Cancel
button in the delete dialog, and Display Name input in the edit dialog. Remove
the import dialog’s no-op stopPropagation handler.

In `@frontend/src/features/codebase/hooks/use-codebase.ts`:
- Around line 88-100: Add a cleanup effect in useChatStream that calls the
existing abort() helper when the hook unmounts, so the active AbortController is
cleared and the POST stream is closed. Keep the current abortRef/current,
setIsStreaming, and stream callback behavior unchanged, and anchor the change
around useChatStream and abort().
- Around line 114-135: Update the onMessage state updater in the ChatService
streaming flow to treat every non-empty chunk as a delta and append it
unconditionally to the previous message. Remove the cumulative-content prefix
heuristics, while preserving the existing handling for an empty previous state
and empty chunks.

In `@frontend/src/routes/_app/codebases/`$codebaseId.chat.tsx:
- Around line 11-15: Update the useCodebases call and route rendering in the
chat page to use its isPending and isError states. Show a pending state while
the codebase query is unresolved, and show a not-found state after resolution or
failure when codebaseInfo is undefined; only render the header, branch content,
and CodebaseChatContainer once a valid codebase is found.
- Around line 11-15: Add a single-codebase GET endpoint in codebase.api.ts,
expose a matching useCodebase(id) hook, and update the chat route loader to
fetch the detail record by codebaseId instead of calling useCodebases and
searching the full list. Use the detail request result for the name and branch,
and propagate its not-found response so direct navigation produces a natural
404.

In `@frontend/src/routes/index.tsx`:
- Around line 162-179: Remove the hardcoded codebaseId from the Link in the
homepage card and stop rendering the fixed ID text in the index route card.
Update the Link in the index.tsx route so it no longer navigates directly to
/codebases/$codebaseId/chat with a constant params value; instead point the card
to the codebase list/select flow and let the user choose a codebase before
entering chat. Keep the existing card styling and surrounding layout unchanged.

In `@server/specs/codebase.md`:
- Around line 98-104: Repair the PATCH response table in the codebase API
documentation by removing the stray repository link, restoring the four-column
header separator, and placing the 200, 400, 403, and 404 response rows in
separate valid Markdown rows. Match the table structure and response contract
used in the corresponding frontend codebase specification.

In `@server/src/main/java/com/meet/server/feature/chat/ChatController.java`:
- Around line 26-32: Update streamChat to safely resolve and validate the caller
identity before invoking chatService.stream: handle null or anonymous
Authentication and malformed non-UUID principal names by rejecting with HTTP
401, while preserving the existing service call for valid UUID identities.

In `@server/src/main/java/com/meet/server/feature/chat/ChatService.java`:
- Around line 48-49: Remove `@Transactional` from stream and extract the
codebaseRepository.findById lookup plus ownership validation into a separate
read-only transactional method. Update stream to call that method before
building the Flux, while preserving the existing validation behavior and
downstream flatMap, concatWith, and onErrorResume flow.
- Around line 88-92: Keep the existing SLF4J logging call and its trailing
Throwable unchanged, but update the onErrorResume fallback in ChatService to
include a stable error code in the SSE error payload alongside the message.
Ensure the code consistently identifies the chat-stream failure so clients can
use it when deciding whether to offer a retry.

In `@server/src/main/java/com/meet/server/feature/chat/dto/CodeChatRequest.java`:
- Line 1: Update the message field in CodeChatRequest, which currently uses
`@NotBlank`, to add a `@Size`(max = ...) constraint matching the model context
budget. Leave chatId unchanged, and ensure validation rejects oversized messages
before ChatService.stream invokes embeddingModel.embed.

In
`@server/src/main/java/com/meet/server/feature/codebase/CodebaseController.java`:
- Around line 38-72: Extract the repeated
UUID.fromString(authentication.getName()) logic into a private helper in
CodebaseController, such as a principal-to-user-ID method, and use it in
importCodebase, list, update, delete, and reindex. Preserve the existing handler
behavior while centralizing the principal-name contract and ensuring invalid
UUID input is handled consistently rather than surfacing independently from each
handler.

In `@server/src/main/java/com/meet/server/feature/codebase/CodebaseService.java`:
- Around line 216-223: Update CodebaseRepository with a pessimistic-write finder
for the codebase ID, and change CodebaseService.ownedCodebase to use it so
ownership checks and status transitions are serialized. In process,
conditionally transition to PROCESSING only when the codebase is still eligible;
otherwise stop without writing chunks when it was reset or removed.
- Around line 101-108: Update CodebaseService.updateCodebase so a branch change
does not leave the codebase falsely marked as INDEXED: detect when
request.branch() differs from the current branch on the owned Codebase, and then
reuse the same reindexing path as reindexCodebase instead of only saving the
name and branch. Keep the existing save-and-return flow for name-only updates,
but when the branch changes make the indexing state explicit by queueing a
reindex (or, if you choose the alternative, reset the indexing metadata
consistently so stale chunks and status cannot be reported as current).

In
`@server/src/main/java/com/meet/server/feature/codebase/dto/CodebaseUpdateRequest.java`:
- Around line 5-8: Update CodebaseUpdateRequest and its updateCodebaseApi flow
to support partial PATCH payloads: make name and branch optional, apply only
non-null values in the update handler, and preserve existing values for omitted
fields; if the client is confirmed to always send both, retain required
validation instead. Add appropriate `@Size` bounds to both fields consistent with
the entity/database limits.

In `@server/src/main/java/com/meet/server/feature/codebase/GitService.java`:
- Around line 232-258: Update deletePath to retry only the explicitly transient
deletion failure, while immediately propagating non-transient IOException types
such as DirectoryNotEmptyException. Change deleteRepository to continue deleting
all paths, collect failures from deletePath, and throw one aggregate
CodebaseException after traversal completes. Align the InterruptedException
handling in deletePath with CodebaseService.process so the import worker does
not continue with a stale interrupt flag; preserve interruption status only if
the caller handles it appropriately.

In `@server/src/main/resources/application.yaml`:
- Line 42: Replace the invalid user:emails OAuth scope entry in the application
configuration with the valid user:email scope, preserving the surrounding scope
configuration.
🪄 Autofix

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: ASSERTIVE

Plan: Pro Plus

Run ID: b8fa4d88-5682-4bba-9c02-39d82e254bcd

📥 Commits

Reviewing files that changed from the base of the PR and between f2fb874 and c8f287f.

⛔ Files ignored due to path filters (1)
  • frontend/bun.lock is excluded by !**/*.lock
📒 Files selected for processing (67)
  • frontend/.cta.json
  • frontend/README.md
  • frontend/components.json
  • frontend/eslint.config.js
  • frontend/package.json
  • frontend/prettier.config.js
  • frontend/specs/auth.md
  • frontend/specs/codebase.md
  • frontend/src/components/ui/button.tsx
  • frontend/src/components/ui/input.tsx
  • frontend/src/components/ui/label.tsx
  • frontend/src/components/ui/select.tsx
  • frontend/src/components/ui/slider.tsx
  • frontend/src/components/ui/switch.tsx
  • frontend/src/components/ui/textarea.tsx
  • frontend/src/features/auth/api/auth.api.ts
  • frontend/src/features/auth/hooks/use-auth.ts
  • frontend/src/features/auth/schemas/auth.schema.ts
  • frontend/src/features/auth/types/auth.types.ts
  • frontend/src/features/codebase/api/codebase.api.ts
  • frontend/src/features/codebase/components/chat-message-item.tsx
  • frontend/src/features/codebase/components/citation-badge.tsx
  • frontend/src/features/codebase/components/codebase-card.tsx
  • frontend/src/features/codebase/components/codebase-chat-container.tsx
  • frontend/src/features/codebase/components/codebase-delete-dialog.tsx
  • frontend/src/features/codebase/components/codebase-edit-dialog.tsx
  • frontend/src/features/codebase/components/codebase-import-dialog.tsx
  • frontend/src/features/codebase/hooks/use-codebase.ts
  • frontend/src/features/codebase/index.ts
  • frontend/src/features/codebase/schemas/codebase.schema.ts
  • frontend/src/features/codebase/types/codebase.types.ts
  • frontend/src/lib/api-client.ts
  • frontend/src/lib/utils.ts
  • frontend/src/routeTree.gen.ts
  • frontend/src/router.tsx
  • frontend/src/routes/__root.tsx
  • frontend/src/routes/_app/codebases.tsx
  • frontend/src/routes/_app/codebases/$codebaseId.chat.tsx
  • frontend/src/routes/_app/codebases/index.tsx
  • frontend/src/routes/_app/route.tsx
  • frontend/src/routes/_auth/login.tsx
  • frontend/src/routes/_auth/register.tsx
  • frontend/src/routes/_auth/route.tsx
  • frontend/src/routes/index.tsx
  • frontend/src/routes/oauth2/callback.tsx
  • frontend/src/styles.css
  • frontend/src/types/api.types.ts
  • frontend/tsconfig.json
  • server/build.gradle
  • server/specs/codebase.md
  • server/src/main/java/com/meet/server/common/ratelimit/config/RateLimitConfig.java
  • server/src/main/java/com/meet/server/feature/chat/ChatController.java
  • server/src/main/java/com/meet/server/feature/chat/ChatService.java
  • server/src/main/java/com/meet/server/feature/chat/dto/CodeChatRequest.java
  • server/src/main/java/com/meet/server/feature/chat/dto/CodeCitation.java
  • server/src/main/java/com/meet/server/feature/codebase/CodebaseController.java
  • server/src/main/java/com/meet/server/feature/codebase/CodebaseRepository.java
  • server/src/main/java/com/meet/server/feature/codebase/CodebaseService.java
  • server/src/main/java/com/meet/server/feature/codebase/GitService.java
  • server/src/main/java/com/meet/server/feature/codebase/dto/CodebaseResponse.java
  • server/src/main/java/com/meet/server/feature/codebase/dto/CodebaseUpdateRequest.java
  • server/src/main/java/com/meet/server/feature/codebase/mapper/CodebaseMapper.java
  • server/src/main/java/com/meet/server/feature/repositoryfile/RepositoryFileRepository.java
  • server/src/main/java/com/meet/server/feature/retriver/CodeRetriever.java
  • server/src/main/java/com/meet/server/feature/user/UserRepository.java
  • server/src/main/java/com/meet/server/feature/user/UserService.java
  • server/src/main/resources/application.yaml
💤 Files with no reviewable changes (2)
  • frontend/src/routes/__root.tsx
  • frontend/src/types/api.types.ts
📜 Review details
🧰 Additional context used
🪛 LanguageTool
frontend/specs/auth.md

[uncategorized] ~163-~163: The official name of this software platform is spelled with a capital “H”.
Context: ...s | Configured registrations: google, github. | - Request body: None. - Response: ...

(GITHUB)

🪛 PMD (7.26.0)
server/src/main/java/com/meet/server/feature/chat/ChatService.java

[Low] 89-90: InvalidLogMessageFormat (Error Prone): Too many arguments, expected 3 arguments but found 4

(InvalidLogMessageFormat (Error Prone))

🔇 Additional comments (62)
frontend/specs/codebase.md (3)

1-30: LGTM!

Also applies to: 38-48, 61-74, 94-103, 104-144, 146-172, 187-243, 245-262


173-186: 🗄️ Data Integrity & Integration

Verify and document exact SSE payloads.

The event names and data encodings must match both the server emitter and frontend parser.

  • frontend/specs/codebase.md#L173-L186: document exact payload examples for message, citations, done, and error.
  • server/specs/codebase.md#L175-L188: verify the emitted event names and serialization format.

31-37: 🔒 Security & Privacy

SSRF (CWE-918): Server-Side Request Forgery (SSRF)

Reachability: External

Repository import needs destination authorization, not only URL syntax.

cloneUrl flows into server-side cloning. HTTPS, host, and no-user-info checks do not establish destination authorization.

  • frontend/specs/codebase.md#L31-L37: document the required private-address, DNS-rebinding, and redirect controls.
  • server/specs/codebase.md#L31-L37: document the same controls and verify GitService enforces them.
server/specs/codebase.md (1)

1-30: LGTM!

Also applies to: 38-48, 61-75, 95-97, 106-146, 148-174, 189-265

server/build.gradle (1)

38-38: 🎯 Functional Correctness

Verify test provider configuration.

This change selects the OpenAI model starter. The test dependencies still include Testcontainers Ollama. Verify that test profiles and model tests no longer require spring.ai.ollama or an OllamaContainer.

#!/bin/bash
set -euo pipefail

rg -n -C 4 \
  'OllamaContainer|spring\.ai\.ollama|testcontainers-ollama|spring\.ai\.openai|AZURE_OPENAI' \
  server
server/src/main/resources/application.yaml (1)

5-24: LGTM!

Also applies to: 43-45

server/src/main/java/com/meet/server/common/ratelimit/config/RateLimitConfig.java (1)

24-37: LGTM!

frontend/package.json (1)

18-18: LGTM!

Also applies to: 66-66

frontend/src/features/codebase/schemas/codebase.schema.ts (1)

3-55: LGTM!

frontend/src/features/codebase/types/codebase.types.ts (1)

22-26: 🗄️ Data Integrity & Integration

Verify the SSE event names against the backend emitter.

ChatStreamEvent hard-codes the event names message, citations, done, and error, and the payload shape of each one. The backend emitter is not part of this file set. If a name or payload differs, the consumer silently drops those events.

#!/bin/bash
# Description: Compare the frontend SSE event contract with the backend emitter.
set -euo pipefail

# Locate the chat streaming backend sources.
fd -t f 'ChatService.java|ChatController.java' server

# Inspect emitted SSE event names and payloads.
fd -t f 'ChatService.java|ChatController.java' server --exec rg -n -C4 'event\(|SseEmitter|ServerSentEvent|"message"|"citations"|"done"|"error"'

# Inspect the frontend consumer that parses these events.
fd -t f 'use-codebase.ts|codebase.api.ts' frontend --exec rg -n -C4 "'message'|'citations'|'done'|'error'|onmessage|EventSource|fetchEventSource"
frontend/src/features/codebase/components/chat-message-item.tsx (1)

18-85: LGTM!

Also applies to: 87-154, 193-215

frontend/src/features/codebase/components/citation-badge.tsx (1)

12-23: LGTM!

server/src/main/java/com/meet/server/feature/codebase/mapper/CodebaseMapper.java (1)

10-22: LGTM!

server/src/main/java/com/meet/server/feature/codebase/CodebaseRepository.java (2)

18-28: 🗄️ Data Integrity & Integration

Verify the JPQL entity join and the entity/field names.

The query joins an unrelated entity with an explicit on clause and mixes a constructor expression with count(f.id). Confirm two points:

  • The declared Hibernate version accepts left join RepositoryFile f on f.codebase.id = c.id.
  • RepositoryFile exposes a codebase association and CodebaseResponse declares a constructor whose parameter types and order match the projected columns, with a numeric type that accepts Long.

A mismatch in either point fails at context startup, not at request time.

#!/bin/bash
# Description: Verify the projection contract and the declared JPA/Hibernate versions.
set -euo pipefail

# Confirm the DTO constructor signature used by the constructor expression.
fd -t f 'CodebaseResponse.java' server --exec cat -n

# Confirm the RepositoryFile entity name and its codebase association.
fd -t f 'RepositoryFile.java' server --exec rg -n -C3 '`@Entity`|`@Table`|codebase|class RepositoryFile'

# Confirm declared Spring Boot / Hibernate versions.
fd -t f 'build.gradle|build.gradle.kts|gradle.properties' server --exec rg -n 'springframework.boot|hibernate|spring-boot-starter-data-jpa|version'

16-16: LGTM!

server/src/main/java/com/meet/server/feature/repositoryfile/RepositoryFileRepository.java (1)

15-20: LGTM!

server/src/main/java/com/meet/server/feature/user/UserRepository.java (1)

21-23: LGTM!

server/src/main/java/com/meet/server/feature/user/UserService.java (1)

24-28: LGTM!

server/src/main/java/com/meet/server/feature/codebase/CodebaseController.java (1)

59-63: 🗄️ Data Integrity & Integration

Verify that the frontend client accepts a bodyless 204 from DELETE.

Every other endpoint returns the ApiResponse<> envelope. delete returns 204 No Content with no body. If the shared client always parses JSON or always reads data from the envelope, the delete call fails after a successful server-side deletion.

#!/bin/bash
# Description: Check how the frontend API client handles responses without a body.
set -euo pipefail

# Inspect the shared client response handling.
fd -t f 'api-client.ts' frontend --exec cat -n

# Inspect the codebase delete call site.
fd -t f 'codebase.api.ts' frontend --exec rg -n -C6 'delete|DELETE|204'
server/src/main/java/com/meet/server/feature/codebase/CodebaseService.java (1)

47-67: LGTM!

Also applies to: 156-163, 184-191

server/src/main/java/com/meet/server/feature/chat/dto/CodeCitation.java (1)

1-1: 🗄️ Data Integrity & Integration

Confirm that no reference to the old package remains.

The record moved from com.meet.server.feature.codebase.dto to com.meet.server.feature.chat.dto. The JSON field set is unchanged, so the frontend codeCitationSchema stays compatible. Any remaining import of the old fully qualified name fails the build.

#!/bin/bash
# Description: Find stale references to the previous CodeCitation package.
set -euo pipefail

# Search for the old fully qualified name and for any remaining declaration.
rg -n 'com\.meet\.server\.feature\.codebase\.dto\.CodeCitation' server || echo "no stale FQN references"

# List every file that uses the type, with its import line.
rg -n -C2 '\bCodeCitation\b' server --type=java
frontend/src/features/codebase/hooks/use-codebase.ts (1)

21-86: LGTM!

frontend/src/features/codebase/index.ts (1)

1-2: 📐 Maintainability & Code Quality

Verify that the schema and type modules do not export the same names.

export * from both ./schemas/codebase.schema and ./types/codebase.types fails to compile if the two modules export any identical name. Zod feature modules commonly define a schema and an inferred type with related names, so confirm the exported names are distinct.

#!/bin/bash
# Description: List exported names from the codebase schema and type modules to detect collisions.
fd -t f 'codebase.(schema|types).ts$' frontend/src --exec ast-grep outline {} --items all
server/src/main/java/com/meet/server/feature/codebase/dto/CodebaseResponse.java (1)

8-19: 🔒 Security & Privacy

Sensitive Data Exposure (CWE-522): Insufficiently Protected Credentials

Reachability: External

Reachability path
● Entry
  frontend/src/routes/_app/codebases/index.tsx:27
  Route
│
▼
● Hop
  frontend/src/routes/_app/codebases/$codebaseId.chat.tsx:5
  Route
│
▼
● Hop
  frontend/src/features/codebase/hooks/use-codebase.ts:29
  CodebaseResponse
│
▼
● Sink
  server/src/main/java/com/meet/server/feature/codebase/dto/CodebaseResponse.java

Confirm that cloneUrl cannot carry embedded credentials back to the client.

A Git clone URL can embed credentials, for example https://user:token@host/org/repo.git. If the import flow persists the submitted URL unchanged, this response returns the token to the client, and the token then reaches browser caches and any response log. Confirm that the import path rejects or strips user-info from the URL, or that the mapper redacts it before it enters this record.

#!/bin/bash
# Description: Inspect the codebase mapper, entity, and import validation for clone URL handling.
fd -t f 'CodebaseMapper.java|Codebase.java|CodebaseService.java|CodebaseImportRequest.java' server/src --exec cat -n {}
# Look for any user-info stripping or URL validation.
rg -n -C3 'getUserInfo|userInfo|cloneUrl' server/src --type=java
server/src/main/java/com/meet/server/feature/retriver/CodeRetriever.java (1)

3-3: LGTM!

server/src/main/java/com/meet/server/feature/chat/ChatService.java (1)

55-57: 🔒 Security & Privacy | ⚡ Quick win

Denial of Service (CWE-770): Allocation of Resources Without Limits or Throttling

Reachability: External

Reachability path
● Entry
  server/src/main/java/com/meet/server/feature/chat/ChatController.java:25
  streamChat
│
▼
● Sink
  server/src/main/java/com/meet/server/feature/chat/ChatService.java

Bound chatId before you use it as the chat-memory key.

chatId comes from the request body. CodeChatRequest declares chatId without any constraint, so the value has no length or charset bound. Line 56 concatenates it into conversationId, which MessageChatMemoryAdvisor uses as the partition key. MessageWindowChatMemory retains up to 20 messages for each distinct key.

One authenticated codebase owner can therefore allocate an unbounded number of conversations by varying chatId on each request. Each new key retains its own message window. Add a @Size constraint and a charset pattern on CodeChatRequest.chatId, and confirm that the configured ChatMemoryRepository evicts idle conversations.

Cross-tenant key forgery is not possible here, because the userId prefix is server-derived and a UUID cannot contain :.

#!/bin/bash
# Description: Inspect the CodeChatRequest constraints and the configured chat memory repository.
fd -t f 'CodeChatRequest.java' server/src --exec cat -n {}
rg -n -C5 'ChatMemoryRepository|MessageWindowChatMemory|chat-memory|chatMemory' server/src --type=java
rg -n -C5 'chat' server/src/main/resources/application.yaml
frontend/src/features/codebase/components/codebase-chat-container.tsx (1)

154-166: 🔒 Security & Privacy

XSS (CWE-79): Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')

Reachability: External

Reachability path
● Entry
  frontend/src/routes/_app/codebases/index.tsx:27
  Route
│
▼
● Hop
  frontend/src/routes/_app/codebases/$codebaseId.chat.tsx:5
  Route
│
▼
● Sink
  frontend/src/features/codebase/components/codebase-chat-container.tsx

Verify that ChatMessageItem escapes the streamed model output.

This container passes raw model output to ChatMessageItem as message.content. Every interpolation in this file is a JSX text child, so React escapes it here. ChatMessageItem is not part of this review context. If it renders the content as Markdown or HTML, confirm that it does not use dangerouslySetInnerHTML on unsanitized output and that any rendered link cannot carry a javascript: URL.

#!/bin/bash
# Description: Inspect the chat message renderer for unsafe HTML or URL sinks.
fd -t f 'chat-message-item.tsx|citation-badge.tsx' frontend/src --exec cat -n {}
rg -n -C4 'dangerouslySetInnerHTML|rehype-raw|allowDangerous|innerHTML' frontend/src
server/src/main/java/com/meet/server/feature/chat/ChatController.java (1)

31-31: 🔒 Security & Privacy | ⚡ Quick win

IDOR (CWE-639): Authorization Bypass Through User-Controlled Key (IDOR)

Reachability: External

Verify that ChatService.stream enforces codebase ownership.

The controller passes codebaseId directly from the path. Any authenticated user can submit an arbitrary codebase id. The ownership check must exist in ChatService.stream; that implementation is not in the provided context. If the check is missing, a user can stream code content from another user's codebase.

#!/bin/bash
# Inspect ChatService.stream and the ownership lookup it performs.
fd -g 'ChatService.java' --exec ast-grep outline {} --items all
fd -g 'ChatService.java' --exec rg -n -C6 'stream|findBy|userId|owner|AccessDenied|NotFound' {} +
frontend/src/routes/_app/codebases/index.tsx (1)

33-124: LGTM!

Also applies to: 254-357

frontend/src/features/codebase/components/codebase-import-dialog.tsx (1)

48-51: 🔒 Security & Privacy | ⚡ Quick win

SSRF (CWE-918): Server-Side Request Forgery (SSRF)

Reachability: External

Reachability path
● Entry
  frontend/src/routeTree.gen.ts
│
▼
● Hop
  frontend/src/routes/_auth/login.tsx:82
  handleSubmit
│
▼
● Hop
  frontend/src/features/codebase/components/codebase-edit-dialog.tsx:123
  handleSubmit
│
▼
● Sink
  frontend/src/features/codebase/components/codebase-import-dialog.tsx

Verify server-side clone URL validation.

This regex only requires an https:// prefix in the browser. The server clones the submitted URL, so it must validate the scheme and restrict the host. Confirm that the backend rejects internal hosts, credentials in the URL, and non-git endpoints.

#!/bin/bash
# Check backend validation of cloneUrl before the clone operation.
fd -g 'GitService.java' -g 'CodebaseService.java' -g 'CodebaseImportRequest.java' --exec rg -n -C6 'cloneUrl|https|URI|InetAddress|Pattern|`@Pattern`|allow' {} +
frontend/src/routes/_app/codebases.tsx (1)

3-9: LGTM!

frontend/src/routeTree.gen.ts (1)

13-20: LGTM!

Also applies to: 55-65, 202-226

frontend/src/router.tsx (1)

5-5: LGTM!

frontend/src/routes/index.tsx (1)

4-10: LGTM!

Also applies to: 48-60, 74-76, 118-120, 144-160, 196-198

frontend/src/routes/_app/route.tsx (1)

38-43: 📐 Maintainability & Code Quality

Inspect redirect detection before changing this guard.

The available evidence only covers TanStack documentation; the repository source and lockfile should be checked to determine whether 'to' in error is an unnecessary shape check in this route.

frontend/src/features/codebase/components/codebase-card.tsx (2)

214-218: 🎯 Functional Correctness

Keep the Tailwind important modifier where it already works.

!text-white is valid Tailwind CSS syntax and generates the important-color utility; changing it to postfix syntax is optional cleanup, not a required fix.

			> Likely an incorrect or invalid review comment.

211-223: 🎯 Functional Correctness

No change needed for disabled link behavior.

TanStack Router Link supports disabled; when it is true, navigation/preload is prevented and the rendered anchor does not get an href, so keyboard activation is not enabled by pointer-events-none alone.

frontend/.cta.json (1)

25-25: LGTM!

frontend/README.md (1)

35-35: LGTM!

Also applies to: 43-43, 56-56, 64-64, 73-77, 95-95, 163-167, 220-220

frontend/components.json (1)

21-21: LGTM!

frontend/eslint.config.js (1)

18-18: LGTM!

frontend/src/features/auth/api/auth.api.ts (1)

11-18: LGTM!

Also applies to: 20-28, 30-32

frontend/src/features/auth/schemas/auth.schema.ts (1)

20-31: LGTM!

frontend/src/features/auth/types/auth.types.ts (1)

1-2: LGTM!

frontend/src/lib/api-client.ts (1)

58-58: LGTM!

frontend/prettier.config.js (1)

7-10: LGTM!

frontend/specs/auth.md (1)

18-22: LGTM!

Also applies to: 38-63, 73-112, 123-137, 148-163, 197-211, 222-243

frontend/src/features/auth/hooks/use-auth.ts (1)

4-7: LGTM!

Also applies to: 30-30, 58-58, 83-83, 108-108

frontend/src/routes/_auth/register.tsx (1)

3-13: LGTM!

Also applies to: 51-52, 77-78, 91-95, 104-105, 115-116, 205-209, 218-220, 245-247, 286-299, 342-346, 358-373, 391-391

frontend/src/routes/_auth/route.tsx (1)

36-38: LGTM!

Also applies to: 47-53, 62-103, 114-144, 154-162, 171-175

frontend/src/routes/oauth2/callback.tsx (1)

42-48: LGTM!

frontend/src/styles.css (1)

225-235: 🎯 Functional Correctness

Verify the cascade behavior of the new @layer base wrapper.

These selectors were previously unlayered. The wrapper changes their cascade precedence, so Tailwind utility classes can now override a and a:hover. Confirm the intended colors and hover behavior for links on the authentication pages before merging.

frontend/tsconfig.json (1)

2-9: LGTM!

frontend/src/components/ui/button.tsx (1)

1-52: LGTM!

frontend/src/components/ui/input.tsx (1)

1-14: LGTM!

frontend/src/components/ui/label.tsx (1)

1-6: LGTM!

Also applies to: 16-17

frontend/src/components/ui/select.tsx (1)

1-5: LGTM!

Also applies to: 27-39, 54-66, 75-77, 95-95, 111-111, 135-135, 149-150, 167-168

frontend/src/components/ui/slider.tsx (1)

1-6: LGTM!

Also applies to: 23-23, 34-48

frontend/src/components/ui/switch.tsx (1)

1-26: LGTM!

frontend/src/components/ui/textarea.tsx (1)

1-11: LGTM!

frontend/src/lib/utils.ts (1)

11-11: LGTM!

Also applies to: 54-54

frontend/src/routes/_auth/login.tsx (1)

29-30: LGTM!

Also applies to: 43-47, 76-77, 134-138, 181-185, 197-212, 232-239

Comment on lines +49 to +60
- Responses:

| Status | Body | Example |
|---|---|---|
| `202 Accepted` | `ApiResponse<CodebaseImportResponse>` | `{"success":true,"message":"Codebase import queued","data":{"codebaseId":"3fa85f64-5717-4562-b3fc-2c963f66afa6","status":"QUEUED","fileCount":0}}` |
| `400 Bad Request` | Validation or clone URL error envelope | `{"success":false,"message":"Validation failed","data":{"cloneUrl":"cloneUrl must use HTTPS"}}` |
| `409 Conflict` | `ApiResponse<Void>` | `{"success":false,"message":"A user can have at most 5 codebases","data":null}` |

- Errors:
- `CODEBASE_LIMIT_REACHED` (`409`) when the user already has five persisted codebases. All statuses count.
- `INVALID_CLONE_URL` (`400`) when service-level URI validation fails.
- Validation failures (`400`) for invalid request fields.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Document separate 400 response shapes.

Validation failures use field-level data, while INVALID_CLONE_URL is a domain error with data: null.

  • frontend/specs/codebase.md#L49-L60: add distinct examples for validation and service-level clone URL failures.
  • server/specs/codebase.md#L49-L60: add the same distinct examples.
📍 Affects 2 files
  • frontend/specs/codebase.md#L49-L60 (this comment)
  • server/specs/codebase.md#L49-L60
🤖 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/specs/codebase.md` around lines 49 - 60, Update the Responses and
Errors sections in frontend/specs/codebase.md lines 49-60 and
server/specs/codebase.md lines 49-60 to document separate 400 response examples:
retain field-level data for validation failures and show INVALID_CLONE_URL as a
domain error with data: null. Apply the same distinct examples and descriptions
in both files.

Comment on lines +75 to +93
### PATCH /api/codebases/{codebaseId}

- Purpose: Update codebase metadata without reindexing.
- Authentication/authorization: Authenticated user required; the codebase must belong to the authenticated user.
- Request headers/path/query parameters:

| Name | Type | Required | Constraints | Description |
|---|---|---:|---|---|
| `Authorization` | string | Yes | Authentication is enforced by security configuration. | Authenticated principal. |
| `codebaseId` | UUID | Yes | Valid UUID path value. | Target codebase. |

- Request body: `CodebaseUpdateRequest`

| Field | Type | Required | Nullability | Constraints | Description |
|---|---|---:|---|---|---|
| `name` | string | Yes | Non-null | `@NotBlank` | New display name. |
| `branch` | string | Yes | Non-null | `@NotBlank` | New branch name. |

`cloneUrl` cannot be updated by this endpoint.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Branch changes must invalidate or rebuild the index.

A metadata-only branch update can report the new branch while indexed files and chat context still represent the old branch.

  • frontend/specs/codebase.md#L75-L93: reject branch changes, queue reindexing, or mark the codebase stale.
  • server/specs/codebase.md#L76-L94: apply the same contract and prevent chat from using stale indexed data.
📍 Affects 2 files
  • frontend/specs/codebase.md#L75-L93 (this comment)
  • server/specs/codebase.md#L76-L94
🤖 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/specs/codebase.md` around lines 75 - 93, Update the PATCH codebase
contract in frontend/specs/codebase.md lines 75-93 and server/specs/codebase.md
lines 76-94 so changing branch cannot leave indexed data silently stale:
explicitly reject branch changes, queue a reindex, or mark the codebase stale
and prevent chat from using that index until refreshed. Keep both specifications
consistent.

Comment on lines +67 to +93
/**
* Opens a POST-based SSE connection to the chat stream endpoint.
* Proactively checks/refreshes the access token if needed before initiating the stream.
* Returns an AbortController so the caller can cancel the stream.
*/
export function streamChatApi(
codebaseId: string,
data: CodeChatRequest,
callbacks: StreamChatCallbacks,
): AbortController {
const ctrl = new AbortController()

const startStream = async () => {
let token = tokenManager.getAccessToken()

// If no access token in memory, attempt refresh using HttpOnly cookie first
if (!token) {
try {
const refreshRes = await refreshApi()
if (refreshRes.success && refreshRes.data?.accessToken) {
tokenManager.setAccessToken(refreshRes.data.accessToken)
token = refreshRes.data.accessToken
}
} catch {
// Continue to attempt stream request
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Refresh the token when it is present but expired.

The refresh branch runs only when tokenManager.getAccessToken() returns no token. If a token exists and has expired, the code sends it unchanged. The server then rejects the stream with 401, onopen throws, and the user sees a stream error instead of an automatic recovery. The apiClient interceptors handle this case for the other functions in this file, so the SSE path behaves differently from every other request.

The doc comment on line 69 states that the function checks and refreshes the token "if needed". Update the behavior or the comment so they agree.

One option is to retry once on a 401 response: refresh the token in onopen, then restart startStream with the new token.

🤖 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/features/codebase/api/codebase.api.ts` around lines 67 - 93,
Update streamChatApi and its startStream flow to refresh an expired access
token, not only a missing one, and retry the SSE connection once after a 401
response using the refreshed token. Preserve the existing AbortController and
callback behavior, and ensure the implementation cannot retry indefinitely; keep
the doc comment accurate with the resulting refresh behavior.

Comment on lines +121 to +145
onmessage: (event) => {
switch (event.event) {
case 'message': {
let chunkText = event.data
if (typeof event.data === 'string') {
try {
const parsed = JSON.parse(event.data)
if (typeof parsed === 'string') {
chunkText = parsed
} else if (parsed && typeof parsed === 'object' && parsed !== null) {
if ('content' in parsed && typeof parsed.content === 'string') {
chunkText = parsed.content
} else if ('message' in parsed && typeof parsed.message === 'string') {
chunkText = parsed.message
} else if ('text' in parsed && typeof parsed.text === 'string') {
chunkText = parsed.text
}
}
} catch {
// Keep raw event.data
}
}
callbacks.onMessage(chunkText)
break
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Confirm the shape of the message payload with the backend.

ChatService emits sse("message", text) where text is a String. writeValueAsString produces a JSON string literal, so JSON.parse returns a plain string and line 129 assigns it. The content, message, and text object branches on lines 131-137 have no matching producer in ChatService. Remove the unused branches, or keep them only if another producer emits object payloads on this event.

🤖 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/features/codebase/api/codebase.api.ts` around lines 121 - 145,
In the message handler’s JSON parsing within the onmessage callback, remove the
content, message, and text object-payload branches because ChatService emits
serialized string values for this event; retain parsing that unwraps JSON string
literals and the raw-data fallback.

Comment on lines +154 to +156
case 'done':
callbacks.onDone(event.data)
break

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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

Decode the done payload before you call onDone.

ChatService.sse serializes every payload with jsonMapper.writeValueAsString(data). For the done event the data is the chatId string, so the wire value is a JSON string literal such as "default" including the quote characters. Line 155 forwards event.data unchanged, so useChatStream stores chatId as "default" with quotes. Any follow-up request that reuses this value sends the wrong conversation identifier.

Note that the message, citations, and error branches already parse the payload, so only this branch is affected.

🐛 Proposed fix to parse the JSON-encoded chat id
-              case 'done':
-                callbacks.onDone(event.data)
-                break
+              case 'done': {
+                let resolvedChatId = event.data
+                try {
+                  const parsed = JSON.parse(event.data)
+                  if (typeof parsed === 'string') {
+                    resolvedChatId = parsed
+                  }
+                } catch {
+                  // Keep raw event.data
+                }
+                callbacks.onDone(resolvedChatId)
+                break
+              }
📝 Committable suggestion

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

Suggested change
case 'done':
callbacks.onDone(event.data)
break
case 'done': {
let resolvedChatId = event.data
try {
const parsed = JSON.parse(event.data)
if (typeof parsed === 'string') {
resolvedChatId = parsed
}
} catch {
// Keep raw event.data
}
callbacks.onDone(resolvedChatId)
break
}
🤖 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/features/codebase/api/codebase.api.ts` around lines 154 - 156,
Update the done branch in the SSE event handling switch to JSON-decode
event.data before passing it to callbacks.onDone, matching the parsing behavior
of the message, citations, and error branches. Ensure onDone receives the raw
chat ID without surrounding JSON string quotes.

Comment on lines +101 to +108
@Transactional
public CodebaseResponse updateCodebase(UUID userId, UUID codebaseId, CodebaseUpdateRequest request) {
var codebase = ownedCodebase(userId, codebaseId);
codebase.setName(request.name().trim());
codebase.setBranch(request.branch().trim());
codebaseRepository.save(codebase);
return codebaseMapper.toCodebaseResponse(codebase, repositoryFileRepository.countByCodebaseId(codebaseId));
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

A branch change leaves the index stale while the status stays INDEXED.

updateCodebase writes the new branch but keeps status, lastCommitSha, indexedAt, and every indexed chunk and file. After a caller changes the branch, the codebase reports INDEXED while the stored chunks still belong to the previous branch. Code chat then answers from content that does not exist on the selected branch, and nothing signals the mismatch to the caller.

Choose one behavior and make it explicit:

  • Queue a reindex when branch changes, in the same way reindexCodebase does.
  • Or reset the indexing metadata so the codebase no longer reports INDEXED.
🛠️ Proposed fix: queue a reindex when the branch changes
     `@Transactional`
     public CodebaseResponse updateCodebase(UUID userId, UUID codebaseId, CodebaseUpdateRequest request) {
         var codebase = ownedCodebase(userId, codebaseId);
+        var newBranch = request.branch().trim();
+        var branchChanged = !newBranch.equals(codebase.getBranch());
+        if (branchChanged && (codebase.getStatus() == CodebaseStatus.QUEUED
+                || codebase.getStatus() == CodebaseStatus.PROCESSING)) {
+            throw new CodebaseException("CODEBASE_BUSY", "Codebase is currently being indexed", HttpStatus.CONFLICT);
+        }
         codebase.setName(request.name().trim());
-        codebase.setBranch(request.branch().trim());
+        codebase.setBranch(newBranch);
+        if (branchChanged) {
+            codeChunkRepository.deleteByCodebaseId(codebaseId);
+            repositoryFileRepository.deleteByCodebaseId(codebaseId);
+            codebase.setLastCommitSha(null);
+            codebase.setIndexedAt(null);
+            codebase.setStatus(CodebaseStatus.QUEUED);
+        }
         codebaseRepository.save(codebase);
+        if (branchChanged) {
+            TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
+                `@Override`
+                public void afterCommit() {
+                    processAsync(codebaseId);
+                }
+            });
+        }
         return codebaseMapper.toCodebaseResponse(codebase, repositoryFileRepository.countByCodebaseId(codebaseId));
     }
🤖 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 `@server/src/main/java/com/meet/server/feature/codebase/CodebaseService.java`
around lines 101 - 108, Update CodebaseService.updateCodebase so a branch change
does not leave the codebase falsely marked as INDEXED: detect when
request.branch() differs from the current branch on the owned Codebase, and then
reuse the same reindexing path as reindexCodebase instead of only saving the
name and branch. Keep the existing save-and-return flow for name-only updates,
but when the branch changes make the indexing state explicit by queueing a
reindex (or, if you choose the alternative, reset the indexing metadata
consistently so stale chunks and status cannot be reported as current).

Comment on lines +216 to 223
private Codebase ownedCodebase(UUID userId, UUID codebaseId) {
var codebase = codebaseRepository.findById(codebaseId).orElseThrow(() ->
new CodebaseException("CODEBASE_NOT_FOUND", "Codebase not found", HttpStatus.NOT_FOUND));
if (codebase.getUser() == null || !userId.equals(codebase.getUser().getId())) {
throw new CodebaseException("CODEBASE_FORBIDDEN", "You do not own this codebase", HttpStatus.FORBIDDEN);
}
String chatId = request.chatId() == null || request.chatId().isBlank() ? "default" : request.chatId().trim();
String conversationId = userId + ":" + codebaseId + ":" + chatId;
var citations = new AtomicReference<List<CodeCitation>>(List.of());

return chatClient.prompt().user(request.message().trim())
.advisors(advisors -> advisors
.param(ChatMemory.CONVERSATION_ID, conversationId)
.param(CodeAdvisor.CODEBASE_ID_CONTEXT, codebaseId))
.stream().chatClientResponse()
.map(response -> {
var responseCitations = response.context().get(CodeAdvisor.CITATIONS_CONTEXT);
if (responseCitations instanceof List<?> values) {
citations.set(values.stream()
.filter(CodeCitation.class::isInstance)
.map(CodeCitation.class::cast)
.toList());
}
var text = Objects.requireNonNull(Objects.requireNonNull(response.chatResponse()).getResult())
.getOutput().getText();
return sse("message", text);
})
.concatWith(Flux.defer(() -> Flux.just(
sse("citations", citations.get()),
sse("done", chatId))))
.onErrorResume(error -> Flux.just(sse("error", Map.of("message", "Unable to complete chat"))));
return codebase;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

ownedCodebase reads the status without a lock, so the busy guard is a TOCTOU check.

ownedCodebase loads the entity with findById and no lock. reindexCodebase and deleteCodebase then decide on codebase.getStatus(). The background indexer writes that same status from another thread through statusService. Two concurrent requests can both observe INDEXED and both pass the guard.

Concrete outcome: two concurrent reindexCodebase calls each delete the chunks and files, each set QUEUED, and each schedule processAsync. Two indexing tasks then write chunk rows for the same codebase at the same time, which produces a duplicated or interleaved index. A deleteCodebase racing a reindexCodebase removes the row while a task is already scheduled, and process fails with CODEBASE_NOT_FOUND.

This is the root cause for the guards at Line 113 and Line 208. Load the row with a pessimistic write lock so the status check and the status write are serialized.

🔒 Proposed fix

Add a locking finder to CodebaseRepository:

`@Lock`(LockModeType.PESSIMISTIC_WRITE)
`@Query`("select c from Codebase c where c.id = :codebaseId")
Optional<Codebase> findByIdForUpdate(`@Param`("codebaseId") UUID codebaseId);

Then use it for the ownership lookup:

     private Codebase ownedCodebase(UUID userId, UUID codebaseId) {
-        var codebase = codebaseRepository.findById(codebaseId).orElseThrow(() ->
+        var codebase = codebaseRepository.findByIdForUpdate(codebaseId).orElseThrow(() ->
                 new CodebaseException("CODEBASE_NOT_FOUND", "Codebase not found", HttpStatus.NOT_FOUND));
         if (codebase.getUser() == null || !userId.equals(codebase.getUser().getId())) {
             throw new CodebaseException("CODEBASE_FORBIDDEN", "You do not own this codebase", HttpStatus.FORBIDDEN);
         }
         return codebase;
     }

Also make process set PROCESSING conditionally, so a task whose codebase was reset or removed stops instead of writing chunks.

🤖 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 `@server/src/main/java/com/meet/server/feature/codebase/CodebaseService.java`
around lines 216 - 223, Update CodebaseRepository with a pessimistic-write
finder for the codebase ID, and change CodebaseService.ownedCodebase to use it
so ownership checks and status transitions are serialized. In process,
conditionally transition to PROCESSING only when the codebase is still eligible;
otherwise stop without writing chunks when it was reset or removed.

Comment on lines +5 to +8
public record CodebaseUpdateRequest(
@NotBlank String name,
@NotBlank String branch
) {}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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

Both fields are mandatory, so this record does not support a partial PATCH.

updateCodebaseApi in frontend/src/features/codebase/api/codebase.api.ts sends a PATCH request with this body. @NotBlank on both name and branch rejects a body that omits either field with a 400 response. If the edit dialog submits only the changed field, the request fails. Either make the fields optional and apply each non-null value, or confirm that the client always sends both fields.

Add length bounds as well. Neither field has a @Size constraint, so the caller can persist an arbitrarily long name or branch.

#!/bin/bash
# Description: Check whether the edit dialog submits both fields and inspect the update handler.
fd -t f 'codebase-edit-dialog.tsx' frontend/src --exec cat -n {}
rg -n -C6 'CodebaseUpdateRequest' server/src --type=java
🤖 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
`@server/src/main/java/com/meet/server/feature/codebase/dto/CodebaseUpdateRequest.java`
around lines 5 - 8, Update CodebaseUpdateRequest and its updateCodebaseApi flow
to support partial PATCH payloads: make name and branch optional, apply only
non-null values in the update handler, and preserve existing values for omitted
fields; if the client is confirmed to always send both, retain required
validation instead. Add appropriate `@Size` bounds to both fields consistent with
the entity/database limits.

Comment on lines 232 to 258
private void deletePath(Path path) {
try {
Files.deleteIfExists(path);
} catch (IOException e) {
throw new CodebaseException(
"CODEBASE_CLEANUP_FAILED",
"Unable to delete cloned repository path",
HttpStatus.INTERNAL_SERVER_ERROR,
e);
IOException lastFailure = null;
for (int attempt = 1; attempt <= CLEANUP_ATTEMPTS; attempt++) {
try {
Files.deleteIfExists(path);
return;
} catch (IOException exception) {
lastFailure = exception;
if (attempt == CLEANUP_ATTEMPTS) {
break;
}
try {
Thread.sleep(CLEANUP_RETRY_DELAY_MILLIS);
} catch (InterruptedException interruptedException) {
Thread.currentThread().interrupt();
lastFailure = new IOException("Repository cleanup interrupted", interruptedException);
break;
}
}
}

throw new CodebaseException(
"CODEBASE_CLEANUP_FAILED",
"Unable to delete cloned repository path",
HttpStatus.INTERNAL_SERVER_ERROR,
lastFailure);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Retry only the transient failure and let the remaining paths finish.

Two concerns in the new retry loop:

  1. Files.deleteIfExists throws DirectoryNotEmptyException when a child deletion failed earlier. That subtype is an IOException, so the loop retries it four times and sleeps 1 s in total. A retry cannot empty the directory, so the delay produces no benefit. Restrict the retry to the transient case and fail fast otherwise.
  2. deletePath throws on the final attempt. deleteRepository invokes it inside forEach, so the first hard failure aborts the walk and leaves every remaining file and directory on disk. Collect the failures and throw once after the walk completes, so a single locked file does not strand the whole workspace.

Note also that the interrupt path sets the thread interrupt flag and then throws. CodebaseService.process catches the exception and only logs a warning, so the worker thread continues to run with the interrupt flag set. Confirm that this is the intended behavior for the import worker.

♻️ Proposed fix to skip non-transient retries
     private void deletePath(Path path) {
         IOException lastFailure = null;
         for (int attempt = 1; attempt <= CLEANUP_ATTEMPTS; attempt++) {
             try {
                 Files.deleteIfExists(path);
                 return;
             } catch (IOException exception) {
                 lastFailure = exception;
-                if (attempt == CLEANUP_ATTEMPTS) {
+                if (attempt == CLEANUP_ATTEMPTS || exception instanceof DirectoryNotEmptyException) {
                     break;
                 }
🤖 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 `@server/src/main/java/com/meet/server/feature/codebase/GitService.java` around
lines 232 - 258, Update deletePath to retry only the explicitly transient
deletion failure, while immediately propagating non-transient IOException types
such as DirectoryNotEmptyException. Change deleteRepository to continue deleting
all paths, collect failures from deletePath, and throw one aggregate
CodebaseException after traversal completes. Align the InterruptedException
handling in deletePath with CodebaseService.process so the import worker does
not continue with a stale interrupt flag; preserve interruption status only if
the caller handles it appropriately.

level:
com.meet.server.feature.retriver.CodeRetriever: DEBUG

- user:emails

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== application.yaml context =="
if [ -f server/src/main/resources/application.yaml ]; then
  nl -ba server/src/main/resources/application.yaml | sed -n '30,50p'
else
  echo "application.yaml not found"
fi

echo
echo "== GitHub scope references in repo =="
rg -n "user:emails|user:email|scopes:" server/src/main/resources/application.yaml 2>/dev/null || true

echo
echo "== OAuth app config files =="
fd -a -i '(^application\.ya?ml$|oauth|github|client)' server/src/main/resources . | sed 's#^\./##' | head -100

Repository: Meet-08/CodeCompass

Length of output: 1927


🌐 Web query:

GitHub OAuth scopes user:email user:emails documentation

💡 Result:

GitHub provides the user:email scope to manage access to a user's email addresses [1][2]. Key details regarding this scope: - Purpose: The user:email scope grants read access to a user's private email addresses [1][3]. - Hierarchy: The user scope is a broader scope that includes user:email and user:follow access [1][4]. Because of this hierarchical relationship, if your application is granted the user scope, it effectively has access to the user's email addresses even if user:email is not explicitly listed on the token [3]. - Normalization: When requesting multiple scopes, GitHub normalizes them by discarding redundant scopes [1][4]. For example, if you request both user and user:email, the resulting token will only contain the user scope because it already encompasses the privileges of user:email [1][4]. - API usage: To list email addresses for an authenticated user, you must have the user:email scope [2]. However, more advanced operations, such as adding an email address to an account, require the broader user scope [2]. - Verification: Because users may modify requested scopes during the authorization flow, applications should verify which scopes were actually granted by checking the response or the X-OAuth-Scopes header [1][3]. When checking if you have email access, it is best practice to verify if the token possesses either the user:email scope OR the broader user scope [3].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== application.yaml context =="
if [ -f server/src/main/resources/application.yaml ]; then
  nl -ba server/src/main/resources/application.yaml | sed -n '30,50p'
else
  echo "application.yaml not found"
fi

echo
echo "== GitHub scope references in repo =="
rg -n "user:emails|user:email|scopes:" server/src/main/resources/application.yaml 2>/dev/null || true

echo
echo "== OAuth app config files =="
fd -a -i '(^application\.ya?ml$|oauth|github|client)' server/src/main/resources . | sed 's#^\./##' | head -100

Repository: Meet-08/CodeCompass

Length of output: 1927


Use the valid GitHub email scope.

GitHub documents the OAuth scope as user:email, not user:emails. Replace the user:emails entry so the authorization flow requests the intended email access.

Proposed fix
-              - user:emails
+              - user:email
📝 Committable suggestion

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

Suggested change
- user:emails
- user:email
🤖 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 `@server/src/main/resources/application.yaml` at line 42, Replace the invalid
user:emails OAuth scope entry in the application configuration with the valid
user:email scope, preserving the surrounding scope configuration.

@Meet-08
Meet-08 merged commit ac7ae76 into main Aug 5, 2026
2 checks passed
Sign up for free to 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