Skip to content

merge: 개발 코드 메인 브랜치로 머지 - #11

Merged
pearseona merged 35 commits into
mainfrom
develop
Jul 11, 2026
Merged

merge: 개발 코드 메인 브랜치로 머지#11
pearseona merged 35 commits into
mainfrom
develop

Conversation

@ljh8450

@ljh8450 ljh8450 commented Jul 11, 2026

Copy link
Copy Markdown
Collaborator

개발 코드 메인 브랜치로 머지

Summary by CodeRabbit

  • New Features
    • Added authentication flows for sign-up, login, and token refresh, including onboarding status.
    • Added episode creation, detail views, listing, search, pagination, and title suggestions.
    • Added home dashboard, rankings, history, matching, placement, and weekly/monthly show session APIs.
    • Added match cancellation and result completion with score and title updates.
    • Added optional OpenAI-powered title suggestions and local demo data.
  • Documentation
    • Expanded setup, API, configuration, security, database, and development guidance.

ljh8450 and others added 30 commits July 11, 2026 00:31
feat: 링 도메인 기초 인프라 & 링 메인 화면 조회
feature: 기억 대결 생명주기 API 구현 (시작, 취소)
feat: 대결 결과 확정 및 점수 로직 구현
feature: 랭킹 및 검색 구현
@coderabbitai

coderabbitai Bot commented Jul 11, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This PR adds episode-based domain models and APIs, authentication and onboarding flows, ranking and history views, match and show-session workflows, OpenAI/local title suggestions, database migrations, local demo seeding, and integration coverage.

Changes

Platform feature rollout

