Skip to content

feat(review-loop): AI 코드 리뷰 루프 이식 + 자율 수정 루프(P0/P1) 배선 - #5

Merged
mosungjin merged 4 commits into
developfrom
feat/review-loop-setup
Aug 3, 2026
Merged

feat(review-loop): AI 코드 리뷰 루프 이식 + 자율 수정 루프(P0/P1) 배선#5
mosungjin merged 4 commits into
developfrom
feat/review-loop-setup

Conversation

@dlxodus02

@dlxodus02 dlxodus02 commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

📌 연관 이슈

📝 작업 내용

  • AI 코드 리뷰 루프 이식(엔진 48 main + 30 test): com.wanted.backend → com.module06.backend
  • P0 자율 배선: AutoLoopRunner · AutoLoopOrchestrator · reviewAutoFix 태스크 · scripts/review-autoloop.sh
  • P1 안전막: VerificationPort · CompileVerification(in-JVM javac) · VerifiedFixer(컴파일 실패 시 롤백)
  • 게이트: pre-push 훅(Gate1 ArchUnit은 테스트 없으면 스킵) · CI gate1-semgrep · gate2-judge
  • build.gradle: 루프 deps(anthropic-java·snakeyaml·starter-test) + review-loop 태스크 9종

🖥️ 프론트엔드 연동 가이드 (API 명세)

  • 해당 없음 — 개발 인프라(리뷰 게이트/CLI), FE 노출 엔드포인트 없음

🚨 주요 에러 코드 및 예외

  • 도메인 에러코드 미도입. 루프 런타임 예외는 내부 처리:
    • GEMINI_API_KEY 없음 → graceful 스킵(exit 0, 머지 차단 안 함)
    • 컴파일 실패 → 수정 롤백(무변경) / budget 소진 → 사람 인계

💡 백엔드 리뷰 포인트

  • reviewloop.judge는 Spring 무관 순수 CLI(도메인 계층 밖). judge(찾기)/fixer(고치기)/verify(검증) seam 분리
  • 우려: (1) 자율 루프 라이브 경로는 GEMINI_API_KEY 필요 → 본 PR 미검증 (2) P1 라운드 게이트는 단일파일 javac라 교차 파괴는 커밋 직전 ./gradlew test로 잡음

✅ 체크리스트

  • 코드 컨벤션 준수
  • 불필요 주석·로그 제거
  • reviewloop.judge.* 결정론 테스트 통과(로컬)
  • 예외(키없음/컴파일실패/budget) 처리 검증

Summary by CodeRabbit

  • 새 기능
    • 변경된 Java 코드의 자동 리뷰, 점수화, 심각도 판정 및 감사 로그를 지원합니다.
    • 아키텍처·쿼리 규칙 검사와 CI 게이트를 추가했습니다.
    • Critical·미완성 문제는 push와 CI에서 차단하고, Minor 문제는 수정 요청서로 제공합니다.
    • 자동 수정, 컴파일 검증, 예산 관리 및 선택적 원격 반영을 지원합니다.
    • 오탐·확정·누락 사례를 저장해 이후 리뷰에 활용합니다.
  • 문서
    • 리뷰 게이트, 자동 수정 절차, 운영 정책 및 지식 관리 방법을 문서화했습니다.
  • 테스트
    • 리뷰 루프, 검증, CI 게이트 및 외부 AI 연동 테스트를 추가했습니다.

- 루프 엔진 이식(48 main + 30 test): com.wanted.backend → com.module06.backend
- P0 배선: AutoLoopRunner · AutoLoopOrchestrator · reviewAutoFix 태스크 · scripts/review-autoloop.sh
- P1 안전막: VerificationPort · CompileVerification(in-JVM javac) · VerifiedFixer(컴파일 실패 시 롤백)
- 게이트: pre-push 훅(Gate1 ArchUnit skip-if-absent) · CI gate1-semgrep · gate2-judge
- build.gradle: 루프 deps(anthropic-java·snakeyaml·starter-test) + review-loop 태스크 9종

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

로컬 훅과 CI가 변경 Java 파일을 대상으로 결정론적 검사와 LLM Judge를 실행한다. Java 리뷰 루프는 findings를 검증하고 점수화한다. 자동 수정 루프는 예산과 컴파일 검증을 적용한다. 감사 로그와 교훈 저장소가 실행 결과를 기록한다.

Changes

리뷰 루프 인프라