Layer / File(s) Summary
Configuration and persistence foundation
.env.example, .github/workflows/ci.yml, README.md, docs/schema.sql, src/main/resources/application.yml, src/main/resources/db/migration/*, src/main/java/com/team6/server/global/*
Adds platform configuration, timezone-aware auditing, validation handling, schema documentation, and migrations for episodes, rankings, matches, sessions, and onboarding.
Authentication and member onboarding
src/main/java/com/team6/server/auth/*, src/main/java/com/team6/server/member/*
Adds signup/login/refresh endpoints, token and profile responses, authenticated member resolution, onboarding state transitions, and member status APIs.
Episode management and title suggestions
src/main/java/com/team6/server/episode/*
Adds episode lifecycle management, creation/detail/list/search APIs, cursor pagination, ranking initialization, local title generation, and conditional OpenAI title suggestions.
Home, history, and ranking read APIs
src/main/java/com/team6/server/home/*, src/main/java/com/team6/server/history/*, src/main/java/com/team6/server/ranking/*
Adds authenticated home summaries, champion and match history, and paginated ranking responses backed by ranking repositories.
Match lifecycle and scoring
src/main/java/com/team6/server/match/controller/MatchController.java, src/main/java/com/team6/server/match/service/MatchService.java, src/main/java/com/team6/server/match/entity/*, src/main/java/com/team6/server/match/repository/*
Adds authenticated match creation, cancellation, completion, pessimistic locking, score deltas, ranking score events, and episode restoration.
Placement and scheduled show sessions
src/main/java/com/team6/server/match/service/*, src/main/java/com/team6/server/match/controller/{PlacementController,ShowController}.java
Adds onboarding/additional placement, weekly/monthly scheduled shows, balanced pairing, session progress, ordered matches, and show participation APIs.
Local demo data and operational support
src/main/java/com/team6/server/global/config/*, src/main/resources/application.yml, .env.example
Adds local-profile demo seeding, configurable dummy data, OpenAPI server metadata, and scheduling activation.
Integration validation
src/test/java/*
Adds integration coverage for authentication, onboarding, home and episode behavior, history, ring matching, authorization, pagination, and validation.

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

Possibly related PRs

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 1.11% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive 제목이 병합 작업을 나타낼 뿐, 로그인·에피소드/매칭·온보딩·랭킹 등 핵심 변경점을 설명하지 않습니다. 주요 변경을 반영해 예: "에피소드·매칭·온보딩 및 AI 제목 추천 기능 추가"처럼 구체적으로 수정하세요.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
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
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch develop

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
docs/schema.sql (1)

1-186: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Reference schema is stale and its header comment is inaccurate.

The header claims this reflects state "Current schema after Flyway V6"-equivalent, but the body already includes changes from V3 (SCHEDULED status), V5 (round_count), and V7 (title_score DEFAULT 1000 / CHECK ≥100), while omitting V8 (onboarding_status) and V9 (period_key, match_type, session_type, primary_episode_id, score_multiplier, loser_episode_id, match_order, score columns, placement_status) entirely. As the stated ERD-import reference, this drift will actively mislead readers about the live schema.

Suggest regenerating this file from the full migration set (through V9) and updating the header comment to reflect the actual applied version.

🤖 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 `@docs/schema.sql` around lines 1 - 186, Regenerate the reference schema from
the complete Flyway migration set through V9, ensuring it includes V8/V9
additions such as onboarding_status, period_key, match_type, session_type,
primary_episode_id, score_multiplier, loser_episode_id, match_order, score
fields, and placement_status. Update the header to accurately state the schema
version and remove outdated claims that it represents V6.
🧹 Nitpick comments (10)
src/main/resources/db/migration/V9__extend_matches_and_show_sessions.sql (1)

1-5: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

New match_type/session_type columns lack CHECK constraints.

Every other status/type column in this schema (event_type, status, placement_status, score_type) is constrained via CHECK. The new match_type (matching_events, matches) and session_type (ring_sessions) columns accept arbitrary strings, which is inconsistent and risks bad data feeding into score-multiplier logic.

🛡️ Example fix
 ALTER TABLE matching_events
     ADD COLUMN period_key VARCHAR(20) NULL AFTER event_type,
     ADD COLUMN match_type VARCHAR(30) NOT NULL DEFAULT 'RIVAL' AFTER period_key,
     ADD COLUMN score_multiplier DECIMAL(4,2) NOT NULL DEFAULT 1.00 AFTER score_reward,
+    ADD CONSTRAINT ck_matching_events_match_type CHECK (match_type IN ('RIVAL', 'GENERAL')),
     ADD CONSTRAINT uk_matching_events_type_period UNIQUE (event_type, period_key);

Also applies to: 11-11, 20-20

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

In `@src/main/resources/db/migration/V9__extend_matches_and_show_sessions.sql`
around lines 1 - 5, Add CHECK constraints for the new match_type and
session_type columns across migrations V9, 11-11, and 20-20, matching the
allowed values used by the application and existing schema conventions. Update
matching_events, matches, and ring_sessions definitions so invalid type strings
cannot be stored, and ensure the constraints are compatible with each column’s
defaults and migration order.
src/main/java/com/team6/server/auth/service/AuthService.java (1)

30-48: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Consider normalizing email casing.

Email is used for lookups/uniqueness checks (Lines 31, 34, 39) without normalization. Depending on DB collation, this can allow case-variant duplicate accounts or cause login mismatches for the same address entered with different casing.

♻️ Suggested normalization
 public Long signUp(SignUpRequest request) {
-    if (members.existsByEmail(request.email())) {
+    String email = request.email().toLowerCase();
+    if (members.existsByEmail(email)) {
         throw new BusinessException(ErrorCode.MEMBER_EMAIL_DUPLICATED);
     }
-    return members.save(new Member(request.email(), encoder.encode(request.password()), request.name())).getId();
+    return members.save(new Member(email, encoder.encode(request.password()), request.name())).getId();
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/team6/server/auth/service/AuthService.java` around lines 30
- 48, Normalize email addresses consistently before uniqueness checks,
persistence, and lookup: update signUp and login to use a shared normalization
rule (such as trimming whitespace and lowercasing) before calling
members.existsByEmail, constructing Member, and members.findByEmail. Keep the
normalized value consistent across all authentication flows.
src/main/java/com/team6/server/match/controller/MatchController.java (1)

48-62: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use imports instead of fully-qualified names for consistency.

completeMatch inlines fully-qualified com.team6.server.global.response.ApiResponse and com.team6.server.match.dto.* while the sibling handlers rely on imports. Adding imports keeps the controller consistent and readable.

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

In `@src/main/java/com/team6/server/match/controller/MatchController.java` around
lines 48 - 62, Replace the fully qualified ApiResponse, MatchResultResponseDto,
and MatchResultRequestDto references in completeMatch with their corresponding
imports, matching the style of the sibling controller handlers.
src/main/java/com/team6/server/match/service/MatchService.java (2)

92-94: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use the injected Clock for consistency and testability.

completeMatch derives time via LocalDateTime.now(clock), but startMatch uses LocalDateTime.now() directly, bypassing the injected Clock. Align both on clock so time is controllable in tests and consistent across the match lifecycle.

Proposed fix
-        LocalDateTime startedAt = LocalDateTime.now();
+        LocalDateTime startedAt = LocalDateTime.now(clock);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/team6/server/match/service/MatchService.java` around lines
92 - 94, Update startMatch to derive startedAt with LocalDateTime.now(clock),
matching completeMatch’s injected-clock usage; keep the shared timestamp passed
to both episodeA.markMatched and episodeB.markMatched.

190-192: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Redundant locked fetch of the show session.

showSessionRepository.findByIdWithPessimisticLock(match.getSessionId()) is executed once for the multiplier (Lines 191-192) and again for round completion (Lines 238-239). Fetch it once and reuse the reference to avoid the duplicate query/lock acquisition within the same transaction.

Also applies to: 238-239

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

In `@src/main/java/com/team6/server/match/service/MatchService.java` around lines
190 - 192, Fetch the show session once in the match-processing method, store the
result in a local variable, and derive the multiplier from that reference
instead of calling findByIdWithPessimisticLock again. Reuse the same session
object in the round-completion logic around the second fetch, preserving the
existing null-session handling and not-found exception behavior.
src/main/java/com/team6/server/member/service/MemberService.java (1)

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

Avoid magic string "IN_PROGRESS" for session status comparison.

Line 49 compares against a raw string literal. If ShowSession's status representation changes, this silently breaks without a compile error.

♻️ Suggested fix (pending confirmation of the actual status type)
-        return new OnboardingStatusResponse(member.getOnboardingStatus().name(),
-                placement != null && "IN_PROGRESS".equals(placement.getStatus()) ? placement.getId() : null,
+        return new OnboardingStatusResponse(member.getOnboardingStatus().name(),
+                placement != null && ShowSessionStatus.IN_PROGRESS.name().equals(placement.getStatus()) ? placement.getId() : null,

Please confirm ShowSession.getStatus() type (enum vs. String) to pick the right comparison approach.

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

In `@src/main/java/com/team6/server/member/service/MemberService.java` around
lines 44 - 53, Replace the raw "IN_PROGRESS" comparison in
MemberService.getOnboardingStatus with the canonical ShowSession status constant
or enum value, based on the return type of placement.getStatus(); use a typed
comparison for an enum or the centralized constant for a String.
src/main/java/com/team6/server/episode/repository/EpisodeRepository.java (1)

44-56: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

findReadyForShowForUpdate and findOnboardingCandidatesForUpdate are byte-for-byte identical queries.

Both methods have the same JPQL, parameters, and lock mode, differing only by name. Consider consolidating into one method with a name reflecting the shared semantics, or documenting why two names are needed for readability at call sites.

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

In `@src/main/java/com/team6/server/episode/repository/EpisodeRepository.java`
around lines 44 - 56, Consolidate the duplicate repository methods
findReadyForShowForUpdate and findOnboardingCandidatesForUpdate into one shared
method with a name describing their common semantics, then update all call
sites; if distinct names are required for readability, document the intentional
duplication instead.
src/main/java/com/team6/server/match/dto/MatchResultRequestDto.java (1)

3-11: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

No setter/constructor for winnerEpisodeId — relies on a Jackson quirk instead of an explicit mutator.

Only @Getter/@NoArgsConstructor are present; there's no @Setter and no all-args constructor. Jackson can still populate the private field by default because the getter makes the property "known" and Jackson falls back to direct field injection — but this behavior is an implicit default rather than an explicit contract, and depends on the getter existing and default Jackson visibility settings not being overridden elsewhere in the app. Every other request/response DTO shown in this PR (e.g. TodayEpisodeResponse, EpisodeListItemResponse) uses a record, which makes the deserialization contract explicit and immutable. Consider aligning this class with that pattern.

♻️ Suggested refactor to a record
-import lombok.Getter;
-import lombok.NoArgsConstructor;
-
-@Getter
-@NoArgsConstructor
-public class MatchResultRequestDto {
-
-    private Long winnerEpisodeId;
-}
+public record MatchResultRequestDto(Long winnerEpisodeId) {}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/team6/server/match/dto/MatchResultRequestDto.java` around
lines 3 - 11, Make MatchResultRequestDto’s deserialization contract explicit by
replacing the Lombok-based mutable DTO with a record that declares
winnerEpisodeId as its component; remove the `@Getter` and `@NoArgsConstructor`
annotations and preserve the existing Long field name and type.
src/main/java/com/team6/server/ranking/repository/RankingEpisodeScoreRepository.java (1)

37-51: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Consider a window function instead of a correlated subquery for competitionRank.

The (SELECT COUNT(*) + 1 FROM episode_rankings higher WHERE higher.title_score > er.title_score) subquery re-scans episode_rankings for every row returned by the page, which scales poorly as the table grows. A single RANK() OVER (ORDER BY er.title_score DESC) produces the same competition-ranking semantics (ties share a rank) in one pass.

♻️ Proposed refactor
 `@Query`(value = """
         SELECT er.episode_id AS episodeId, e.title AS episodeTitle,
-               er.title_score AS score, t.name AS titleName,
-               (SELECT COUNT(*) + 1 FROM episode_rankings higher
-                WHERE higher.title_score > er.title_score) AS competitionRank
+               er.title_score AS score, t.name AS titleName,
+               RANK() OVER (ORDER BY er.title_score DESC) AS competitionRank
         FROM episode_rankings er
         JOIN episodes e ON e.id = er.episode_id
         LEFT JOIN titles t ON t.id = er.current_title_id
         ORDER BY er.title_score DESC, er.episode_id ASC
         LIMIT :limit OFFSET :offset
         """, nativeQuery = true)

Confirm the target database engine/version supports window functions before adopting (e.g., MySQL 8.0+/PostgreSQL support RANK() OVER, older MySQL 5.7 does not).

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

In
`@src/main/java/com/team6/server/ranking/repository/RankingEpisodeScoreRepository.java`
around lines 37 - 51, Replace the correlated competitionRank subquery in
findRankingPage with RANK() OVER (ORDER BY er.title_score DESC), preserving the
existing aliases and pagination ordering. Verify the configured database
engine/version supports window functions, and update compatibility configuration
or choose an equivalent fallback if it does not.
src/main/java/com/team6/server/global/exception/GlobalExceptionHandler.java (1)

38-41: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract violation details instead of a generic message.

The sibling handler for MethodArgumentNotValidException builds a field-level message, but this new handler for ConstraintViolationException/HandlerMethodValidationException discards violation details entirely, reducing debuggability of query/path-parameter validation errors.

♻️ Suggested consolidation
     `@ExceptionHandler`({ConstraintViolationException.class, HandlerMethodValidationException.class})
-    ResponseEntity<ApiResponse<Void>> parameterValidation(Exception e) {
-        return response(ErrorCode.VALIDATION_ERROR);
+    ResponseEntity<ApiResponse<Void>> parameterValidation(Exception e) {
+        String message = e instanceof ConstraintViolationException cve
+                ? cve.getConstraintViolations().stream()
+                        .map(v -> v.getPropertyPath() + ": " + v.getMessage())
+                        .collect(Collectors.joining(", "))
+                : e.getMessage();
+        return response(ErrorCode.VALIDATION_ERROR, message);
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/team6/server/global/exception/GlobalExceptionHandler.java`
around lines 38 - 41, Update GlobalExceptionHandler.parameterValidation to
extract field/parameter violation details from ConstraintViolationException and
HandlerMethodValidationException, matching the field-level message behavior of
the MethodArgumentNotValidException handler, and pass the resulting detail into
the validation error response instead of returning only the generic error code.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/ci.yml:
- Around line 18-19: Restore CI test execution by removing the global “-x test”
exclusion from the Gradle command. Fix the timezone-dependent integration tests
using the existing TimeConfig pattern, such as injecting a fixed Clock or
TimeZone, or narrowly exclude only the identified failing test tasks/classes if
an immediate workaround is required.

In `@README.md`:
- Around line 164-176: The README migration list documents duplicate V7 Flyway
migrations, which causes startup failure. Update the migration documentation to
assign one of the V7 migrations, such as
V7__align_episode_ranking_score_bounds.sql, the next unused unique version and
ensure the referenced filename matches the actual migration file.

In `@src/main/java/com/team6/server/auth/service/AuthService.java`:
- Around line 30-35: Handle the unique-email constraint race in
AuthService.signUp by catching DataIntegrityViolationException from members.save
and translating it to BusinessException(ErrorCode.MEMBER_EMAIL_DUPLICATED),
while preserving the existing pre-check and successful return behavior.
- Around line 50-62: Implement refresh-token rotation and revocation across
AuthService and JwtProvider: persist or denylist issued refresh-token
identifiers with expiry, validate the presented token is active, revoke it
before issuing replacements in refresh, and reject revoked or reused tokens. Add
logout revocation support and ensure token creation stores the necessary
identifier while preserving existing member and expiry handling.

In
`@src/main/java/com/team6/server/episode/infrastructure/openai/OpenAiResponse.java`:
- Around line 7-9: The IncompleteDetails field is not mapped from OpenAI’s
snake_case response property. In OpenAiResponse, annotate the incompleteDetails
component with `@JsonProperty`("incomplete_details") (or configure an equivalent
shared snake_case naming strategy) so the returned value is populated.

In `@src/main/java/com/team6/server/episode/repository/EpisodeRepository.java`:
- Around line 58-72: The PESSIMISTIC_WRITE lock in
findPlacementOpponentsForUpdate currently applies to the joined
RankingEpisodeScore rows as well as Episode. Narrow the locked query to Episode
records only by removing the join and score-based ordering, or split score
retrieval into a separate non-locking read while preserving opponent ordering
and pagination.

In `@src/main/java/com/team6/server/global/config/LocalDummyDataSeeder.java`:
- Around line 91-107: Prevent silent desynchronization in createEpisodes by
validating that names and deltas have equal sizes before iterating, failing fast
with a clear exception if they differ. Keep the existing indexing logic only
after this guard, or refactor the inputs into paired records so each episode
name and delta cannot become mismatched.

In `@src/main/java/com/team6/server/global/security/CurrentMemberProvider.java`:
- Around line 18-26: Handle a null authentication before accessing it in
CurrentMemberProvider.require. Treat null as an invalid token by throwing
BusinessException with ErrorCode.INVALID_TOKEN, while preserving the existing
member ID parsing and lookup behavior for non-null authentication.

In `@src/main/java/com/team6/server/match/service/MatchService.java`:
- Around line 193-195: The score calculation in MatchService’s ScoreDelta
construction uses longValueExact(), which fails for fractional multipliers such
as 1.25. Replace both conversions with an explicit, documented rounding strategy
before converting to long, or validate/constrain multipliers to whole numbers
before this calculation.

In `@src/main/java/com/team6/server/ranking/entity/RankingEpisodeScore.java`:
- Around line 30-32: Update GlobalExceptionHandler to handle
ObjectOptimisticLockingFailureException explicitly and return HTTP 409 Conflict
with an appropriate response body; use the existing exception-handler
conventions and verify MatchService version conflicts no longer reach the
generic 500 handler.

In `@src/main/resources/application.yml`:
- Line 22: Replace the hardcoded default in the JWT secret configuration with a
required unset fallback so startup fails when JWT_SECRET is missing, or restrict
the development fallback to an explicitly activated local profile. Ensure
non-local environments cannot use the publicly known secret.
- Around line 14-18: In the active application configuration, change
spring.flyway.enabled to true so the versioned V2–V9 migrations run, and replace
hibernate.ddl-auto: update with a migration-compatible setting such as validate.
Remove the checked-in JWT_SECRET fallback from the relevant security
configuration and require the property so startup fails when it is missing.

In `@src/main/resources/db/migration/V7__align_episode_ranking_score_bounds.sql`:
- Around line 1-4: Rename the migration file containing the `ALTER TABLE
episode_rankings` statement so its Flyway version is unique and does not
conflict with `V7__add_ranking_period_index.sql`; preserve the migration
description and SQL contents.
- Around line 1-4: Backfill existing rows before tightening the constraint: in
the migration, update `episode_rankings` rows with `title_score` below 100 to a
valid value (such as 100), then alter the default and replace
`ck_episode_rankings_title_score` with the `title_score >= 100` check.

In `@src/main/resources/db/migration/V9__extend_matches_and_show_sessions.sql`:
- Line 4: Update the migration definitions for score_multiplier in both
referenced locations to enforce a strictly positive value with a database CHECK
constraint, while retaining the existing DECIMAL type and default. Ensure the
constraint is applied consistently to every definition of this column.

---

Outside diff comments:
In `@docs/schema.sql`:
- Around line 1-186: Regenerate the reference schema from the complete Flyway
migration set through V9, ensuring it includes V8/V9 additions such as
onboarding_status, period_key, match_type, session_type, primary_episode_id,
score_multiplier, loser_episode_id, match_order, score fields, and
placement_status. Update the header to accurately state the schema version and
remove outdated claims that it represents V6.

---

Nitpick comments:
In `@src/main/java/com/team6/server/auth/service/AuthService.java`:
- Around line 30-48: Normalize email addresses consistently before uniqueness
checks, persistence, and lookup: update signUp and login to use a shared
normalization rule (such as trimming whitespace and lowercasing) before calling
members.existsByEmail, constructing Member, and members.findByEmail. Keep the
normalized value consistent across all authentication flows.

In `@src/main/java/com/team6/server/episode/repository/EpisodeRepository.java`:
- Around line 44-56: Consolidate the duplicate repository methods
findReadyForShowForUpdate and findOnboardingCandidatesForUpdate into one shared
method with a name describing their common semantics, then update all call
sites; if distinct names are required for readability, document the intentional
duplication instead.

In `@src/main/java/com/team6/server/global/exception/GlobalExceptionHandler.java`:
- Around line 38-41: Update GlobalExceptionHandler.parameterValidation to
extract field/parameter violation details from ConstraintViolationException and
HandlerMethodValidationException, matching the field-level message behavior of
the MethodArgumentNotValidException handler, and pass the resulting detail into
the validation error response instead of returning only the generic error code.

In `@src/main/java/com/team6/server/match/controller/MatchController.java`:
- Around line 48-62: Replace the fully qualified ApiResponse,
MatchResultResponseDto, and MatchResultRequestDto references in completeMatch
with their corresponding imports, matching the style of the sibling controller
handlers.

In `@src/main/java/com/team6/server/match/dto/MatchResultRequestDto.java`:
- Around line 3-11: Make MatchResultRequestDto’s deserialization contract
explicit by replacing the Lombok-based mutable DTO with a record that declares
winnerEpisodeId as its component; remove the `@Getter` and `@NoArgsConstructor`
annotations and preserve the existing Long field name and type.

In `@src/main/java/com/team6/server/match/service/MatchService.java`:
- Around line 92-94: Update startMatch to derive startedAt with
LocalDateTime.now(clock), matching completeMatch’s injected-clock usage; keep
the shared timestamp passed to both episodeA.markMatched and
episodeB.markMatched.
- Around line 190-192: Fetch the show session once in the match-processing
method, store the result in a local variable, and derive the multiplier from
that reference instead of calling findByIdWithPessimisticLock again. Reuse the
same session object in the round-completion logic around the second fetch,
preserving the existing null-session handling and not-found exception behavior.

In `@src/main/java/com/team6/server/member/service/MemberService.java`:
- Around line 44-53: Replace the raw "IN_PROGRESS" comparison in
MemberService.getOnboardingStatus with the canonical ShowSession status constant
or enum value, based on the return type of placement.getStatus(); use a typed
comparison for an enum or the centralized constant for a String.

In
`@src/main/java/com/team6/server/ranking/repository/RankingEpisodeScoreRepository.java`:
- Around line 37-51: Replace the correlated competitionRank subquery in
findRankingPage with RANK() OVER (ORDER BY er.title_score DESC), preserving the
existing aliases and pagination ordering. Verify the configured database
engine/version supports window functions, and update compatibility configuration
or choose an equivalent fallback if it does not.

In `@src/main/resources/db/migration/V9__extend_matches_and_show_sessions.sql`:
- Around line 1-5: Add CHECK constraints for the new match_type and session_type
columns across migrations V9, 11-11, and 20-20, matching the allowed values used
by the application and existing schema conventions. Update matching_events,
matches, and ring_sessions definitions so invalid type strings cannot be stored,
and ensure the constraints are compatible with each column’s defaults and
migration order.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 82410e96-01e9-4617-ab7e-d435936a7c6f

📥 Commits

Reviewing files that changed from the base of the PR and between 596e11e and 069110b.

📒 Files selected for processing (105)
  • .env.example
  • .github/workflows/ci.yml
  • README.md
  • docs/schema.sql
  • src/main/java/com/team6/server/ServerApplication.java
  • src/main/java/com/team6/server/auth/AuthController.java
  • src/main/java/com/team6/server/auth/AuthDtos.java
  • src/main/java/com/team6/server/auth/AuthService.java
  • src/main/java/com/team6/server/auth/controller/AuthController.java
  • src/main/java/com/team6/server/auth/dto/LoginRequest.java
  • src/main/java/com/team6/server/auth/dto/LoginResponse.java
  • src/main/java/com/team6/server/auth/dto/RefreshRequest.java
  • src/main/java/com/team6/server/auth/dto/SignUpRequest.java
  • src/main/java/com/team6/server/auth/dto/TokenResponse.java
  • src/main/java/com/team6/server/auth/repository/MemberRepository.java
  • src/main/java/com/team6/server/auth/service/AuthService.java
  • src/main/java/com/team6/server/episode/Episode.java
  • src/main/java/com/team6/server/episode/controller/EpisodeController.java
  • src/main/java/com/team6/server/episode/dto/CreateEpisodeRequest.java
  • src/main/java/com/team6/server/episode/dto/CreateEpisodeResponse.java
  • src/main/java/com/team6/server/episode/dto/EpisodeDetailResponse.java
  • src/main/java/com/team6/server/episode/dto/EpisodeListItemResponse.java
  • src/main/java/com/team6/server/episode/dto/EpisodeListResponse.java
  • src/main/java/com/team6/server/episode/dto/EpisodeSearchItemResponse.java
  • src/main/java/com/team6/server/episode/dto/EpisodeSearchResponse.java
  • src/main/java/com/team6/server/episode/dto/TitleSuggestionRequest.java
  • src/main/java/com/team6/server/episode/dto/TitleSuggestionResponse.java
  • src/main/java/com/team6/server/episode/infrastructure/openai/OpenAiConfig.java
  • src/main/java/com/team6/server/episode/infrastructure/openai/OpenAiProperties.java
  • src/main/java/com/team6/server/episode/infrastructure/openai/OpenAiRequest.java
  • src/main/java/com/team6/server/episode/infrastructure/openai/OpenAiResponse.java
  • src/main/java/com/team6/server/episode/infrastructure/openai/OpenAiTitleSuggestionProvider.java
  • src/main/java/com/team6/server/episode/repository/EpisodeRepository.java
  • src/main/java/com/team6/server/episode/service/EpisodeCursorCodec.java
  • src/main/java/com/team6/server/episode/service/EpisodeService.java
  • src/main/java/com/team6/server/episode/service/LocalTitleSuggestionProvider.java
  • src/main/java/com/team6/server/episode/service/TitleSuggestionProvider.java
  • src/main/java/com/team6/server/global/config/LocalDummyDataSeeder.java
  • src/main/java/com/team6/server/global/config/OpenApiConfig.java
  • src/main/java/com/team6/server/global/config/TimeConfig.java
  • src/main/java/com/team6/server/global/exception/ErrorCode.java
  • src/main/java/com/team6/server/global/exception/GlobalExceptionHandler.java
  • src/main/java/com/team6/server/global/security/CurrentMemberProvider.java
  • src/main/java/com/team6/server/history/controller/HistoryController.java
  • src/main/java/com/team6/server/history/dto/ChampionHistoryItemResponse.java
  • src/main/java/com/team6/server/history/dto/HistoryHomeResponse.java
  • src/main/java/com/team6/server/history/dto/MatchHistoryItemResponse.java
  • src/main/java/com/team6/server/history/service/HistoryService.java
  • src/main/java/com/team6/server/home/controller/HomeController.java
  • src/main/java/com/team6/server/home/dto/HomeResponse.java
  • src/main/java/com/team6/server/home/dto/TodayEpisodeResponse.java
  • src/main/java/com/team6/server/home/dto/UpcomingEventResponse.java
  • src/main/java/com/team6/server/home/service/HomeService.java
  • src/main/java/com/team6/server/match/controller/MatchController.java
  • src/main/java/com/team6/server/match/controller/PlacementController.java
  • src/main/java/com/team6/server/match/controller/ShowController.java
  • src/main/java/com/team6/server/match/dto/AvailableShowResponse.java
  • src/main/java/com/team6/server/match/dto/MatchRequestDto.java
  • src/main/java/com/team6/server/match/dto/MatchResultRequestDto.java
  • src/main/java/com/team6/server/match/dto/MatchResultResponseDto.java
  • src/main/java/com/team6/server/match/dto/RingResponseDto.java
  • src/main/java/com/team6/server/match/dto/ShowSessionProgressResponse.java
  • src/main/java/com/team6/server/match/dto/ShowSessionResponse.java
  • src/main/java/com/team6/server/match/entity/Match.java
  • src/main/java/com/team6/server/match/entity/MatchingEvent.java
  • src/main/java/com/team6/server/match/entity/ShowSession.java
  • src/main/java/com/team6/server/match/repository/MatchRepository.java
  • src/main/java/com/team6/server/match/repository/MatchingEventRepository.java
  • src/main/java/com/team6/server/match/repository/ShowSessionRepository.java
  • src/main/java/com/team6/server/match/service/BalancedPairingPolicy.java
  • src/main/java/com/team6/server/match/service/MatchService.java
  • src/main/java/com/team6/server/match/service/PlacementService.java
  • src/main/java/com/team6/server/match/service/ShowScheduler.java
  • src/main/java/com/team6/server/match/service/ShowService.java
  • src/main/java/com/team6/server/member/Member.java
  • src/main/java/com/team6/server/member/MemberRepository.java
  • src/main/java/com/team6/server/member/controller/MemberController.java
  • src/main/java/com/team6/server/member/dto/MemberMeResponse.java
  • src/main/java/com/team6/server/member/dto/OnboardingStatusResponse.java
  • src/main/java/com/team6/server/member/service/MemberService.java
  • src/main/java/com/team6/server/ranking/controller/RankingController.java
  • src/main/java/com/team6/server/ranking/dto/RankingItemResponse.java
  • src/main/java/com/team6/server/ranking/dto/RankingListResponse.java
  • src/main/java/com/team6/server/ranking/entity/RankingEpisodeScore.java
  • src/main/java/com/team6/server/ranking/entity/RankingScoreEvent.java
  • src/main/java/com/team6/server/ranking/entity/Title.java
  • src/main/java/com/team6/server/ranking/repository/RankingEpisodeScoreRepository.java
  • src/main/java/com/team6/server/ranking/repository/RankingScoreEventRepository.java
  • src/main/java/com/team6/server/ranking/repository/TitleRepository.java
  • src/main/java/com/team6/server/ranking/service/RankingService.java
  • src/main/resources/application.yml
  • src/main/resources/db/migration/V2__create_product_feature_tables.sql
  • src/main/resources/db/migration/V3__add_scheduled_matching_event_status.sql
  • src/main/resources/db/migration/V4__rename_episode_domain.sql
  • src/main/resources/db/migration/V5__create_ring_sessions.sql
  • src/main/resources/db/migration/V6__add_member_onboarding_completed_at.sql
  • src/main/resources/db/migration/V7__add_ranking_period_index.sql
  • src/main/resources/db/migration/V7__align_episode_ranking_score_bounds.sql
  • src/main/resources/db/migration/V8__add_member_onboarding_status.sql
  • src/main/resources/db/migration/V9__extend_matches_and_show_sessions.sql
  • src/test/java/com/team6/server/auth/AuthApiIntegrationTest.java
  • src/test/java/com/team6/server/history/HistoryApiIntegrationTest.java
  • src/test/java/com/team6/server/home/HomeEpisodeApiIntegrationTest.java
  • src/test/java/com/team6/server/matching/RingApiIntegrationTest.java
  • src/test/java/com/team6/server/member/MemberOnboardingApiIntegrationTest.java
💤 Files with no reviewable changes (4)
  • src/main/java/com/team6/server/member/MemberRepository.java
  • src/main/java/com/team6/server/auth/AuthService.java
  • src/main/java/com/team6/server/auth/AuthDtos.java
  • src/main/java/com/team6/server/auth/AuthController.java

Comment thread .github/workflows/ci.yml
Comment on lines +18 to +19
# TODO: 통합 테스트의 시간대 의존성을 제거한 뒤 테스트 실행을 복구한다.
- run: ./gradlew clean build -x test

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

CI no longer runs tests.

-x test skips the entire test suite on every build; the TODO acknowledges this is a workaround for a timezone dependency in integration tests. Merging with tests disabled removes the safety net for all changes going forward (including this large PR), which is risky given the sizeable auth/matching/ranking logic being introduced.

Consider fixing the underlying timezone dependency (e.g., inject a fixed Clock/TimeZone in test config as done via TimeConfig) instead of disabling tests outright, or at minimum scope -x test to only the failing test classes/tasks.

🤖 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 @.github/workflows/ci.yml around lines 18 - 19, Restore CI test execution by
removing the global “-x test” exclusion from the Gradle command. Fix the
timezone-dependent integration tests using the existing TimeConfig pattern, such
as injecting a fixed Clock or TimeZone, or narrowly exclude only the identified
failing test tasks/classes if an immediate workaround is required.

Comment thread README.md
Comment on lines +164 to +176
## 데이터베이스와 마이그레이션

마이그레이션 파일은 `src/main/resources/db/migration` 아래에 위치합니다.

```text
V1__init.sql
V2__create_product_feature_tables.sql
V3__add_scheduled_matching_event_status.sql
...
```

운영에 적용된 Flyway 파일은 수정하지 않고, 스키마 변경이 필요하면 새 버전 파일을 추가합니다.

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# List migration files and flag any duplicate Flyway version prefixes
fd . src/main/resources/db/migration -e sql | sed -E 's#.*/(V[0-9]+(_[0-9]+)*)__.*#\1#' | sort | uniq -c | sort -rn | head -20
fd . src/main/resources/db/migration -e sql