Layer / File(s) Summary
리뷰 계약과 판정 파이프라인
src/main/java/com/module06/backend/reviewloop/judge/*, review-loop/rules.yaml
Finding, RuleCatalog, ReviewLoop, JudgeScorer 및 Gemini·Claude 어댑터를 추가했다. Findings는 근거와 규칙 severity를 검증한 뒤 점수와 JudgeDecision으로 변환된다.
자동 수정과 검증
src/main/java/com/module06/backend/reviewloop/judge/Auto*.java, *Fixer*.java, *Verification*.java, scripts/review-autoloop.sh, scripts/review-verify.sh
자동 수정 라운드, 파일·전역 예산, Gemini 수정, 컴파일 검증, 실패 시 원본 복원을 구현했다.
감사·지식·운영 산출물
src/main/java/com/module06/backend/reviewloop/judge/Audit*.java, KnowledgeStore.java, Lesson*.java, ReviewReport.java, scripts/review-*.sh
감사 기록과 교훈을 JSONL로 저장한다. 사람 승인, 정확도 집계, 반복 패턴 탐지, 수정 요청서 및 리포트 생성을 추가했다.
Git 훅과 CI 게이트
.githooks/pre-push, .github/workflows/*, review-loop/semgrep/query.yml, build.gradle
pre-push가 변경 Java 파일을 수집하고 ArchUnit과 Gate 2 Judge를 순차 실행한다. CI는 신규 @Query, 결정론 테스트, 변경 파일 Judge 및 선택적 라이브 테스트를 실행한다.
검증 테스트와 예시
src/test/java/com/module06/backend/reviewloop/judge/*, review-loop/golden/*
결정론 테스트가 판정, 예산, 저장, 검증, 지식 루프를 확인한다. 조건부 Gemini·Claude 라이브 테스트와 Judge 입력 예시를 추가했다.

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

Sequence Diagram(s)

sequenceDiagram
  participant Developer
  participant PrePushHook
  participant ReviewLoopRunner
  participant GeminiJudgeAdapter
  participant JudgeScorer
  participant GitPush
  Developer->>PrePushHook: git push
  PrePushHook->>ReviewLoopRunner: 변경 Java 파일 목록
  ReviewLoopRunner->>GeminiJudgeAdapter: review(filePath, code, policy)
  GeminiJudgeAdapter-->>ReviewLoopRunner: findings
  ReviewLoopRunner->>JudgeScorer: score(findings)
  JudgeScorer-->>PrePushHook: 판정 결과
  PrePushHook->>GitPush: push 허용 또는 차단
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.98% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목은 AI 코드 리뷰 루프 이식과 자율 수정 루프 배선이라는 PR의 핵심 변경을 간결하게 설명합니다.
Linked Issues check ✅ Passed 변경 사항은 리뷰 루프 이식, 게이트·태스크·CI 구성, 컴파일 검증·롤백·예산 처리를 포함하여 연결 이슈 #4의 목표를 충족합니다.
Out of Scope Changes check ✅ Passed 추가된 엔진, 테스트, 스크립트, 훅, CI, 규칙 및 설계 문서는 연결 이슈 #4의 개발 인프라 범위와 관련됩니다.
✨ 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/review-loop-setup

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown

Gate 2 · 결정론 판정

95 tests   89 ✅  0s ⏱️
30 suites   6 💤
30 files     0 ❌

Results for commit 8382a60.

♻️ This comment has been updated with latest results.

@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

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

🟡 Minor comments (12)
review-loop/AUTOLOOP_DESIGN.md-28-60 (1)

28-60: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

이전 패키지명을 현재 패키지명으로 바꾸세요.

Line 30과 Line 57의 com.wanted.backend.reviewloop.judge.AutoLoopRunner는 이 PR의 이식 대상과 다릅니다. 문서와 Gradle 예시를 복사하면 AutoLoopRunner를 찾지 못합니다. 두 위치를 com.module06.backend.reviewloop.judge.AutoLoopRunner로 변경하세요.

🤖 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 `@review-loop/AUTOLOOP_DESIGN.md` around lines 28 - 60, 문서의 두 AutoLoopRunner
참조가 현재 패키지명을 사용하도록 수정하세요.
`com.wanted.backend.reviewloop.judge.AutoLoopRunner#main`과 Gradle `mainClass` 값을
모두 `com.module06.backend.reviewloop.judge.AutoLoopRunner`로 변경하고, 나머지 배선과 옵션은
유지하세요.
review-loop/UNIFIED_DESIGN.md-158-169 (1)

158-169: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

검증 명령을 실제 스크립트 호출로 바꾸세요.

Line 82-84는 Gradle 래퍼를 만들지 않는다고 설명합니다. 그러나 Line 164와 Line 167은 ./gradlew reviewVerify를 실행합니다. 현재 scripts/review-verify.sh--files-from도 받지 않습니다. 이 절차를 따르면 드라이버 검증 단계가 실패합니다.

수정 예시
-5. 검증             ./gradlew reviewVerify --args="--files-from <changed>"
+5. 검증             bash scripts/review-verify.sh
@@
-7. 최종 검증        ./gradlew reviewVerify --args="--files-from <changed> --with-test"
+7. 최종 검증        bash scripts/review-verify.sh --with-test
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@review-loop/UNIFIED_DESIGN.md` around lines 158 - 169, Update the review
workflow steps 5 and 7 to invoke the existing scripts/review-verify.sh directly
instead of the unavailable ./gradlew reviewVerify wrapper, preserving the
--files-from <changed> argument and adding --with-test for final verification.
Ensure the documented command matches the script’s supported options.
src/main/java/com/module06/backend/reviewloop/judge/ReviewRunner.java-34-58 (1)

34-58: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

초기 소진 예산의 AuditSummary 계약을 명시하세요. ReviewBudget(0) 또는 이미 소진된 budget이면 루프를 실행하지 않고 finalVerdict() == null, roundsUsed() == 0AuditSummary를 반환합니다. 예산이 최소 한 라운드를 요구하면 입력을 거부하고, 0라운드를 허용하면 nullable 결과를 문서화하고 소비자와 회귀 테스트에서 처리하세요.

🤖 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/module06/backend/reviewloop/judge/ReviewRunner.java` around
lines 34 - 58, Update ReviewRunner.run and its AuditSummary contract to
explicitly handle an initially exhausted ReviewBudget: return an AuditSummary
with finalVerdict() null and roundsUsed() 0 without invoking reviewer.review or
consuming budget. Ensure the minimum-round budget configuration is rejected when
zero rounds are disallowed; otherwise document and handle the nullable verdict
in consumers and add regression coverage.
scripts/review-fix.sh-10-10 (1)

10-10: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

사용 예시의 패키지 경로를 갱신하세요.

이 PR은 com.wanted.backend에서 com.module06.backend로 이식합니다. Line 10의 예시 경로는 여전히 com/wanted/backend를 가리킵니다. 존재하지 않는 경로를 안내하면 사용자가 잘못된 인자를 전달합니다.

🛠️ 제안 수정
-#       bash scripts/review-fix.sh --path src/main/java/com/wanted/backend/domain/cart --domain cart
+#       bash scripts/review-fix.sh --path src/main/java/com/module06/backend/domain/cart --domain cart
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/review-fix.sh` at line 10, Update the example command comment in
review-fix.sh to replace the outdated com/wanted/backend package path with the
migrated com/module06/backend path, while preserving the existing cart domain
example.
src/main/java/com/module06/backend/reviewloop/judge/ReviewLoopCli.java-78-81 (1)

78-81: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

build 디렉터리를 먼저 생성하세요.

Line 80은 build/review-cli-screen.txt에 바로 씁니다. build/가 없으면 NoSuchFileException이 발생합니다. ./gradlew clean 직후나 JVM으로 직접 실행할 때 이 경로가 없을 수 있습니다. Line 16의 logsDir 처리와 동일하게 부모 디렉터리를 보장하세요.

🛠️ 제안 수정
         String screen = s.toString();
         System.out.println(screen);                                  // 터미널 출력
-        Files.writeString(Path.of("build/review-cli-screen.txt"), screen);  // 깨끗한 확인용
+        Path screenFile = Path.of("build/review-cli-screen.txt");
+        Files.createDirectories(screenFile.getParent());
+        Files.writeString(screenFile, screen);  // 깨끗한 확인용
🤖 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/module06/backend/reviewloop/judge/ReviewLoopCli.java`
around lines 78 - 81, Update the file-writing flow around Files.writeString in
ReviewLoopCli so the parent build directory is created or otherwise guaranteed
to exist before writing build/review-cli-screen.txt. Match the existing logsDir
directory-initialization approach, while preserving the current screen output
and file contents.
review-loop/knowledge/README.md-3-13 (1)

3-13: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

ReviewLoopCli의 교훈 저장 경로를 정본으로 통일하세요. ReviewLoopClireview-loop/logs/lessons.jsonl에 저장하지만, ReviewLoopRunner(Gate 2)와 reviewLessonReviewLoopPaths.LESSONSreview-loop/knowledge/lessons.jsonl을 사용합니다. ReviewLoopCli에서 ReviewLoopPaths.LESSONS를 사용하도록 수정하세요. 현재 .gitignore에는 review-loop/logs/ 규칙도 없으므로 README의 설명과 ignore 규칙을 일치시키세요.

🤖 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 `@review-loop/knowledge/README.md` around lines 3 - 13,
ReviewLoopCliの教訓保存先をReviewLoopPaths.LESSONSに統一し、review-loop/knowledge/lessons.jsonlへ保存するよう更新してください。あわせてREADMEのlogs/に関する説明と.gitignoreの設定を確認し、review-loop/logs/を揮発性ログとして無視する規則を追加して記述と実際の挙動を一致させてください。
src/main/java/com/module06/backend/reviewloop/judge/CompileVerification.java-36-52 (1)

36-52: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

임시 출력 디렉터리를 삭제하지 않습니다.

verify 호출마다 라인 38이 새 임시 디렉터리를 만듭니다. 그 디렉터리와 생성된 .class 파일은 삭제되지 않습니다. 루프는 파일마다 여러 라운드를 돌므로 실행마다 임시 디렉터리가 누적됩니다. 검증 후 디렉터리를 정리하십시오.

🧹 제안: 검증 후 임시 디렉터리 정리
         String classpath = System.getProperty("java.class.path", "");
+        Path outDir = null;
         try {
-            Path outDir = Files.createTempDirectory("autoloop-javac");
+            outDir = Files.createTempDirectory("autoloop-javac");
             StringWriter diag = new StringWriter();
             try (StandardJavaFileManager fm =
                          compiler.getStandardFileManager(null, null, StandardCharsets.UTF_8)) {
                 Iterable<? extends JavaFileObject> units = fm.getJavaFileObjects(filePath.toFile());
                 List<String> options = List.of(
                         "-classpath", classpath,
                         "-d", outDir.toString(),
                         "-encoding", "UTF-8");
                 boolean ok = compiler.getTask(diag, fm, null, options, null, units).call();
                 return new VerifyResult(ok, diag.toString());
             }
         } catch (IOException e) {
             return new VerifyResult(false, "컴파일 게이트 IO 오류: " + e.getMessage());
+        } finally {
+            deleteQuietly(outDir);
         }
     }
+
+    private static void deleteQuietly(Path dir) {
+        if (dir == null) {
+            return;
+        }
+        try (var paths = Files.walk(dir)) {
+            paths.sorted(java.util.Comparator.reverseOrder()).forEach(p -> {
+                try {
+                    Files.deleteIfExists(p);
+                } catch (IOException ignored) {
+                    // 임시 파일 정리 실패는 검증 결과에 영향을 주지 않는다.
+                }
+            });
+        } catch (IOException ignored) {
+            // 정리 실패 무시
+        }
+    }
🤖 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/module06/backend/reviewloop/judge/CompileVerification.java`
around lines 36 - 52, Update CompileVerification.verify to always delete the
temporary outDir and its generated class files after compilation completes,
including when compilation or verification fails. Use guaranteed cleanup around
the existing compiler task while preserving the current VerifyResult and
diagnostic handling.
src/main/java/com/module06/backend/reviewloop/judge/AutoFixRunner.java-41-63 (1)

41-63: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

AutoFixRunner의 소진된 ReviewBudget 결과를 null-safe하게 처리하십시오.

ReviewBudget가 이미 소진되면 run은 판정 없이 finalVerdict == null인 결과를 반환합니다. AutoLoopOrchestrator는 이 호출을 건너뛰고 finalVerdict()도 검사하지만, 다른 호출자는 null을 역참조할 수 있습니다. 소진된 budget으로 호출하지 않거나 명시적 verdict를 반환하십시오. null을 반환하는 조기 반환만 추가하면 문제는 남습니다.

🤖 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/module06/backend/reviewloop/judge/AutoFixRunner.java`
around lines 41 - 63, Update AutoFixRunner.run so an already-exhausted
ReviewBudget cannot produce an AutoFixResult with a null verdict: either
reject/avoid the call before entering the loop or return an explicit terminal
verdict. Ensure every return path, including the final path after the loop,
provides a non-null finalVerdict rather than adding only a null-return guard.
src/test/java/com/module06/backend/reviewloop/judge/GeminiJudgeAdapterLiveTest.java-38-40 (1)

38-40: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

build/ 디렉터리가 없으면 결과 파일 쓰기가 실패합니다.

build/gemini-findings.txt에 쓰기 전에 부모 디렉터리 존재를 보장하지 않습니다. Gradle 태스크로 실행하면 build/가 이미 존재하지만, 다른 실행 경로(IDE 등)에서는 존재하지 않을 수 있습니다. 이 경우 실제 API 호출과 finding 검증은 성공했는데도 파일 쓰기 단계에서 NoSuchFileException이 발생해 테스트가 실패합니다.

ReviewLoopRunner.writeFindings처럼 부모 디렉터리를 먼저 생성해 주십시오. 함께, 이미 import된 Files, Path를 두고 전체 한정 이름을 쓴 부분도 정리해 주십시오.

🔧 제안하는 수정
-        // 데모: 실제로 뭐라고 잡았는지 파일로 남긴다(확인용).
-        java.nio.file.Files.writeString(java.nio.file.Path.of("build/gemini-findings.txt"),
-                findings.stream().map(Object::toString).collect(java.util.stream.Collectors.joining("\n")));
+        // 데모: 실제로 뭐라고 잡았는지 파일로 남긴다(확인용).
+        Path out = Path.of("build/gemini-findings.txt");
+        Files.createDirectories(out.getParent());
+        Files.writeString(out,
+                findings.stream().map(Object::toString).collect(java.util.stream.Collectors.joining("\n")));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/test/java/com/module06/backend/reviewloop/judge/GeminiJudgeAdapterLiveTest.java`
around lines 38 - 40, Update the findings-file write in
GeminiJudgeAdapterLiveTest to create the parent directory before calling
writeString, matching ReviewLoopRunner.writeFindings behavior so direct or IDE
execution succeeds when build is absent. Reuse the existing Files and Path
imports instead of fully qualified names.
src/main/java/com/module06/backend/reviewloop/judge/DriverBudget.java-31-32 (1)

31-32: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

.git 파일을 해석하여 Git 경로를 구성하십시오. 이 프로젝트는 별도 worktree 사용을 권장합니다. worktree와 submodule에서는 .git이 파일이므로 현재 코드는 "(unknown)"을 반환하거나 상태 저장에 실패합니다.

🤖 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/module06/backend/reviewloop/judge/DriverBudget.java` around
lines 31 - 32, DriverBudget의 STATE_FILE 및 HEAD_FILE을 고정된 .git 하위 경로로 만들지 말고,
.git이 디렉터리인지 파일인지 확인해 실제 Git 디렉터리를 해석하도록 수정하십시오. .git 파일의 gitdir 경로를 읽어
worktree와 submodule에서도 HEAD 조회 및 상태 저장이 동일하게 동작하게 하며, 일반 저장소의 디렉터리 형태도 유지하십시오.
src/main/java/com/module06/backend/reviewloop/judge/ReviewLoopRunner.java-116-118 (1)

116-118: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

리포트 출력 전에 상위 디렉터리를 만드세요.

Line 118은 build/reviewloop-run.txt에 직접 씁니다. build 디렉터리가 없으면 NoSuchFileException이 발생합니다. 그 경우 LLM 호출을 모두 마친 뒤에 러너가 실패하고, 게이트가 자기 IO 오류로 push를 막습니다. Line 173-176의 방어 방식과 동일하게 디렉터리를 먼저 생성하세요.

🐛 제안 수정
         String report = out.toString();
         System.out.println(report);
-        Files.writeString(Path.of("build/reviewloop-run.txt"), report);
+        Path reportFile = Path.of("build/reviewloop-run.txt");
+        Files.createDirectories(reportFile.getParent());
+        Files.writeString(reportFile, report);
🤖 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/module06/backend/reviewloop/judge/ReviewLoopRunner.java`
around lines 116 - 118, Before the Files.writeString call in ReviewLoopRunner,
create the parent directory for build/reviewloop-run.txt using the same
defensive directory-creation approach already used around lines 173-176. Ensure
report generation and output remain unchanged while preventing failure when the
build directory does not exist.
src/main/java/com/module06/backend/reviewloop/judge/OptionPanel.java-6-9 (1)

6-9: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

OptionPanel의 불변성을 보장하고 빈 응답 경로를 분리하세요.

optionsList.copyOf(options)로 방어 복사하세요. 정상 패널에서는 recommendation.pick()이 옵션의 letter와 일치하는지 검증하세요. 단, GeminiOptionPanelAdapter.parsePanel은 빈 옵션과 빈 pick을 응답 없음 상태로 생성하므로, 이 상태를 별도로 표현하거나 해당 경로에서 OptionPanel을 생성하지 않도록 수정해야 합니다.

🤖 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/module06/backend/reviewloop/judge/OptionPanel.java` around
lines 6 - 9, OptionPanel의 options를 List.copyOf(options)로 방어 복사해 불변성을 보장하세요. 정상
패널 생성 시 recommendation.pick()이 options의 PanelOption letter와 일치하는지 검증하고,
GeminiOptionPanelAdapter.parsePanel의 빈 options·빈 pick 응답 없음 경로는 별도 상태로 표현하거나
OptionPanel 생성을 건너뛰도록 수정하세요.
🧹 Nitpick comments (15)
src/main/java/com/module06/backend/reviewloop/judge/ReviewLessonRecorder.java (1)

52-69: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

시스템 기본 시계를 직접 사용해 팀 컨벤션과 다르다.

Line 68에서 LocalDateTime.now()를 직접 호출한다. ReviewRunner의 주석(Line 15)은 "시각은 팀 컨벤션(ClockConfig)에 맞춰 주입된 Clock을 쓴다"고 명시한다. 이 파일은 그 컨벤션을 따르지 않는다.

lessons.jsonl은 팀 전체가 공유하고 git으로 추적하는 파일이다(ReviewLoopPaths.java Line 10 참고). 기여자마다 로컬 시스템 시간대가 다르면 기록된 시각이 서로 어긋나 리포트의 시간 순서 해석이 혼란스러워진다.

Instant.now()(UTC 기준) 사용 또는 Clock 주입으로 통일하는 것을 제안한다. 이 저장소의 CONV_001 규칙(재발명 금지)이 정확히 이런 패턴을 겨냥한다는 점도 참고한다.

♻️ 제안하는 수정
-        return new Lesson(LocalDateTime.now().toString(), rule.strip(), kind, note.strip());
+        return new Lesson(java.time.Instant.now().toString(), rule.strip(), kind, note.strip());
🤖 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/module06/backend/reviewloop/judge/ReviewLessonRecorder.java`
around lines 52 - 69, Update the timestamp creation in
ReviewLessonRecorder.parse to follow the repository’s ClockConfig convention
instead of calling LocalDateTime.now() directly. Reuse the existing injected or
configured Clock, or use Instant.now() in UTC if that is the established project
pattern, while preserving the Lesson construction and argument parsing behavior.
src/main/java/com/module06/backend/reviewloop/judge/ReviewReport.java (1)

47-55: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

humanNote에 개행 문자가 있으면 목록 서식이 깨질 수 있다.

l.humanNote()ReviewLessonRecorder--note-file 옵션을 통해 여러 줄 텍스트를 담을 수 있다(strip()은 선행·후행 공백만 제거하고 내부 개행은 유지한다). 여러 줄 노트가 하나의 마크다운 불릿 항목(Line 52-54)에 그대로 삽입되면, 후속 줄이 불릿 접두사 없이 렌더링되어 리포트 가독성이 떨어진다.

내부 개행을 공백으로 치환하는 것을 제안한다.

♻️ 제안하는 수정
             for (Lesson l : lessons) {
                 md.append("- **[").append(l.kind()).append("]** `").append(l.ruleId())
-                  .append("` — ").append(l.humanNote()).append('\n');
+                  .append("` — ").append(l.humanNote().replace("\n", " ").replace("\r", "")).append('\n');
             }
🤖 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/module06/backend/reviewloop/judge/ReviewReport.java` around
lines 47 - 55, Update the lesson rendering loop in ReviewReport so each
l.humanNote() is normalized by replacing internal newline characters with spaces
before appending it to the Markdown list item. Preserve the existing note
content and formatting for single-line notes.
src/main/java/com/module06/backend/reviewloop/judge/RepeatedPatternDetector.java (1)

23-31: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

동일 count에서 정렬 순서를 고정하세요.

countsHashMap입니다. count가 같은 규칙이 둘 이상이면 결과 순서가 실행마다 달라질 수 있습니다. 클래스 주석은 결정론을 명시하고, ReviewLoopCli는 이 순서를 그대로 화면에 출력합니다. ruleId를 2차 정렬 키로 추가하면 순서가 고정됩니다.

♻️ 제안 리팩터
         return counts.entrySet().stream()
                 .filter(e -> e.getValue() >= threshold)
                 .map(e -> new Alert(e.getKey(), e.getValue()))
-                .sorted((a, b) -> Long.compare(b.count(), a.count()))
+                .sorted(Comparator.comparingLong(Alert::count).reversed()
+                        .thenComparing(Alert::ruleId))
                 .toList();

java.util.Comparator 임포트를 추가하세요.

🤖 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/module06/backend/reviewloop/judge/RepeatedPatternDetector.java`
around lines 23 - 31, Update RepeatedPatternDetector.detect by adding ruleId as
a secondary ascending sort key after descending count, using Comparator as
needed, so alerts with equal counts always have deterministic ordering.
scripts/review-fix.sh (1)

17-20: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

리포지토리 루트로 이동한 뒤 ./gradlew를 호출하세요.

Line 31과 Line 41은 상대 경로 ./gradlew$DIR/review-fix-apply.sh를 사용합니다. 사용자가 하위 디렉터리에서 이 스크립트를 실행하면 ./gradlew를 찾지 못합니다. scripts/review-score-domains.sh(Line 22-23)는 루트로 이동합니다. 동일한 방식을 적용하면 동작이 일관됩니다.

♻️ 제안 리팩터
 DIR="$(cd "$(dirname "$0")" && pwd)"
+ROOT="$(cd "$DIR/.." && pwd)"
+cd "$ROOT" || exit 1
 TARGET_ARGS="$*"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/review-fix.sh` around lines 17 - 20, Update scripts/review-fix.sh to
change into the repository root before invoking the relative ./gradlew and
$DIR/review-fix-apply.sh commands, following the existing root-resolution
approach used by review-score-domains.sh while preserving the script directory
references.
scripts/review-score-domains.sh (1)

80-98: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

eval 대신 파일이나 read로 집계 값을 받으세요.

Line 81은 awk 출력을 eval로 재파싱합니다. 현재 awkprintf 포맷은 모두 숫자로 강제 변환하므로 즉각적인 주입 경로는 없습니다. 그러나 $out은 LLM 판정 출력이며 신뢰 경계 밖입니다. 포맷이 나중에 %s로 바뀌면 임의 명령 실행이 가능해집니다. read로 값을 받으면 이 위험이 사라집니다. Shellcheck의 SC2154 경고(Line 96, 104)도 함께 해소됩니다.

♻️ 제안 리팩터
-  eval "$(awk '
+  read -r files avg low nr crit fnd < <(awk '
     / score / {
       files++
       for (i = 1; i <= NF; i++) {
         if ($i == "score")    { s = $(i+1) + 0; sum += s; if (s < 80) low++ }
         if ($i == "findings") { fnd += $(i+1) + 0 }
       }
       if ($0 ~ /NEEDS_REVISION/) nr++
       if ($0 ~ /AWAITING_HUMAN/) crit++
     }
     END {
-      printf "files=%d avg=%s low=%d nr=%d crit=%d fnd=%d\n",
+      printf "%d %s %d %d %d %d\n",
              files, (files ? sprintf("%.1f", sum / files) : 0), low, nr, crit, fnd
-    }' "$out")"
+    }' "$out")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/review-score-domains.sh` around lines 80 - 98, Replace the eval-based
assignment around the awk aggregation with safe read-based parsing of its fixed
output, assigning files, avg, low, nr, crit, and fnd directly without executing
command text. Preserve the existing numeric aggregation and zero-files handling,
and ensure the change resolves the SC2154 warnings for these variables.

Source: Linters/SAST tools

src/test/java/com/module06/backend/reviewloop/judge/GeminiAutoFixLoopLiveTest.java (1)

28-65: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

세 개의 Gemini 라이브 테스트에 타임아웃을 추가하십시오. 세 파일 모두 @EnabledIfEnvironmentVariable(named = "GEMINI_API_KEY", ...)로 게이트되지만, 실제 외부 API 호출에 시간 제한을 두지 않는다. API가 응답하지 않으면 라이브 게이트 잡이 무한정 대기할 수 있다.

  • src/test/java/com/module06/backend/reviewloop/judge/GeminiAutoFixLoopLiveTest.java#L28-L65: autoFixLoopEndToEnd@Timeout 애노테이션을 추가하십시오. AutoFixRunner.run은 예산 소진까지 반복하므로 API 무응답 시 대기 시간이 특히 길어질 수 있습니다.
  • src/test/java/com/module06/backend/reviewloop/judge/GeminiOptionPanelLiveTest.java#L19-L45: generatesPanel@Timeout 애노테이션을 추가하십시오.
  • src/test/java/com/module06/backend/reviewloop/judge/GeminiReviewLoopLiveTest.java#L25-L57: fullLoopEndToEnd@Timeout 애노테이션을 추가하십시오.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/test/java/com/module06/backend/reviewloop/judge/GeminiAutoFixLoopLiveTest.java`
around lines 28 - 65, 세 개의 Gemini 라이브 테스트에 외부 API 무응답을 제한할 수 있는 유한한 JUnit
`@Timeout을` 추가하십시오.
src/test/java/com/module06/backend/reviewloop/judge/GeminiAutoFixLoopLiveTest.java:28-65의
autoFixLoopEndToEnd,
src/test/java/com/module06/backend/reviewloop/judge/GeminiOptionPanelLiveTest.java:19-45의
generatesPanel,
src/test/java/com/module06/backend/reviewloop/judge/GeminiReviewLoopLiveTest.java:25-57의
fullLoopEndToEnd에 적용하고, 필요한 Timeout import와 모든 테스트에 일관된 적절한 제한 시간을 사용하십시오.
src/test/java/com/module06/backend/reviewloop/judge/OptionPanelTest.java (1)

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

테스트가 라우팅을 검증하지 않습니다.

selectedActionRoutesPanelOptionaction() 필드 값만 확인합니다. 라우팅 동작은 실행하지 않습니다. DisplayName은 "다음 처리가 라우팅된다"라고 설명하므로 테스트 내용과 설명이 일치하지 않습니다. 선택한 PanelAction을 처리 경로에 넣고 결과를 확인하도록 보강하거나, 설명을 필드 검증으로 정정하십시오.

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

In `@src/test/java/com/module06/backend/reviewloop/judge/OptionPanelTest.java`
around lines 38 - 44, Update selectedActionRoutes so it either passes the chosen
PanelAction through the actual routing/handling path and asserts the resulting
behavior, or rename its DisplayName to accurately describe validating the
PanelOption action field; prefer exercising the routing path if the relevant
handler is available.
src/main/java/com/module06/backend/reviewloop/judge/AutoLoopRunner.java (1)

84-107: 🔒 Security & Privacy | 🔵 Trivial | 💤 Low value

대상 파일 경로에 상한 필터가 없습니다.

loadTargets는 목록의 각 줄을 그대로 Path로 변환합니다. .java 확장자와 존재 여부만 검사합니다. 목록 파일이 예상 밖 경로(예: ../ 상위 경로, 저장소 밖 절대 경로)를 담으면 루프가 저장소 밖 파일을 수정할 수 있습니다. 현재는 스크립트가 git diff 결과로 목록을 만들므로 실제 위험은 낮습니다. 방어 차원에서 저장소 루트 하위 경로만 허용하는 검사를 추가하십시오.

♻️ 제안: 저장소 루트 하위 경로만 허용
+        Path root = Path.of("").toAbsolutePath().normalize();
         List<Path> files = new ArrayList<>();
         for (String raw : Files.readAllLines(list)) {
             String s = raw.replace("", "").trim();   // BOM/공백 방어
             if (s.isBlank() || !s.endsWith(".java")) {
                 continue;
             }
             Path p = Path.of(s);
-            if (Files.exists(p)) {
+            if (!p.toAbsolutePath().normalize().startsWith(root)) {
+                System.out.println("[autoloop] 저장소 밖 경로 스킵: " + s);
+                continue;
+            }
+            if (Files.exists(p)) {
                 files.add(p);
             }
         }
🤖 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/module06/backend/reviewloop/judge/AutoLoopRunner.java`
around lines 84 - 107, Update loadTargets to resolve each listed path against
the repository root and accept it only when its normalized absolute path remains
within that root; skip absolute paths and traversal entries that escape the root
while preserving the existing .java, existence, and maxFiles filtering.
src/test/java/com/module06/backend/reviewloop/judge/ReviewLessonRecorderTest.java (1)

60-75: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

--kind 누락과 MISSED 경로가 없습니다.

현재 테스트는 CONFIRMED, FALSE_POSITIVE, 잘못된 kind만 다룹니다. ReviewLessonRecorder.parse--kind가 없으면 LessonKind.valueOf("")로 예외를 던집니다. 그 경로와 MISSED kind를 추가로 검증하면 CLI 계약이 완결됩니다.

💚 제안: 누락 케이스 추가
+    `@Test`
+    `@DisplayName`("--kind 누락은 거부한다")
+    void rejectsMissingKind() {
+        assertThatThrownBy(() -> ReviewLessonRecorder.parse(new String[]{
+                "--rule", "CONV_001", "--note", "x"}))
+                .isInstanceOf(IllegalArgumentException.class);
+    }
+
+    `@Test`
+    `@DisplayName`("MISSED도 유효한 kind다")
+    void acceptsMissedKind() throws IOException {
+        Lesson lesson = ReviewLessonRecorder.parse(new String[]{
+                "--rule", "CONV_001", "--kind", "MISSED", "--note", "x"});
+        assertThat(lesson.kind()).isEqualTo(LessonKind.MISSED);
+    }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/test/java/com/module06/backend/reviewloop/judge/ReviewLessonRecorderTest.java`
around lines 60 - 75, Review the tests around ReviewLessonRecorder.parse and add
coverage for the MISSED kind, asserting it produces LessonKind.MISSED, plus a
case where --kind is omitted, asserting the parser rejects the input with the
expected IllegalArgumentException and message. Preserve the existing CONFIRMED
and unknown-kind coverage.
src/main/java/com/module06/backend/reviewloop/judge/GeminiLessonDraftAdapter.java (1)

51-56: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

일시적 오류와 빈 응답을 구분하지 않습니다.

라인 52는 모든 비-2xx 응답을 즉시 예외로 만듭니다. 429(rate limit)와 5xx는 재시도로 회복 가능합니다. 또한 parseNote는 응답에 parts가 없으면 빈 문자열을 반환합니다. 그 결과 빈 humanNote를 가진 Lesson이 승인 경로로 들어갑니다. 두 경우를 명시적으로 다루십시오.

  • 429/503에는 짧은 지수 백오프 재시도를 적용하십시오.
  • 빈 노트에는 예외를 던지거나 호출자가 판별할 수 있는 값을 반환하십시오.
♻️ 제안: 빈 노트 차단
             String note = parseNote(response.body());
+            if (note.isBlank()) {
+                throw new IllegalStateException("Gemini 교훈 초안이 비어 있습니다.");
+            }
             return new Lesson("(draft)", finding.ruleId(), kind, note);
🤖 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/module06/backend/reviewloop/judge/GeminiLessonDraftAdapter.java`
around lines 51 - 56, 응답 처리에서 429 및 503을 일시적 오류로 분류하고 짧은 지수 백오프로 제한된 재시도를
적용하십시오. 그 외 비-2xx 응답은 기존처럼 명확한 예외로 처리하고, parseNote 결과가 빈 문자열이면 Lesson을 생성하지 말고
예외를 던지거나 호출자가 실패를 판별할 수 있는 값으로 반환하십시오.
src/main/java/com/module06/backend/reviewloop/judge/GeminiCodeFixerAdapter.java (1)

89-97: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

parseFixedCode의 폴백 동작은 안전하지만, 잘린 응답을 감지하지 못합니다.

finishReasonMAX_TOKENS인 경우에도 이 메서드는 잘린 text를 그대로 반환합니다(빈 문자열이 아니므로 Line 96의 isBlank() 검사를 통과함). 잘린 코드가 컴파일 실패로 이어지면 VerifiedFixer의 롤백이 이를 잡아낼 가능성이 높지만, 근본 원인은 위 maxOutputTokens 미설정입니다. 위 코멘트의 수정과 함께, 필요하다면 응답의 finishReason을 확인해 MAX_TOKENS일 때 원본 코드로 폴백하는 방어 로직을 추가하는 것을 고려하십시오.

🤖 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/module06/backend/reviewloop/judge/GeminiCodeFixerAdapter.java`
around lines 89 - 97, Update parseFixedCode to inspect the candidate response’s
finishReason before returning generated text, and return fallback when it is
MAX_TOKENS. Preserve the existing empty or blank-text fallback behavior and only
apply this additional guard to truncated responses.
src/main/java/com/module06/backend/reviewloop/judge/DriverBudget.java (1)

62-70: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

load-apply-save 사이에 원자성 보장이 없습니다.

main()load(), applied(), save()를 순차 실행하지만 파일 잠금이 없습니다. 두 프로세스가 동시에 실행되면 한쪽의 증가분이 유실될 수 있습니다(TOCTOU). 이 도구는 드라이버가 라운드마다 순차 호출하는 설계라 실제 발생 가능성은 낮지만, 예산 추적은 무한 루프 방지가 목적이므로 견고성을 높이려면 FileLock으로 read-modify-write 구간을 보호하는 것을 고려해 주십시오.

Also applies to: 84-97, 99-102

🤖 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/module06/backend/reviewloop/judge/DriverBudget.java` around
lines 62 - 70, Protect the entire load-apply-save read-modify-write sequence in
main() with an exclusive FileLock on the budget state file, keeping the lock
held through load(), applied(), and save(). Ensure the lock is released reliably
via the existing Java resource-management pattern while preserving rendering and
exhausted-status behavior.
src/main/java/com/module06/backend/reviewloop/judge/ClaudeJudgeAdapter.java (1)

77-91: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

toUpperCaseLocale.ROOT를 지정하세요.

기본 로케일 의존 변환은 터키어 로케일에서 iİ로 바꿉니다. 그 경우 "high" 입력이 파싱에 실패하고 조용히 Confidence.LOW로 떨어집니다. Locale.ROOT를 쓰면 결과가 로케일과 무관해집니다.

♻️ 제안 수정
+import java.util.Locale;
+
     private Severity parseSeverity(String raw) {
         try {
-            return Severity.valueOf(raw == null ? "" : raw.trim().toUpperCase());
+            return Severity.valueOf(raw == null ? "" : raw.trim().toUpperCase(Locale.ROOT));
         } catch (IllegalArgumentException e) {
             return Severity.MINOR;   // 알 수 없으면 보수적으로 MINOR (Critical 오판 방지)
         }
     }
 
     private Confidence parseConfidence(String raw) {
         try {
-            return Confidence.valueOf(raw == null ? "" : raw.trim().toUpperCase());
+            return Confidence.valueOf(raw == null ? "" : raw.trim().toUpperCase(Locale.ROOT));
         } catch (IllegalArgumentException e) {
             return Confidence.LOW;
         }
     }
🤖 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/module06/backend/reviewloop/judge/ClaudeJudgeAdapter.java`
around lines 77 - 91, Update the toUpperCase calls in parseSeverity and
parseConfidence to use Locale.ROOT, ensuring normalized enum inputs parse
consistently regardless of the JVM’s default locale.
src/main/java/com/module06/backend/reviewloop/judge/ReviewLoopRunner.java (1)

86-108: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

파일별 리뷰 실패를 격리하세요.

loop.review는 LLM 호출을 포함합니다. 한 파일에서 예외가 발생하면 루프가 중단되고 나머지 파일은 리뷰되지 않습니다. pre-push 훅에서는 일시적 API 오류가 push 차단으로 이어집니다. 파일별로 예외를 잡고, 실패를 리포트에 기록한 뒤 다음 파일을 계속 처리하세요.

🤖 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/module06/backend/reviewloop/judge/ReviewLoopRunner.java`
around lines 86 - 108, 각 파일의 ReviewLoopRunner 처리에서 loop.review 호출과 후속 결과 처리를 파일별
try-catch로 감싸세요. 예외가 발생한 파일은 실패 내용을 out 리포트와 audit에 기록하고 blocked 상태를 적절히 반영한 뒤,
예외를 전파하지 말고 다음 targets 항목으로 계속 진행하도록 수정하세요.
src/main/java/com/module06/backend/reviewloop/judge/GeminiJudgeAdapter.java (1)

26-163: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Gemini REST 호출 골격이 두 어댑터에서 중복된다.

두 파일은 HttpClient/ObjectMapper 필드, 생성자와 API 키 검증, 엔드포인트 조립, 응답 코드 확인과 예외 래핑 로직이 거의 동일하다. 프롬프트 내용과 응답 스키마, 결과 파싱만 다르다. 이후 계층에 있는 GeminiLessonDraftAdapter, GeminiCodeFixerAdapter도 같은 패턴을 따를 가능성이 높다.

  • src/main/java/com/module06/backend/reviewloop/judge/GeminiJudgeAdapter.java#L26-L163: 공통 HTTP 전송·오류 처리 로직을 추상 기반 클래스나 헬퍼로 추출하고, 이 클래스는 프롬프트 생성과 응답 파싱만 구현하도록 리팩터링한다.
  • src/main/java/com/module06/backend/reviewloop/judge/GeminiOptionPanelAdapter.java#L20-L134: 동일한 공통 기반을 사용하도록 리팩터링한다.
🤖 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/module06/backend/reviewloop/judge/GeminiJudgeAdapter.java`
around lines 26 - 163, Extract the duplicated Gemini REST setup and
request/response error handling from GeminiJudgeAdapter and
GeminiOptionPanelAdapter into a shared abstract base class or helper, including
HttpClient/ObjectMapper state, API-key validation, endpoint construction, status
checks, and exception wrapping. Refactor GeminiJudgeAdapter at
src/main/java/com/module06/backend/reviewloop/judge/GeminiJudgeAdapter.java:26-163
and GeminiOptionPanelAdapter at
src/main/java/com/module06/backend/reviewloop/judge/GeminiOptionPanelAdapter.java:20-134
to reuse it while retaining each adapter’s prompt construction, response schema,
and result parsing behavior; structure it for reuse by GeminiLessonDraftAdapter
and GeminiCodeFixerAdapter.
🤖 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/gate1-semgrep.yml:
- Around line 16-17: Pin the container image in the workflow’s container
configuration to a verified immutable Semgrep image digest instead of the
floating semgrep/semgrep reference, and document the procedure for validating
and updating that digest.
- Around line 19-22: Disable checkout credential persistence by adding
persist-credentials: false to the checkout step in
.github/workflows/gate1-semgrep.yml lines 19-22 and
.github/workflows/gate2-judge.yml lines 27-28. In
.github/workflows/gate2-judge.yml lines 70-73 and 117-118, stop exposing
GEMINI_API_KEY while executing PR-controlled ./gradlew code, and arrange for the
secret to be used only by trusted workflow code.

In @.github/workflows/gate2-judge.yml:
- Around line 17-20: Update the workflow permissions in gate2-judge.yml to
remove the global pull-requests: write permission, keeping only contents: read
at workflow scope. If test-result publishing requires checks: write, move that
permission to the gate2-deterministic job rather than retaining it globally.
- Around line 101-108: Remove direct repository-secret injection from
gate2-review at .github/workflows/gate2-judge.yml lines 101-108 and run its
key-using step only after approval from a protected GitHub Environment secret;
keep the pre-approval path deterministic and key-free. Apply the same
protected-environment boundary to gate2-live-judge at
.github/workflows/gate2-judge.yml lines 135-141, without relying on
continue-on-error to protect the secret.

In `@scripts/review-autoloop.sh`:
- Around line 37-40: Update the CHANGED path in the review-autoloop script to
use the repository-managed path returned by git rev-parse --git-path instead of
assuming .git is a directory, so linked worktrees can write the changed-file
list successfully. Keep the existing git diff filtering and line-count logic
unchanged.

In `@scripts/review-fix-apply.sh`:
- Around line 47-58: Replace the Bash 4-only mapfile usage in the
findings-processing while loop with a Bash 3.2-compatible method that reliably
populates opts from options_for, including when no options are returned.
Preserve the existing iteration and display behavior in the "${opts[@]}" loop so
set -u cannot trigger an unbound-variable failure.

In `@scripts/review-score-domains.sh`:
- Line 29: Update scripts/review-score-domains.sh lines 29-29 by changing
DOMAIN_ROOT to src/main/java/com/module06/backend/domain, and update
scripts/review-fix.sh lines 10-10 by changing the usage example’s --path value
to src/main/java/com/module06/backend/domain/cart.

In
`@src/main/java/com/module06/backend/reviewloop/judge/AutoLoopOrchestrator.java`:
- Around line 78-99: Wrap the AutoFixRunner.run invocation in the
file-processing flow with try/catch so any exception restores the file from
original before being rethrown. Ensure Files.writeString(abs, original) executes
on failure, while preserving the existing changed-result handling for successful
runs.

In `@src/main/java/com/module06/backend/reviewloop/judge/EvidenceValidator.java`:
- Around line 28-30: Update EvidenceValidator’s target-path validation to
normalize the resolved finding path and canonical repository root, rejecting any
target whose actual path falls outside the repository, including absolute paths,
../ traversal, and symlinked files escaping the root. Preserve regular-file
validation for in-repository targets, remove out-of-scope findings through the
existing validation flow, and add tests covering all three path cases.

In
`@src/main/java/com/module06/backend/reviewloop/judge/GeminiCodeFixerAdapter.java`:
- Around line 80-87: Update the response-handling flow around parseFixedCode to
inspect Gemini’s finishReason before returning generated code. Treat token-limit
termination (MAX_TOKENS) as a failed or rejected response rather than returning
the potentially truncated result, while preserving the existing behavior for
normally completed responses.

In `@src/main/java/com/module06/backend/reviewloop/judge/GeminiJudgeAdapter.java`:
- Around line 115-120: Remove the explicit temperature=0 setting from generation
configuration in GeminiJudgeAdapter.java (lines 115-120) and
GeminiOptionPanelAdapter.java (lines 117-121), while preserving the existing
systemInstruction and explicit JSON response rules. In GeminiModels.java (lines
14-20), restrict GEMINI_MODEL to supported models or add model-specific
regression tests covering the allowed configuration.

In `@src/main/java/com/module06/backend/reviewloop/judge/JudgePromptBuilder.java`:
- Around line 35-46: Update the lesson prompt construction around the loop in
JudgePromptBuilder to apply explicit per-rule recent-lesson limits and a
deterministic overall character or token budget before appending to the policy
string. Exclude CONFIRMED lessons, then deterministically select, order, and
truncate remaining lessons so the generated prompt always stays within the
configured bounds. Add deterministic tests covering selection order, per-rule
limits, and total-budget truncation.

In `@src/main/java/com/module06/backend/reviewloop/judge/KnowledgeStore.java`:
- Around line 32-44: Update KnowledgeStore.lessons() in
src/main/java/com/module06/backend/reviewloop/judge/KnowledgeStore.java lines
32-44 to wrap each mapper.readValue(line, Lesson.class) call in a line-scoped
try-catch, warn on parsing failure, and skip only the invalid line. Apply the
same handling to mapper.readValue(line, AuditRecord.class) inside
ReviewReport.fromFiles() in
src/main/java/com/module06/backend/reviewloop/judge/ReviewReport.java lines
59-72, preserving successful records and preventing one malformed JSONL line
from aborting the entire read.

In `@src/main/java/com/module06/backend/reviewloop/judge/ReviewLoopRunner.java`:
- Around line 42-52: Update the early-return branches in ReviewLoopRunner for
empty targets and missing GEMINI_API_KEY to initialize findingsOut as an empty
findings file when it is configured, before returning. Ensure both paths clear
stale findings while preserving the existing normal writeFindings flow.

In `@src/main/java/com/module06/backend/reviewloop/judge/RuleCatalog.java`:
- Around line 213-219: Update parseSeverity so an unknown severity never
silently falls back to Severity.MINOR; instead, fail rule loading by propagating
an appropriate exception, or use the safer Severity.CRITICAL fallback with a
warning. Preserve normal parsing for valid severity values and ensure
misspellings such as “Critial” cannot bypass the human-approval path.

---

Minor comments:
In `@review-loop/AUTOLOOP_DESIGN.md`:
- Around line 28-60: 문서의 두 AutoLoopRunner 참조가 현재 패키지명을 사용하도록 수정하세요.
`com.wanted.backend.reviewloop.judge.AutoLoopRunner#main`과 Gradle `mainClass` 값을
모두 `com.module06.backend.reviewloop.judge.AutoLoopRunner`로 변경하고, 나머지 배선과 옵션은
유지하세요.

In `@review-loop/knowledge/README.md`:
- Around line 3-13:
ReviewLoopCliの教訓保存先をReviewLoopPaths.LESSONSに統一し、review-loop/knowledge/lessons.jsonlへ保存するよう更新してください。あわせてREADMEのlogs/に関する説明と.gitignoreの設定を確認し、review-loop/logs/を揮発性ログとして無視する規則を追加して記述と実際の挙動を一致させてください。

In `@review-loop/UNIFIED_DESIGN.md`:
- Around line 158-169: Update the review workflow steps 5 and 7 to invoke the
existing scripts/review-verify.sh directly instead of the unavailable ./gradlew
reviewVerify wrapper, preserving the --files-from <changed> argument and adding
--with-test for final verification. Ensure the documented command matches the
script’s supported options.

In `@scripts/review-fix.sh`:
- Line 10: Update the example command comment in review-fix.sh to replace the
outdated com/wanted/backend package path with the migrated com/module06/backend
path, while preserving the existing cart domain example.

In `@src/main/java/com/module06/backend/reviewloop/judge/AutoFixRunner.java`:
- Around line 41-63: Update AutoFixRunner.run so an already-exhausted
ReviewBudget cannot produce an AutoFixResult with a null verdict: either
reject/avoid the call before entering the loop or return an explicit terminal
verdict. Ensure every return path, including the final path after the loop,
provides a non-null finalVerdict rather than adding only a null-return guard.

In
`@src/main/java/com/module06/backend/reviewloop/judge/CompileVerification.java`:
- Around line 36-52: Update CompileVerification.verify to always delete the
temporary outDir and its generated class files after compilation completes,
including when compilation or verification fails. Use guaranteed cleanup around
the existing compiler task while preserving the current VerifyResult and
diagnostic handling.

In `@src/main/java/com/module06/backend/reviewloop/judge/DriverBudget.java`:
- Around line 31-32: DriverBudget의 STATE_FILE 및 HEAD_FILE을 고정된 .git 하위 경로로 만들지
말고, .git이 디렉터리인지 파일인지 확인해 실제 Git 디렉터리를 해석하도록 수정하십시오. .git 파일의 gitdir 경로를 읽어
worktree와 submodule에서도 HEAD 조회 및 상태 저장이 동일하게 동작하게 하며, 일반 저장소의 디렉터리 형태도 유지하십시오.

In `@src/main/java/com/module06/backend/reviewloop/judge/OptionPanel.java`:
- Around line 6-9: OptionPanel의 options를 List.copyOf(options)로 방어 복사해 불변성을
보장하세요. 정상 패널 생성 시 recommendation.pick()이 options의 PanelOption letter와 일치하는지
검증하고, GeminiOptionPanelAdapter.parsePanel의 빈 options·빈 pick 응답 없음 경로는 별도 상태로
표현하거나 OptionPanel 생성을 건너뛰도록 수정하세요.

In `@src/main/java/com/module06/backend/reviewloop/judge/ReviewLoopCli.java`:
- Around line 78-81: Update the file-writing flow around Files.writeString in
ReviewLoopCli so the parent build directory is created or otherwise guaranteed
to exist before writing build/review-cli-screen.txt. Match the existing logsDir
directory-initialization approach, while preserving the current screen output
and file contents.

In `@src/main/java/com/module06/backend/reviewloop/judge/ReviewLoopRunner.java`:
- Around line 116-118: Before the Files.writeString call in ReviewLoopRunner,
create the parent directory for build/reviewloop-run.txt using the same
defensive directory-creation approach already used around lines 173-176. Ensure
report generation and output remain unchanged while preventing failure when the
build directory does not exist.

In `@src/main/java/com/module06/backend/reviewloop/judge/ReviewRunner.java`:
- Around line 34-58: Update ReviewRunner.run and its AuditSummary contract to
explicitly handle an initially exhausted ReviewBudget: return an AuditSummary
with finalVerdict() null and roundsUsed() 0 without invoking reviewer.review or
consuming budget. Ensure the minimum-round budget configuration is rejected when
zero rounds are disallowed; otherwise document and handle the nullable verdict
in consumers and add regression coverage.

In
`@src/test/java/com/module06/backend/reviewloop/judge/GeminiJudgeAdapterLiveTest.java`:
- Around line 38-40: Update the findings-file write in
GeminiJudgeAdapterLiveTest to create the parent directory before calling
writeString, matching ReviewLoopRunner.writeFindings behavior so direct or IDE
execution succeeds when build is absent. Reuse the existing Files and Path
imports instead of fully qualified names.

---

Nitpick comments:
In `@scripts/review-fix.sh`:
- Around line 17-20: Update scripts/review-fix.sh to change into the repository
root before invoking the relative ./gradlew and $DIR/review-fix-apply.sh
commands, following the existing root-resolution approach used by
review-score-domains.sh while preserving the script directory references.

In `@scripts/review-score-domains.sh`:
- Around line 80-98: Replace the eval-based assignment around the awk
aggregation with safe read-based parsing of its fixed output, assigning files,
avg, low, nr, crit, and fnd directly without executing command text. Preserve
the existing numeric aggregation and zero-files handling, and ensure the change
resolves the SC2154 warnings for these variables.

In `@src/main/java/com/module06/backend/reviewloop/judge/AutoLoopRunner.java`:
- Around line 84-107: Update loadTargets to resolve each listed path against the
repository root and accept it only when its normalized absolute path remains
within that root; skip absolute paths and traversal entries that escape the root
while preserving the existing .java, existence, and maxFiles filtering.

In `@src/main/java/com/module06/backend/reviewloop/judge/ClaudeJudgeAdapter.java`:
- Around line 77-91: Update the toUpperCase calls in parseSeverity and
parseConfidence to use Locale.ROOT, ensuring normalized enum inputs parse
consistently regardless of the JVM’s default locale.

In `@src/main/java/com/module06/backend/reviewloop/judge/DriverBudget.java`:
- Around line 62-70: Protect the entire load-apply-save read-modify-write
sequence in main() with an exclusive FileLock on the budget state file, keeping
the lock held through load(), applied(), and save(). Ensure the lock is released
reliably via the existing Java resource-management pattern while preserving
rendering and exhausted-status behavior.

In
`@src/main/java/com/module06/backend/reviewloop/judge/GeminiCodeFixerAdapter.java`:
- Around line 89-97: Update parseFixedCode to inspect the candidate response’s
finishReason before returning generated text, and return fallback when it is
MAX_TOKENS. Preserve the existing empty or blank-text fallback behavior and only
apply this additional guard to truncated responses.

In `@src/main/java/com/module06/backend/reviewloop/judge/GeminiJudgeAdapter.java`:
- Around line 26-163: Extract the duplicated Gemini REST setup and
request/response error handling from GeminiJudgeAdapter and
GeminiOptionPanelAdapter into a shared abstract base class or helper, including
HttpClient/ObjectMapper state, API-key validation, endpoint construction, status
checks, and exception wrapping. Refactor GeminiJudgeAdapter at
src/main/java/com/module06/backend/reviewloop/judge/GeminiJudgeAdapter.java:26-163
and GeminiOptionPanelAdapter at
src/main/java/com/module06/backend/reviewloop/judge/GeminiOptionPanelAdapter.java:20-134
to reuse it while retaining each adapter’s prompt construction, response schema,
and result parsing behavior; structure it for reuse by GeminiLessonDraftAdapter
and GeminiCodeFixerAdapter.

In
`@src/main/java/com/module06/backend/reviewloop/judge/GeminiLessonDraftAdapter.java`:
- Around line 51-56: 응답 처리에서 429 및 503을 일시적 오류로 분류하고 짧은 지수 백오프로 제한된 재시도를 적용하십시오.
그 외 비-2xx 응답은 기존처럼 명확한 예외로 처리하고, parseNote 결과가 빈 문자열이면 Lesson을 생성하지 말고 예외를 던지거나
호출자가 실패를 판별할 수 있는 값으로 반환하십시오.

In
`@src/main/java/com/module06/backend/reviewloop/judge/RepeatedPatternDetector.java`:
- Around line 23-31: Update RepeatedPatternDetector.detect by adding ruleId as a
secondary ascending sort key after descending count, using Comparator as needed,
so alerts with equal counts always have deterministic ordering.

In
`@src/main/java/com/module06/backend/reviewloop/judge/ReviewLessonRecorder.java`:
- Around line 52-69: Update the timestamp creation in ReviewLessonRecorder.parse
to follow the repository’s ClockConfig convention instead of calling
LocalDateTime.now() directly. Reuse the existing injected or configured Clock,
or use Instant.now() in UTC if that is the established project pattern, while
preserving the Lesson construction and argument parsing behavior.

In `@src/main/java/com/module06/backend/reviewloop/judge/ReviewLoopRunner.java`:
- Around line 86-108: 각 파일의 ReviewLoopRunner 처리에서 loop.review 호출과 후속 결과 처리를 파일별
try-catch로 감싸세요. 예외가 발생한 파일은 실패 내용을 out 리포트와 audit에 기록하고 blocked 상태를 적절히 반영한 뒤,
예외를 전파하지 말고 다음 targets 항목으로 계속 진행하도록 수정하세요.

In `@src/main/java/com/module06/backend/reviewloop/judge/ReviewReport.java`:
- Around line 47-55: Update the lesson rendering loop in ReviewReport so each
l.humanNote() is normalized by replacing internal newline characters with spaces
before appending it to the Markdown list item. Preserve the existing note
content and formatting for single-line notes.

In
`@src/test/java/com/module06/backend/reviewloop/judge/GeminiAutoFixLoopLiveTest.java`:
- Around line 28-65: 세 개의 Gemini 라이브 테스트에 외부 API 무응답을 제한할 수 있는 유한한 JUnit
`@Timeout을` 추가하십시오.
src/test/java/com/module06/backend/reviewloop/judge/GeminiAutoFixLoopLiveTest.java:28-65의
autoFixLoopEndToEnd,
src/test/java/com/module06/backend/reviewloop/judge/GeminiOptionPanelLiveTest.java:19-45의
generatesPanel,
src/test/java/com/module06/backend/reviewloop/judge/GeminiReviewLoopLiveTest.java:25-57의
fullLoopEndToEnd에 적용하고, 필요한 Timeout import와 모든 테스트에 일관된 적절한 제한 시간을 사용하십시오.

In `@src/test/java/com/module06/backend/reviewloop/judge/OptionPanelTest.java`:
- Around line 38-44: Update selectedActionRoutes so it either passes the chosen
PanelAction through the actual routing/handling path and asserts the resulting
behavior, or rename its DisplayName to accurately describe validating the
PanelOption action field; prefer exercising the routing path if the relevant
handler is available.

In
`@src/test/java/com/module06/backend/reviewloop/judge/ReviewLessonRecorderTest.java`:
- Around line 60-75: Review the tests around ReviewLessonRecorder.parse and add
coverage for the MISSED kind, asserting it produces LessonKind.MISSED, plus a
case where --kind is omitted, asserting the parser rejects the input with the
expected IllegalArgumentException and message. Preserve the existing CONFIRMED
and unknown-kind coverage.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: f041290f-2b36-4a53-a38b-2415f15c0cce

📥 Commits

Reviewing files that changed from the base of the PR and between 84a7783 and 2eb94b4.

📒 Files selected for processing (104)
  • .githooks/pre-push
  • .github/workflows/gate1-semgrep.yml
  • .github/workflows/gate2-judge.yml
  • build.gradle
  • review-loop/AUTOLOOP_DESIGN.md
  • review-loop/DRIVER.md
  • review-loop/UNIFIED_DESIGN.md
  • review-loop/golden/conv001/ReinventedClock.java.txt
  • review-loop/golden/perf001/QuizListN1.java.txt
  • review-loop/knowledge/README.md
  • review-loop/knowledge/lessons.jsonl
  • review-loop/logs/error_log.jsonl
  • review-loop/rules.yaml
  • review-loop/semgrep/query.yml
  • scripts/review-autoloop.sh
  • scripts/review-fix-apply.sh
  • scripts/review-fix.sh
  • scripts/review-score-domains.sh
  • scripts/review-verify.sh
  • src/main/java/com/module06/backend/reviewloop/judge/AuditLogWriter.java
  • src/main/java/com/module06/backend/reviewloop/judge/AuditRecord.java
  • src/main/java/com/module06/backend/reviewloop/judge/AuditSummary.java
  • src/main/java/com/module06/backend/reviewloop/judge/AutoFixResult.java
  • src/main/java/com/module06/backend/reviewloop/judge/AutoFixRunner.java
  • src/main/java/com/module06/backend/reviewloop/judge/AutoLoopOrchestrator.java
  • src/main/java/com/module06/backend/reviewloop/judge/AutoLoopRunner.java
  • src/main/java/com/module06/backend/reviewloop/judge/ClaudeJudgeAdapter.java
  • src/main/java/com/module06/backend/reviewloop/judge/CliArgs.java
  • src/main/java/com/module06/backend/reviewloop/judge/CodeFixerPort.java
  • src/main/java/com/module06/backend/reviewloop/judge/CompileVerification.java
  • src/main/java/com/module06/backend/reviewloop/judge/Confidence.java
  • src/main/java/com/module06/backend/reviewloop/judge/DriverBudget.java
  • src/main/java/com/module06/backend/reviewloop/judge/EvidenceValidator.java
  • src/main/java/com/module06/backend/reviewloop/judge/Finding.java
  • src/main/java/com/module06/backend/reviewloop/judge/FindingDto.java
  • src/main/java/com/module06/backend/reviewloop/judge/FindingSource.java
  • src/main/java/com/module06/backend/reviewloop/judge/GeminiCodeFixerAdapter.java
  • src/main/java/com/module06/backend/reviewloop/judge/GeminiJudgeAdapter.java
  • src/main/java/com/module06/backend/reviewloop/judge/GeminiLessonDraftAdapter.java
  • src/main/java/com/module06/backend/reviewloop/judge/GeminiModels.java
  • src/main/java/com/module06/backend/reviewloop/judge/GeminiOptionPanelAdapter.java
  • src/main/java/com/module06/backend/reviewloop/judge/JudgeDecision.java
  • src/main/java/com/module06/backend/reviewloop/judge/JudgeFindingsDto.java
  • src/main/java/com/module06/backend/reviewloop/judge/JudgePromptBuilder.java
  • src/main/java/com/module06/backend/reviewloop/judge/JudgeScorer.java
  • src/main/java/com/module06/backend/reviewloop/judge/JudgeVerdict.java
  • src/main/java/com/module06/backend/reviewloop/judge/KnowledgeStore.java
  • src/main/java/com/module06/backend/reviewloop/judge/Lesson.java
  • src/main/java/com/module06/backend/reviewloop/judge/LessonApprovalService.java
  • src/main/java/com/module06/backend/reviewloop/judge/LessonDraftPort.java
  • src/main/java/com/module06/backend/reviewloop/judge/LessonKind.java
  • src/main/java/com/module06/backend/reviewloop/judge/LlmJudgePort.java
  • src/main/java/com/module06/backend/reviewloop/judge/OptionPanel.java
  • src/main/java/com/module06/backend/reviewloop/judge/OptionPanelPort.java
  • src/main/java/com/module06/backend/reviewloop/judge/PanelAction.java
  • src/main/java/com/module06/backend/reviewloop/judge/PanelOption.java
  • src/main/java/com/module06/backend/reviewloop/judge/Recommendation.java
  • src/main/java/com/module06/backend/reviewloop/judge/RepeatedPatternDetector.java
  • src/main/java/com/module06/backend/reviewloop/judge/ReviewBudget.java
  • src/main/java/com/module06/backend/reviewloop/judge/ReviewLessonRecorder.java
  • src/main/java/com/module06/backend/reviewloop/judge/ReviewLoop.java
  • src/main/java/com/module06/backend/reviewloop/judge/ReviewLoopCli.java
  • src/main/java/com/module06/backend/reviewloop/judge/ReviewLoopPaths.java
  • src/main/java/com/module06/backend/reviewloop/judge/ReviewLoopRunner.java
  • src/main/java/com/module06/backend/reviewloop/judge/ReviewReport.java
  • src/main/java/com/module06/backend/reviewloop/judge/ReviewRunner.java
  • src/main/java/com/module06/backend/reviewloop/judge/RoundReviewer.java
  • src/main/java/com/module06/backend/reviewloop/judge/RuleAccuracy.java
  • src/main/java/com/module06/backend/reviewloop/judge/RuleCatalog.java
  • src/main/java/com/module06/backend/reviewloop/judge/Severity.java
  • src/main/java/com/module06/backend/reviewloop/judge/VerificationPort.java
  • src/main/java/com/module06/backend/reviewloop/judge/VerifiedFixer.java
  • src/main/java/com/module06/backend/reviewloop/judge/VerifyResult.java
  • src/test/java/com/module06/backend/reviewloop/judge/AcceptanceGateTest.java
  • src/test/java/com/module06/backend/reviewloop/judge/AuditLogWriterTest.java
  • src/test/java/com/module06/backend/reviewloop/judge/AutoFixRunnerTest.java
  • src/test/java/com/module06/backend/reviewloop/judge/ClaudeJudgeAdapterLiveTest.java
  • src/test/java/com/module06/backend/reviewloop/judge/DriverBudgetTest.java
  • src/test/java/com/module06/backend/reviewloop/judge/EvidenceValidatorTest.java
  • src/test/java/com/module06/backend/reviewloop/judge/GeminiAutoFixLoopLiveTest.java
  • src/test/java/com/module06/backend/reviewloop/judge/GeminiJudgeAdapterLiveTest.java
  • src/test/java/com/module06/backend/reviewloop/judge/GeminiJudgeAdapterRequestTest.java
  • src/test/java/com/module06/backend/reviewloop/judge/GeminiLessonDraftLiveTest.java
  • src/test/java/com/module06/backend/reviewloop/judge/GeminiOptionPanelLiveTest.java
  • src/test/java/com/module06/backend/reviewloop/judge/GeminiReviewLoopLiveTest.java
  • src/test/java/com/module06/backend/reviewloop/judge/JudgePipelineTest.java
  • src/test/java/com/module06/backend/reviewloop/judge/JudgePromptBuilderTest.java
  • src/test/java/com/module06/backend/reviewloop/judge/JudgeScorerTest.java
  • src/test/java/com/module06/backend/reviewloop/judge/KnowledgeArtifactsGenerator.java
  • src/test/java/com/module06/backend/reviewloop/judge/KnowledgeLoopTest.java
  • src/test/java/com/module06/backend/reviewloop/judge/KnowledgeLoopWiringTest.java
  • src/test/java/com/module06/backend/reviewloop/judge/OptionPanelTest.java
  • src/test/java/com/module06/backend/reviewloop/judge/PrePushGatePolicyTest.java
  • src/test/java/com/module06/backend/reviewloop/judge/ReviewBudgetTest.java
  • src/test/java/com/module06/backend/reviewloop/judge/ReviewLessonRecorderTest.java
  • src/test/java/com/module06/backend/reviewloop/judge/ReviewLoopPathsTest.java
  • src/test/java/com/module06/backend/reviewloop/judge/ReviewLoopRunnerTest.java
  • src/test/java/com/module06/backend/reviewloop/judge/ReviewLoopTest.java
  • src/test/java/com/module06/backend/reviewloop/judge/ReviewReportTest.java
  • src/test/java/com/module06/backend/reviewloop/judge/ReviewRunnerTest.java
  • src/test/java/com/module06/backend/reviewloop/judge/RuleAccuracyTest.java
  • src/test/java/com/module06/backend/reviewloop/judge/RuleCatalogDomainTest.java
  • src/test/java/com/module06/backend/reviewloop/judge/RuleCatalogScoringTest.java
  • src/test/java/com/module06/backend/reviewloop/judge/SemiAutoLessonTest.java

Comment thread .github/workflows/gate1-semgrep.yml Outdated
Comment on lines +16 to +17
container:
image: semgrep/semgrep

@coderabbitai coderabbitai Bot Aug 1, 2026

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- workflow ---'
cat -n .github/workflows/gate1-semgrep.yml
printf '%s\n' '--- related image references ---'
rg -n -i 'semgrep/semgrep|container:|image:' .github README.md 2>/dev/null || true
printf '%s\n' '--- repository metadata ---'
git remote -v 2>/dev/null || true
git status --short

Repository: module06-4/BACKEND

Length of output: 1565


🏁 Script executed:

#!/bin/bash
set -eu
python3 - <<'PY'
import re
from pathlib import Path
text = Path(".github/workflows/gate1-semgrep.yml").read_text()
match = re.search(r'^\s*image:\s*(\S+)\s*$', text, re.M)
image = match.group(1)
print("image_ref:", image)
print("has_tag_or_digest:", ":" in image.rsplit("/", 1)[-1] or "@" in image)
PY

token="$(curl -fsSL 'https://auth.docker.io/token?service=registry.docker.io&scope=repository:semgrep/semgrep:pull' | python3 -c 'import json,sys; print(json.load(sys.stdin)["token"])')"
printf '%s\n' '--- latest manifest digest ---'
curl -fsSI \
  -H "Authorization: Bearer $token" \
  -H 'Accept: application/vnd.oci.image.index.v1+json, application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json' \
  'https://registry-1.docker.io/v2/semgrep/semgrep/manifests/latest' |
  tr -d '\r' | grep -i '^docker-content-digest:\|^content-type:'
printf '%s\n' '--- relevant repository guidance ---'
rg -n -i 'digest|pin(ned|ning)?|semgrep|container image|dependency update|renovate|dependabot' . --glob '!*.lock' --glob '!node_modules/**' 2>/dev/null | head -100 || true

Repository: module06-4/BACKEND

Length of output: 2751


🏁 Script executed:

#!/bin/bash
set -eu
python3 - <<'PY'
import re
from pathlib import Path
text = Path(".github/workflows/gate1-semgrep.yml").read_text()
match = re.search(r'^\s*image:\s*(\S+)\s*$', text, re.M)
image = match.group(1)
print("image_ref:", image)
print("has_tag_or_digest:", ":" in image.rsplit("/", 1)[-1] or "@" in image)
PY

token="$(curl -fsSL 'https://auth.docker.io/token?service=registry.docker.io&scope=repository:semgrep/semgrep:pull' | python3 -c 'import json,sys; print(json.load(sys.stdin)["token"])')"
curl -fsSI \
  -H "Authorization: Bearer $token" \
  -H 'Accept: application/vnd.oci.image.index.v1+json, application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json' \
  'https://registry-1.docker.io/v2/semgrep/semgrep/manifests/latest' |
  tr -d '\r' | grep -i '^docker-content-digest:\|^content-type:'

Repository: module06-4/BACKEND

Length of output: 288


Semgrep 컨테이너 이미지를 digest로 고정하세요.

semgrep/semgrep에는 tag와 digest가 없으므로 Docker는 latest를 사용합니다. latest가 변경되면 동일한 커밋에서 다른 Semgrep 버전이 실행될 수 있습니다. 검증된 digest를 지정하고 갱신 절차를 문서화하세요.

🧰 Tools
🪛 zizmor (1.28.0)

[error] 17-17: unpinned image references (unpinned-images): container image is unpinned

(unpinned-images)

🤖 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/gate1-semgrep.yml around lines 16 - 17, Pin the container
image in the workflow’s container configuration to a verified immutable Semgrep
image digest instead of the floating semgrep/semgrep reference, and document the
procedure for validating and updating that digest.

Source: Linters/SAST tools

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

persist-credentials: false는 네 개 checkout 단계 모두에 반영했습니다 (8ef3710).
시크릿 주입 건은 gate2-judge.yml:108 스레드에서 함께 답변드립니다.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- gate1 workflow ---'
sed -n '1,100p' .github/workflows/gate1-semgrep.yml

printf '%s\n' '--- Semgrep image and update-procedure references ---'
rg -n -i -C 2 'semgrep/semgrep|semgrep.*(digest|update|갱신)|digest.*semgrep' \
  .github review-loop README.md 2>/dev/null || true

Length of output: 2102


@dlxodus02 persist-credentials: false 변경은 확인했습니다. 해당 변경은 별도 actions/checkout 지적을 해결합니다.

이 스레드는 Semgrep container image 고정에 관한 지적입니다. 현재 semgrep/semgrep:1.172.0은 버전 태그이지만 immutable digest는 아닙니다. 태그는 다시 지정될 수 있으므로, 동일한 커밋에서 실행 이미지가 변경될 수 있습니다.

워크플로 주석에는 태그 사용 이유가 문서화되어 있습니다. 그러나 이 스레드의 digest 고정 요구 사항은 아직 충족되지 않았습니다.

You are interacting with an AI system.

Comment thread .github/workflows/gate1-semgrep.yml
Comment thread .github/workflows/gate2-judge.yml Outdated
Comment thread .github/workflows/gate2-judge.yml
Comment thread scripts/review-autoloop.sh
Comment on lines +35 to +46
if (lessons != null && !lessons.isEmpty()) {
StringBuilder mistakes = new StringBuilder();
for (Lesson lesson : lessons) {
if (lesson.kind() == LessonKind.CONFIRMED) {
continue; // CONFIRMED는 Judge의 실수가 아님 — 정확도 집계용이라 프롬프트에서 제외
}
mistakes.append("- [").append(lesson.kind()).append("] ")
.append(lesson.ruleId()).append(": ").append(lesson.humanNote()).append('\n');
}
if (mistakes.length() > 0) {
sb.append("\n과거 사람 검토에서 나온 교훈(같은 실수를 반복하지 마라):\n").append(mistakes);
}

@coderabbitai coderabbitai Bot Aug 1, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🚀 Performance & Scalability | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- candidate files ---'
fd -t f 'JudgePromptBuilder\.java|ReviewLoop.*\.java|Lesson.*\.java' . || true

printf '%s\n' '--- relevant symbols and call sites ---'
rg -n -S 'class JudgePromptBuilder|buildPolicy|class ReviewLoop|ReviewLoop|LessonKind|humanNote|ruleId' . \
  -g '*.java' -g '*.kt' -g '*.md' || true

Repository: module06-4/BACKEND

Length of output: 30112


🏁 Script executed:

#!/bin/bash
set -eu

for f in \
  src/main/java/com/module06/backend/reviewloop/judge/JudgePromptBuilder.java \
  src/main/java/com/module06/backend/reviewloop/judge/ReviewLoop.java \
  src/main/java/com/module06/backend/reviewloop/judge/KnowledgeStore.java \
  src/main/java/com/module06/backend/reviewloop/judge/ReviewLoopRunner.java \
  src/main/java/com/module06/backend/reviewloop/judge/AutoLoopRunner.java \
  src/main/java/com/module06/backend/reviewloop/judge/AutoLoopOrchestrator.java \
  src/test/java/com/module06/backend/reviewloop/judge/JudgePromptBuilderTest.java \
  src/test/java/com/module06/backend/reviewloop/judge/KnowledgeLoopTest.java \
  src/test/java/com/module06/backend/reviewloop/judge/KnowledgeLoopWiringTest.java
do
  if [ -f "$f" ]; then
    printf '\n--- %s ---\n' "$f"
    wc -l "$f"
    sed -n '1,220p' "$f"
  fi
done

Repository: module06-4/BACKEND

Length of output: 33826


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- repeated review calls ---'
rg -n -C 5 -S 'review\(|ReviewLoop|roundsUsed|roundsPerFile|globalBudget' \
  src/main/java/com/module06/backend/reviewloop/judge \
  -g '*.java' | sed -n '1,260p'

printf '%s\n' '--- judge request construction and limits ---'
for f in $(fd -t f -i 'JudgeAdapter|Gemini.*Adapter|Claude.*Adapter|AutoFixRunner' src/main/java/com/module06/backend/reviewloop/judge); do
  printf '\n--- %s ---\n' "$f"
  rg -n -C 6 -S 'policy|contents|prompt|request|token|maxOutput|review\(' "$f" || true
done

printf '%s\n' '--- lesson persistence and ordering ---'
rg -n -C 8 -S 'new KnowledgeStore|\.record\(|lessons\(\)|LESSONS|lessons\.jsonl|sort|Comparator' \
  src/main/java src/test review-loop -g '*.java' -g '*.md' -g '*.jsonl' || true

Repository: module06-4/BACKEND

Length of output: 50377


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- exact request builders ---'
sed -n '96,135p' src/main/java/com/module06/backend/reviewloop/judge/GeminiJudgeAdapter.java
sed -n '34,75p' src/main/java/com/module06/backend/reviewloop/judge/ClaudeJudgeAdapter.java

printf '%s\n' '--- repeated-call implementations ---'
sed -n '1,90p' src/main/java/com/module06/backend/reviewloop/judge/ReviewRunner.java
sed -n '1,85p' src/main/java/com/module06/backend/reviewloop/judge/AutoFixRunner.java

printf '%s\n' '--- persisted lessons ---'
fd -t f -i 'lessons\.jsonl' review-loop src || true
for f in $(fd -t f -i 'lessons\.jsonl' review-loop src); do
  printf '\n--- %s ---\n' "$f"
  wc -c -l "$f"
  cat -n "$f"
done

printf '%s\n' '--- request-size configuration ---'
rg -n -i -S 'maxTokens|maxOutputTokens|input.*token|token.*limit|context.*limit|request.*size|payload.*size' \
  src/main/java review-loop build.gradle* gradle.properties 2>/dev/null || true

Repository: module06-4/BACKEND

Length of output: 8996


🏁 Script executed:

#!/bin/bash
set -eu

python3 - <<'PY'
from pathlib import Path

builder = Path("src/main/java/com/module06/backend/reviewloop/judge/JudgePromptBuilder.java").read_text()
store = Path("src/main/java/com/module06/backend/reviewloop/judge/KnowledgeStore.java").read_text()
loop = Path("src/main/java/com/module06/backend/reviewloop/judge/ReviewLoop.java").read_text()
runner = Path("src/main/java/com/module06/backend/reviewloop/judge/AutoFixRunner.java").read_text()

assert "lesson.kind() == LessonKind.CONFIRMED" in builder
assert "append(lesson.humanNote())" in builder
assert "Files.readAllLines(file)" in store
assert "promptBuilder.buildPolicy(catalog, lessons)" in loop
assert "while (!budget.isExhausted())" in runner
assert "reviewer.review(filePath, code)" in runner

def lesson_line(i):
    return f"- [FALSE_POSITIVE] CONV_001: lesson-{i}\n"

def serialized_lesson_bytes(n):
    return len("".join(lesson_line(i) for i in range(n)).encode())

sizes = [serialized_lesson_bytes(n) for n in (0, 1, 10, 100, 1000)]
assert sizes == sorted(sizes)
assert sizes[-1] > sizes[0]

print("CONFIRMED lessons are excluded by the current builder")
print("non-CONFIRMED lesson serialization follows input-file order")
print("serialized lesson section grows monotonically with lesson count:", sizes)
print("ReviewLoop reuses the same unbounded lesson list on each review call")
PY

Repository: module06-4/BACKEND

Length of output: 430


교훈 프롬프트에 명시적 크기 상한을 적용하세요.

CONFIRMED를 제외한 Lesson을 제한 없이 정책 문자열에 추가합니다. 축적된 JSONL 전체를 각 파일과 자동 수정 라운드의 요청에 반복해서 포함하므로, 모델의 컨텍스트 한도를 초과해 Judge 요청이 실패할 수 있습니다. 규칙별 최근 교훈 수와 전체 문자 또는 토큰 예산을 제한하고, 선택·정렬·절단 순서를 검증하는 결정론적 테스트를 추가하세요.

🤖 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/module06/backend/reviewloop/judge/JudgePromptBuilder.java`
around lines 35 - 46, Update the lesson prompt construction around the loop in
JudgePromptBuilder to apply explicit per-rule recent-lesson limits and a
deterministic overall character or token budget before appending to the policy
string. Exclude CONFIRMED lessons, then deterministically select, order, and
truncate remaining lessons so the generated prompt always stays within the
configured bounds. Add deterministic tests covering selection order, per-rule
limits, and total-budget truncation.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

타당한 지적이라 후속 작업으로 등록하고, 이번 PR에서는 보류하겠습니다.

이식 과정에서 lessons.jsonl을 초기화해 현재 축적된 교훈이 0건이라 당장의 영향은 없습니다.
다만 append-only 구조상 교훈이 쌓이면 매 요청 비용이 선형 증가하고 결국 컨텍스트 한도에 닿는다는 지적에 동의합니다.
규칙별 최근 N건 + 전체 문자 예산 상한을 결정론 테스트와 함께 추가하겠습니다.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@dlxodus02, 확인했습니다. 현재 lessons.jsonl이 비어 있으므로 이 PR의 즉시 동작에는 영향이 없습니다.

후속 작업에는 규칙별 최근 교훈 수 제한, 전체 문자 예산, 결정론적 선택·정렬·절단 테스트를 포함하면 됩니다. 이 리뷰 코멘트는 열린 상태로 유지하겠습니다.

후속 GitHub Issue를 생성할까요? 생성하면 해당 Issue에 PR #5와 이 코멘트 링크를 연결하겠습니다.

🐇📏

You are interacting with an AI system.

dlxodus02 and others added 3 commits August 2, 2026 03:55
CodeRabbit(.coderabbit.yaml)과 역할 분리 — 광범위 어드바이저리는 CodeRabbit,
루프 Gate는 게이트/자율수정 대상만 남긴다(중복·오탐 방지).

- 제거: FLOWN 전용 SEC_001/002·DOM_001/002·QDIFF_001/003/005 + ARCH_003 FLOWN 부채 리셋
- 유지: 결정론 백본(ARCH_001/002/003·QUERY_002·MIG_001) + judge 3개(CONV_001·PERF_001·ARCH_003a)
- judge 3개는 회귀 가드(JudgePromptBuilderTest·RuleCatalogScoringTest)가 고정 → 유지
- 앵커의 FLOWN 파일참조 genericize

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
검증된 결함 수정:
- scripts: 이식 누락된 com/wanted/backend 경로 → com/module06/backend
  (review-score-domains.sh는 존재하지 않는 경로를 스캔하고 있었음)
- review-autoloop.sh: linked worktree에서 .git은 파일이라 그 아래 쓰기 실패
  → git rev-parse --git-path 사용
- review-fix-apply.sh: mapfile은 bash 4+ 전용 → macOS 기본 bash 3.2에서 훅이 깨짐
  → 이식성 있는 while-read 방식
- AutoLoopOrchestrator: 판정·수정 중 예외 시 중간 수정본이 작업트리에 남던 문제
  → try/catch로 원본 복원 후 다음 파일 진행
- ReviewLoopRunner: 조기 종료(대상 없음·키 없음) 시 findings 파일 미초기화로
  낡은 findings가 수정 요청서에 재사용되던 문제 → resetFindings 추가
- KnowledgeStore·ReviewReport: JSONL 한 줄 손상이 전체 읽기를 실패시키던 문제
  → 줄 단위 skip (lessons.jsonl은 공유·커밋 파일이라 머지 충돌 위험)
- RuleCatalog.parseSeverity: 알 수 없는 severity를 조용히 MINOR로 폴백 →
  CRITICAL 오타가 "Critical은 항상 사람" 안전장치를 우회 → 로드 실패로 전환

워크플로 보안 강화:
- gate2-judge: 전역 쓰기 권한 제거(contents: read), checks: write는 필요한 잡에만
- 모든 checkout에 persist-credentials: false

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- GeminiJudgeAdapter: finishReason != STOP인데 parts가 비면 findings 0건 →
  "지적 없음 = PASS"로 게이트가 조용히 통과하던 문제(가장 위험한 실패 방향).
  판정 불가를 통과가 아니라 오류로 드러낸다.
- GeminiCodeFixerAdapter: 잘린 응답(MAX_TOKENS)을 '고친 코드'로 채택하던 문제.
  파일 전체를 반환하는 어댑터라 잘리면 파일이 깨진다 → 원본 유지 + 원인 로그.
  (VerifiedFixer가 결국 롤백하지만 라운드·비용 낭비 + 원인 추적 불가였음)
- gate1-semgrep: image를 latest → 1.172.0 고정. latest 드리프트로 게이트 동작이
  조용히 달라지는 것 방지. digest 대신 버전 태그(Dependabot이 container 이미지를
  자동 갱신하지 않아 digest는 수동 유지보수 부담).

CodeRabbit #10·#1 반영. #11(temperature:0)은 스코어 진동(85↔100) 해결책이라 유지.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

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

Caution

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

⚠️ Outside diff range comments (1)
src/main/java/com/module06/backend/reviewloop/judge/GeminiJudgeAdapter.java (1)

126-145: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

누락된 Gemini 판정 메타데이터를 오류로 처리하십시오.

candidates, finishReason, content.parts 또는 텍스트가 없으면 List.of()를 반환하지 말고 예외를 발생시키십시오. finishReason은 반드시 STOP이어야 합니다. 그렇지 않으면 불완전한 응답이 정상적인 finding 없음으로 처리되어 게이트를 통과할 수 있습니다.

🤖 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/module06/backend/reviewloop/judge/GeminiJudgeAdapter.java`
around lines 126 - 145, Update the response validation in GeminiJudgeAdapter
around candidate parsing so missing candidates, finishReason, content.parts, or
text throw an exception instead of returning List.of(). Require finishReason to
be present and exactly STOP, while preserving normal parsing only for complete
responses.
🤖 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.

Outside diff comments:
In `@src/main/java/com/module06/backend/reviewloop/judge/GeminiJudgeAdapter.java`:
- Around line 126-145: Update the response validation in GeminiJudgeAdapter
around candidate parsing so missing candidates, finishReason, content.parts, or
text throw an exception instead of returning List.of(). Require finishReason to
be present and exactly STOP, while preserving normal parsing only for complete
responses.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: cd7e88ed-1eb0-4096-a4fb-5d17d2c4c1ec

📥 Commits

Reviewing files that changed from the base of the PR and between 8ef3710 and 3eac931.

📒 Files selected for processing (3)
  • .github/workflows/gate1-semgrep.yml
  • src/main/java/com/module06/backend/reviewloop/judge/GeminiCodeFixerAdapter.java
  • src/main/java/com/module06/backend/reviewloop/judge/GeminiJudgeAdapter.java
🚧 Files skipped from review as they are similar to previous changes (1)
  • .github/workflows/gate1-semgrep.yml

@mosungjin mosungjin left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

확인했습니다.

@mosungjin
mosungjin merged commit 0cf17ac into develop Aug 3, 2026
6 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.

[FEAT] AI 코드 리뷰 루프 이식 + 자율 수정 루프(P0/P1) 배선

2 participants