Repository: 8th-COKERTHON/server-team6

Length of output: 934


Use a unique Flyway version for one of the V7 migrations
src/main/resources/db/migration/V7__add_ranking_period_index.sql and src/main/resources/db/migration/V7__align_episode_ranking_score_bounds.sql both use version V7, which makes Flyway fail on startup when migrations are enabled. Rename one to the next unused version.

🤖 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 `@README.md` around lines 164 - 176, The README migration list documents
duplicate V7 Flyway migrations, which causes startup failure. Update the
migration documentation to assign one of the V7 migrations, such as
V7__align_episode_ranking_score_bounds.sql, the next unused unique version and
ensure the referenced filename matches the actual migration file.

Comment on lines +30 to +35
public Long signUp(SignUpRequest request) {
if (members.existsByEmail(request.email())) {
throw new BusinessException(ErrorCode.MEMBER_EMAIL_DUPLICATED);
}
return members.save(new Member(request.email(), encoder.encode(request.password()), request.name())).getId();
}

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n -i 'email' docs/schema.sql src/main/resources/db/migration

Repository: 8th-COKERTHON/server-team6

Length of output: 457


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the auth service and any global exception handling around data integrity errors.
ast-grep outline src/main/java/com/team6/server/auth/service/AuthService.java --view expanded || true
printf '\n---\n'
fd -a '.*(Exception|Advice|Handler).*\.java' src/main/java | sed 's#^\./##' | sort
printf '\n---\n'
rg -n "DataIntegrityViolationException|BusinessException|MEMBER_EMAIL_DUPLICATED|`@RestControllerAdvice`|`@ExceptionHandler`" src/main/java

Repository: 8th-COKERTHON/server-team6

Length of output: 16319


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '1,220p' src/main/java/com/team6/server/global/exception/GlobalExceptionHandler.java
printf '\n---\n'
sed -n '1,120p' src/main/java/com/team6/server/auth/service/AuthService.java

Repository: 8th-COKERTHON/server-team6

Length of output: 6147


Handle duplicate-email races as a conflict, not a 500 existsByEmail still leaves a TOCTOU gap; with the unique members.email constraint, a concurrent insert can surface as INTERNAL_SERVER_ERROR instead of MEMBER_EMAIL_DUPLICATED. Catch DataIntegrityViolationException here or map it globally to the duplicate-email business error.

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

In `@src/main/java/com/team6/server/auth/service/AuthService.java` around lines 30
- 35, Handle the unique-email constraint race in AuthService.signUp by catching
DataIntegrityViolationException from members.save and translating it to
BusinessException(ErrorCode.MEMBER_EMAIL_DUPLICATED), while preserving the
existing pre-check and successful return behavior.

Comment on lines +50 to +62
@Transactional(readOnly = true)
public TokenResponse refresh(RefreshRequest request) {
var claims = jwt.parseRefreshToken(request.refreshToken());
Long memberId = Long.valueOf(claims.getSubject());
return tokens(members.findById(memberId)
.orElseThrow(() -> new BusinessException(ErrorCode.MEMBER_NOT_FOUND)));
}

private TokenResponse tokens(Member member) {
String role = member.getRole().name();
return new TokenResponse(jwt.createAccessToken(member.getId(), role),
jwt.createRefreshToken(member.getId(), role), "Bearer", jwt.accessExpiresInSeconds());
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
fd -t f JwtProvider.java | xargs -I{} cat -n {}

Repository: 8th-COKERTHON/server-team6

Length of output: 2411


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== refresh/logout/token tracking search ==\n'
rg -n --hidden --glob '!**/target/**' --glob '!**/build/**' --glob '!**/dist/**' \
  -e 'refresh token' -e 'refreshToken' -e 'denylist' -e 'blacklist' -e 'revoc' -e 'logout' \
  src/main/java

printf '\n== auth-related files ==\n'
fd -t f -e java src/main/java | rg 'Auth|Jwt|Token|Login|Logout|Refresh'

printf '\n== member/token entity/repository search ==\n'
rg -n --hidden --glob '!**/target/**' --glob '!**/build/**' --glob '!**/dist/**' \
  -e 'class .*Token' -e 'interface .*Token' -e 'RefreshToken' -e 'TokenStore' -e 'TokenRepository' \
  src/main/java

Repository: 8th-COKERTHON/server-team6

Length of output: 1190


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== AuthService and JwtProvider context ==\n'
sed -n '1,220p' src/main/java/com/team6/server/auth/service/AuthService.java
printf '\n---\n'
sed -n '1,220p' src/main/java/com/team6/server/global/security/JwtProvider.java

Repository: 8th-COKERTHON/server-team6

Length of output: 5036


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Search the auth package and nearby security infrastructure for server-side refresh-token state or revocation.
rg -n --hidden --glob '!**/target/**' --glob '!**/build/**' --glob '!**/dist/**' \
  -e 'refreshToken' -e 'RefreshToken' -e 'denylist' -e 'blacklist' -e 'revoke' -e 'revocation' \
  -e 'logout' -e 'token store' -e 'tokenStore' -e 'token repository' -e 'TokenRepository' \
  src/main/java

printf '\n== auth package files ==\n'
fd -t f src/main/java/com/team6/server/auth src/main/java/com/team6/server/global/security

printf '\n== references to JwtProvider ==\n'
rg -n --hidden --glob '!**/target/**' --glob '!**/build/**' --glob '!**/dist/**' 'JwtProvider' src/main/java

Repository: 8th-COKERTHON/server-team6

Length of output: 1746


Add refresh-token rotation/revocation

JwtProvider only signs and parses JWTs; it doesn’t persist issued refresh tokens or support logout/reuse detection. A stolen refresh token stays usable until expiry, even after a new pair is issued. Add server-side tracking or a denylist if invalidation is required.

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

In `@src/main/java/com/team6/server/auth/service/AuthService.java` around lines 50
- 62, Implement refresh-token rotation and revocation across AuthService and
JwtProvider: persist or denylist issued refresh-token identifiers with expiry,
validate the presented token is active, revoke it before issuing replacements in
refresh, and reject revoked or reused tokens. Add logout revocation support and
ensure token creation stores the necessary identifier while preserving existing
member and expiry handling.

Comment on lines +7 to +9
record OpenAiResponse(String id, String status, IncompleteDetails incompleteDetails, List<Output> output) {
@JsonIgnoreProperties(ignoreUnknown = true)
record IncompleteDetails(String reason) {}

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check for a snake_case naming strategy or explicit `@JsonProperty` mapping for OpenAI DTOs
rg -n "PropertyNamingStrateg|SNAKE_CASE|JsonProperty" -g '*.java' src/main/java/com/team6/server/episode/infrastructure/openai

Repository: 8th-COKERTHON/server-team6

Length of output: 445


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== ObjectMapper / Jackson config =="
rg -n "ObjectMapper|Jackson2ObjectMapperBuilder|PropertyNamingStrategy|PropertyNamingStrategies|`@JsonNaming`|JsonAutoDetect|Jackson2ObjectMapperBuilderCustomizer" src/main/java src/test/java

echo
echo "== OpenAI DTO files =="
git ls-files 'src/main/java/com/team6/server/episode/infrastructure/openai/*.java'

echo
echo "== OpenAiResponse.java (numbered) =="
cat -n src/main/java/com/team6/server/episode/infrastructure/openai/OpenAiResponse.java

echo
echo "== OpenAiRequest.java (numbered) =="
cat -n src/main/java/com/team6/server/episode/infrastructure/openai/OpenAiRequest.java

Repository: 8th-COKERTHON/server-team6

Length of output: 4260


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== OpenAiConfig.java =="
cat -n src/main/java/com/team6/server/episode/infrastructure/openai/OpenAiConfig.java

echo
echo "== Any Jackson configuration across main sources =="
rg -n "PropertyNamingStrategy|PropertyNamingStrategies|`@JsonNaming`|Jackson2ObjectMapperBuilderCustomizer|ObjectMapper" src/main/java

Repository: 8th-COKERTHON/server-team6

Length of output: 3291


Map incomplete_details explicitly. OpenAiResponse has no snake_case mapping here, so incompleteDetails will stay null when OpenAI returns incomplete_details. Add @JsonProperty("incomplete_details") here, or a shared snake_case strategy.

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

In
`@src/main/java/com/team6/server/episode/infrastructure/openai/OpenAiResponse.java`
around lines 7 - 9, The IncompleteDetails field is not mapped from OpenAI’s
snake_case response property. In OpenAiResponse, annotate the incompleteDetails
component with `@JsonProperty`("incomplete_details") (or configure an equivalent
shared snake_case naming strategy) so the returned value is populated.

Comment on lines +30 to +32
@Version
@Column(nullable = false)
private Long version;

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 | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if optimistic lock conflicts are retried or mapped to a client-safe status
rg -n -C3 'OptimisticLockingFailureException|ObjectOptimisticLockingFailureException|`@Retryable`' src/main/java/com/team6/server/match/service/MatchService.java src/main/java/com/team6/server/global/exception/GlobalExceptionHandler.java 2>/dev/null

Repository: 8th-COKERTHON/server-team6

Length of output: 164


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate the relevant service/exception handling files and any optimistic-lock handling
printf '\n== candidate files ==\n'
git ls-files | rg 'src/main/java/.*/(MatchService|GlobalExceptionHandler|.*ExceptionHandler|.*ControllerAdvice|.*Advice)\.java$|src/main/java/.*/ranking/.+\.java$'

printf '\n== repository search ==\n'
rg -n -C2 'ObjectOptimisticLockingFailureException|OptimisticLockingFailureException|OptimisticLock|`@Retryable`|ControllerAdvice|ExceptionHandler|ResponseStatusException|HttpStatus\.CONFLICT|409' src/main/java 2>/dev/null || true

Repository: 8th-COKERTHON/server-team6

Length of output: 10894


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map ranking entity and service usage around versioned saves
ast-grep outline src/main/java/com/team6/server/ranking/entity/RankingEpisodeScore.java || true
printf '\n== ranking score references ==\n'
rg -n -C2 'RankingEpisodeScore|applyDelta\(|updateTitle\(|getCurrentTitleId\(|save\(' src/main/java/com/team6/server 2>/dev/null || true

Repository: 8th-COKERTHON/server-team6

Length of output: 35153


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map ranking entity and service usage around versioned saves
ast-grep outline src/main/java/com/team6/server/ranking/entity/RankingEpisodeScore.java || true
printf '\n== ranking score references ==\n'
rg -n -C2 'RankingEpisodeScore|applyDelta\(|updateTitle\(|getCurrentTitleId\(\)|save\(' src/main/java/com/team6/server 2>/dev/null || true

Repository: 8th-COKERTHON/server-team6

Length of output: 35153


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '1,120p' src/main/java/com/team6/server/global/exception/GlobalExceptionHandler.java
printf '\n---\n'
sed -n '1,120p' src/main/java/com/team6/server/global/exception/ErrorCode.java

Repository: 8th-COKERTHON/server-team6

Length of output: 7292


Map optimistic-lock conflicts to 409
MatchService relies on @Version, but GlobalExceptionHandler has no handler for ObjectOptimisticLockingFailureException; these conflicts will fall through to the generic Exception handler and return 500. Add a conflict handler or retry the update.

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

In `@src/main/java/com/team6/server/ranking/entity/RankingEpisodeScore.java`
around lines 30 - 32, Update GlobalExceptionHandler to handle
ObjectOptimisticLockingFailureException explicitly and return HTTP 409 Conflict
with an appropriate response body; use the existing exception-handler
conventions and verify MatchService version conflicts no longer reach the
generic 500 handler.

Comment on lines 14 to +18
flyway:
enabled: true
enabled: false
locations: classpath:db/migration
validate-on-migrate: false
baseline-on-migrate: 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.

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
fd -e yml -e yaml . src/main/resources

Repository: 8th-COKERTHON/server-team6

Length of output: 278


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- application.yml ---'
cat -n src/main/resources/application.yml

echo
echo '--- application-dev.yml ---'
cat -n src/main/resources/application-dev.yml

echo
echo '--- application-prod.yml ---'
cat -n src/main/resources/application-prod.yml

Repository: 8th-COKERTHON/server-team6

Length of output: 2232


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- Flyway config references ---'
rg -n "flyway|ddl-auto|JWT_SECRET|CORS_ALLOWED_ORIGINS|OPENAI_TITLE_SUGGESTION_ENABLED" src/main/resources src/main/java

echo
echo '--- Migration files ---'
fd -a . src/main/resources/db/migration

echo
echo '--- application-local.yml if present ---'
fd -a 'application-local.yml' src/main/resources

Repository: 8th-COKERTHON/server-team6

Length of output: 1568


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n "jwt\.secret|JWT_SECRET|secret-key|sign.*jwt|Jwt|jwt" src/main/java src/main/resources -g '!src/main/resources/application.yml'

Repository: 8th-COKERTHON/server-team6

Length of output: 4900


Re-enable Flyway in the active config

spring.flyway.enabled: false leaves the new V2V9 migrations unused, and hibernate.ddl-auto: update shifts schema management to Hibernate instead of the versioned SQL. Re-enable Flyway or drop the migration files if they are not meant to run.

  • Remove the checked-in JWT_SECRET fallback too; missing config should fail fast instead of booting with a shared default secret.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/resources/application.yml` around lines 14 - 18, In the active
application configuration, change spring.flyway.enabled to true so the versioned
V2–V9 migrations run, and replace hibernate.ddl-auto: update with a
migration-compatible setting such as validate. Remove the checked-in JWT_SECRET
fallback from the relevant security configuration and require the property so
startup fails when it is missing.

server:
forward-headers-strategy: framework
jwt:
secret: ${JWT_SECRET:local-development-secret-key-must-be-at-least-32-bytes}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Hardcoded default JWT signing secret fallback.

If JWT_SECRET isn't set, the app silently starts with a fixed, publicly-visible secret rather than failing fast. Consider removing the default (or gating it to a local profile only) so misconfigured non-local environments fail loudly instead of signing tokens with a known key.

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

In `@src/main/resources/application.yml` at line 22, Replace the hardcoded default
in the JWT secret configuration with a required unset fallback so startup fails
when JWT_SECRET is missing, or restrict the development fallback to an
explicitly activated local profile. Ensure non-local environments cannot use the
publicly known secret.

Comment on lines +1 to +4
ALTER TABLE episode_rankings
ALTER COLUMN title_score SET DEFAULT 1000,
DROP CHECK ck_episode_rankings_title_score,
ADD CONSTRAINT ck_episode_rankings_title_score CHECK (title_score >= 100);

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 | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify there is no duplicate Flyway version across migration filenames
fd -e sql . src/main/resources/db/migration | rg -oP '(?<=/)V\d+(?=__)' | sort | uniq -c | sort -rn

Repository: 8th-COKERTHON/server-team6

Length of output: 263


Rename one of the V7 migrations. Flyway requires unique version numbers, and having both src/main/resources/db/migration/V7__add_ranking_period_index.sql and src/main/resources/db/migration/V7__align_episode_ranking_score_bounds.sql will fail startup validation.

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

In `@src/main/resources/db/migration/V7__align_episode_ranking_score_bounds.sql`
around lines 1 - 4, Rename the migration file containing the `ALTER TABLE
episode_rankings` statement so its Flyway version is unique and does not
conflict with `V7__add_ranking_period_index.sql`; preserve the migration
description and SQL contents.

🗄️ Data Integrity & Integration | 🔴 Critical | ⚡ Quick win

Missing backfill before tightening the CHECK constraint.

title_score previously defaulted to 0 with a >= 0 check (V2/V4). Any existing row with title_score between 0–99 will violate the new title_score >= 100 check, and MySQL validates existing data when adding a CHECK constraint — this ALTER TABLE will fail at deploy time if such rows exist.

🛠️ Proposed fix: backfill before tightening the constraint
+UPDATE episode_rankings SET title_score = 1000 WHERE title_score < 100;
+
 ALTER TABLE episode_rankings
     ALTER COLUMN title_score SET DEFAULT 1000,
     DROP CHECK ck_episode_rankings_title_score,
     ADD CONSTRAINT ck_episode_rankings_title_score CHECK (title_score >= 100);
📝 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
ALTER TABLE episode_rankings
ALTER COLUMN title_score SET DEFAULT 1000,
DROP CHECK ck_episode_rankings_title_score,
ADD CONSTRAINT ck_episode_rankings_title_score CHECK (title_score >= 100);
UPDATE episode_rankings SET title_score = 1000 WHERE title_score < 100;
ALTER TABLE episode_rankings
ALTER COLUMN title_score SET DEFAULT 1000,
DROP CHECK ck_episode_rankings_title_score,
ADD CONSTRAINT ck_episode_rankings_title_score CHECK (title_score >= 100);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/resources/db/migration/V7__align_episode_ranking_score_bounds.sql`
around lines 1 - 4, Backfill existing rows before tightening the constraint: in
the migration, update `episode_rankings` rows with `title_score` below 100 to a
valid value (such as 100), then alter the default and replace
`ck_episode_rankings_title_score` with the `title_score >= 100` check.

ALTER TABLE matching_events
ADD COLUMN period_key VARCHAR(20) NULL AFTER event_type,
ADD COLUMN match_type VARCHAR(30) NOT NULL DEFAULT 'RIVAL' AFTER period_key,
ADD COLUMN score_multiplier DECIMAL(4,2) NOT NULL DEFAULT 1.00 AFTER score_reward,

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

score_multiplier has no range check.

Nothing prevents a negative or zero multiplier, which would corrupt score calculations downstream.

🛡️ Suggested constraint
 ALTER TABLE matching_events
     ...
+    ADD CONSTRAINT ck_matching_events_score_multiplier CHECK (score_multiplier > 0),
     ADD CONSTRAINT uk_matching_events_type_period UNIQUE (event_type, period_key);

Also applies to: 13-13

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

In `@src/main/resources/db/migration/V9__extend_matches_and_show_sessions.sql` at
line 4, Update the migration definitions for score_multiplier in both referenced
locations to enforce a strictly positive value with a database CHECK constraint,
while retaining the existing DECIMAL type and default. Ensure the constraint is
applied consistently to every definition of this column.

@pearseona
pearseona merged commit 5b946c4 into main Jul 11, 2026
3 of 4 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.

2 participants