bibimbap/.atp/work-session/20260624-100749/verification.md

60 KiB
Raw Blame History

phase agent agent_version generated_at concerns concerns_checked
verification verification-advisor 1 2026-06-24T12:40:00+09:00
RESOLVED(재검증): 1차 L1 BLOCKER FAIL(JamAdminController 24 errors response() 미정의) → 보정 후 BUILD SUCCESS, Tests run 97 / Failures 0 / Errors 0 / Skipped 0
§33 잠재 리스크(무해): 5매퍼의 camelCase alias 67건이 큰따옴표 비인용 → Postgres 가 소문자 폴딩(AS createdAt → createdat 실측 확인). 단 5매퍼 전부 resultType=POJO(*Data, Map 0건)이므로 MyBatis case-insensitive 매핑으로 현재 무해. resultType 을 Map 으로 바꾸면 즉시 §33 버그화 → 회귀 가드 권고(AS "createdAt" 인용 표준화)
AC-T3 산술(requireJamManage 6 = POST 5 + 선언 1)은 우연 일치. 쓰기 5핸들러 전수 게이트 + 읽기 console gate.has(GAME_JAM_MANAGE) 인라인 — 인가 우회 0. VP-1 JamAdminControllerTest 10 PASS 로 런타임 도달 실증
AC-T6 원문 grep '@MockBean.*Jam' same-line 가정은 오탐 — 실제 어노테이션 별행. 매퍼명 기준 실측 5/5 정합으로 AC 통과, AC 표현 보정 권고
L2 contract: 사용자 dev 스키마 비파괴 위해 격리 throwaway DB 에 jam-ddl 적용 후 검증·DROP 완료. 사용자 dev(bibimbap) DB 의 jam 테이블 0건 — 미적용 상태 유지(파괴적 조작 회피). 운영 적용은 db/apply-local-ddl.sh 로 별도 수행 필요
W2-2 RESULT: L1 BUILD SUCCESS, Tests run 119 / Failures 0 / Errors 0 / Skipped 0 — 전 클래스 GREEN. JamRoleGateTest 8, JamJudgeAdminControllerTest 14. 실패 클래스 없음
W2-2 AC 예측 vs 실측 불일치(무해, FAIL 아님): AC 입력은 JamJudgeAdminControllerTest(16)·총 121(97+24) 예측이었으나 실측 JamJudgeAdminControllerTest=14·총 119. 전 테스트 GREEN·JamRoleGateTest는 예측 8과 정확 일치. 카운트는 구현이 테스트를 더 적게 작성/통합한 결과로 추정되나 advisor는 원인 추정 넘어 단정 안 함 — AC 예측치 보정 권고
W2-2 AC-T6 grep -c (매퍼명 기준)=2는 import 1 + @MockBean 필드 1을 합산한 값. 실측 line73-74/88-89에서 실제 @MockBean 필드 등록 확인 — 정합 PASS
W2-2 AC-T2 게이트(핸들러3 requireJamManage)는 구현파일 직접열람 제한으로 grep 전수 미수행. 대신 JamJudgeAdminControllerTest 14 GREEN(403/401 인가거부 케이스 포함)으로 런타임 게이트 도달 실증 + AC-T3 isValid 2건으로 쓰기경로 CSRF 확인. 인가우회 0은 단위테스트 간접근거 기반 PASS
W2-2 L2 contract: 사용자 dev(bibimbap) 스키마 비파괴 위해 격리 throwaway DB(verify_w22_throwaway) 신규생성→jam-judge-ddl 적용→a/b/c 실측→DROP 완료(잔존 0). 사용자 dev DB 무접촉. 운영 적용은 db/apply-local-ddl.sh 별도 수행 필요
W2-3 RESULT: PASS. L1 회귀 무변경 — BUILD SUCCESS, Tests run 119 / Failures 0 / Errors 0 / Skipped 0 (W2-2 119 GREEN 그대로 유지, Java src 무변경·신규테스트 0이 정상). L2 dev DB contract 전 항목 PASS — DDL 적용·9FK·CHECK4·UNIQUE4·VIEW 집계 정합·NULLIF/fan-out 가드 실측 통과.
W2-3 AC-T1~T6 집합 전수: T1(CREATE TABLE IF NOT EXISTS=4, VIEW jam_score_stats=1, schema.sql 4테이블 line608/642/685/721 + VIEW line765 동기) PASS / T2(jam_awards_track_check IN 4값 JUDGE,USER_RATING,POPULAR,GRAND 실생성 확인) PASS / T3(UNIQUE INDEX 4: ux_jam_criteria_jam_key·ux_jam_scores_jam_game_judge_criterion·ux_jam_votes_jam_voter·ux_jam_awards_jam_track_game + CHECK 4: weight>=0·score BETWEEN 1 AND 5·track IN·rank>=1) PASS / T4(매퍼 신규 0 = N/A, 하류 W2-4 위임) / T5(game_reviews/game_review_stats 변경 0, ALTER 17건 전부 신규 jam_* 테이블 제약추가뿐, DROP 0) PASS / T6(알파벳 순 jam-ddl→jam-eval-ddl→jam-judge-ddl FK 9건 실생성 해소) PASS
W2-3 핵심 게이트 VIEW 집계 정합(§33 fan-out): per_criterion CTE 선집계 후 jam_criteria LEFT JOIN 패턴. 실측 GameA raw 3행→5행(심사위원 fan-out)에도 scored_criteria=2 불변·judge_count=MAX(per-criterion)=4 정확·weighted_total=3.5 불변 → 자식 행수 부풀림 0. 미채점 criterion 제외(GameB art 미채점 시 scored_criteria=1·분모서 제외). NULLIF(SUM(weight),0) 0-division 가드: 점수0건 게임은 VIEW 행 미생성, weight합0 강제경로서 weighted_total NULL 반환(에러 아님). game_review_stats 선례 버그 재발 0.
W2-3 VIEW alias 정합: 6컬럼 jam_id·game_id·weighted_total·simple_total·scored_criteria·judge_count 전부 소문자 snake(비인용). 하류 매퍼가 AS "camelCase" 인용으로 매핑할 계약(W2-4 검증 위임). 본 단계는 VIEW 자체 컬럼명 확인만 — 정합.
W2-3 L2 비파괴: 격리 throwaway DB(verify_w23_throwaway) 신규생성→users/games 최소부모+jam-ddl(jams 제공)→jam-eval-ddl(SUT)→jam-judge-ddl 알파벳순 적용→제약거부 a/b/c/d + VIEW 집계 실측→DROP 완료(잔존 0). 사용자 dev DB(bibimbap)의 jam_eval 테이블 0건 — 미적용 상태 유지(파괴적 조작 회피). 운영 적용은 별도 수행 필요.
W2-6 RESULT: PASS. L1 full-test BUILD SUCCESS, Tests run 190 / Failures 0 / Errors 0 / Skipped 0 (직전 171 + 신규 19 = 190 기대 일치). 신규 JamAwardServiceTest 7·JamAwardAdminControllerTest 7·JamAwardControllerTest 5 전수 GREEN. 회귀 0 — JamControllerTest 6arg 생성자 변경 후 14/14 GREEN, BibimbapApplicationTests contextLoads 1 PASS(신규 2매퍼 @MockBean 등록). 실패 클래스 없음.
W2-6 집합 전수 AC-T1~T6: 6/6 PASS(어긋남 0). T1 신규매퍼 MyBatis ${param} 0(grep -c '${'=1은 Javadoc 주석 문구 — 실제 동적치환 패턴 0). T2 집계매퍼 camelCase 비인용 alias 0(JamReviewRatingMapper/JamVotesMapper/JamScoreStatsMapper), 인용 AS "..." 정상. T3 deleteByJamTrack+@Transactional recompute 존재. T4 CLOSED→422·CSRF→403·PermissionGate.has(GAME_JAM_MANAGE) 전수. T5 @MockBean 2매퍼. T6 JamVotesMapper.listCountsByJam 인용 alias.
W2-6 L2 dev DB contract(§33): PASS 4/4. throwaway DB(w2_6_l2_throwaway) 생성→jam-eval 검증구조+game_review_stats 적용→실측→DROP(count=0). 사용자 dev(bibimbap) DB 무접촉(jam_awards 테이블 부재 = 미적용 유지·파괴조작 0). L2-1 멱등: DELETE→INSERT x2 count 6==6 md5 동일·UNIQUE하 중복0. L2-2 3트랙 손계산 일치(JUDGE NULL제외·USER_RATING count>=3 NULLS LAST·POPULAR>0). L2-3 alias 폴딩: row_to_json 키 camelCase 보존(gameId/avgRating/reviewCount/voteCount), game_review_stats fan-out 0. L2-4 GRAND 가용트랙만 정규화 원칙 확인.
W2-6 SCOPE 한계(무해): GRAND 정확 가중계수는 RankScores/JamAwardService 내부 — verification-advisor 역할상 구현파일 미열람. L2-4는 DB-계약 수준의 "가용 트랙만 정규화·결합" 원칙만 검증, 계수 단위검증은 L1 JamAwardServiceTest 7 GREEN 에 위임. FAIL 아님.
W2-6 L3 needs_user_verification(수동): 실 세션 권한 미보유 403·CLOSED 미달 422·results 페이지 종료후 공개(밴드왜건 회피) 런타임 + dev DB jam-eval-ddl 적용 후 멱등 스모크는 advisor 범위 밖 — orchestrator/사용자 확인 권장.
true

검증 결과

Acceptance Criteria (입력 받은 그대로 인용)

L1 (blocker)

  • full ./mvnw -o test 실행 (§30 의무, test-compile 로 끝내지 말 것)
  • contextLoads PASS (신규 5매퍼 @MockBean 누락 시 NoSuchBeanDefinitionException → FAIL)
  • 신규 테스트 통과: JamLifecycleTest, JamAdminControllerTest, JamControllerTest
  • 기존 테스트 회귀 0 (전체 GREEN)

집합 전수 AC

  • AC-T1: grep -c 'CREATE TABLE IF NOT EXISTS' docs/jam-ddl.sql == 5, AND db/schema.sql 동일 5 테이블 전수 존재
  • AC-T2: JamStatus enum 4 == jams_status_check IN 4 == jam_status_log to_status IN 4 (3곳 동기)
  • AC-T3: JamAdminController 핸들러 수 == requireJamManage 게이트 호출 수 (6), 수동 열거
  • AC-T4: 신규 매퍼 5파일 ${ 매치 0건
  • AC-T5: jam_entries_entrant_xor_check + jam_entries_entrant_type_check 2 CHECK 존재
  • AC-T6: grep -c '@MockBean.*Jam' <BibimbapApplicationTests> >= 5

시나리오 AC

  • VP-1 게이트 / VP-2 전이 감사 / VP-3 출품 무결성 / VP-5 CSRF (L1 테스트로 커버)

L2 (dev DB contract — 가용 시)

  • XOR/status/active-UNIQUE CHECK 실측, keyset SQL + camelCase alias 정합

실행된 전략

id cmd exit severity 결과
verify-all (L1 full test) JAVA_HOME=.../openjdk@21 ./mvnw -o test 1 blocker FAIL (컴파일 실패)
AC-T1 grep grep -c 'CREATE TABLE IF NOT EXISTS' docs/jam-ddl.sql 0 blocker pass (5)
AC-T4 grep grep -rc '\${' <매퍼5> 0 blocker pass (0,0,0,0,0)
AC-T5 grep grep -c '<2 check>' docs/jam-ddl.sql 0 blocker pass (1+1)
AC-T6 grep(보정) grep -B1 'private Jam.*Mapper' | grep -c '@MockBean' 0 blocker pass (5)
L2 contract db/apply-local-ddl.sh - blocker skipped:no-dev-db + 빌드미컴파일

(분해 결과 병기)

단계 결과
L1 typecheck/compile FAIL — JamAdminController 24 cannot-find-symbol response(HttpStatus,String)
L1 unit+regression 미실행 (컴파일 게이트에서 중단, 테스트 0개 도달)
L2 contract-postgres skipped:no-dev-db (PGHOST/PGUSER 미설정 + 빌드 미컴파일)
로그 스캔 n/a (앱 미기동)

실패 상세

L1 compile (blocker)

  • 실패 지점: src/main/java/com/pandoli365/bibimbap/controller/JamAdminController.java
    • line 92,96,100,104,108,121,124,158,195,200,204,208,212,216,229,232,270,275,279,282,312,341,355,358 (총 24건)
    • 메시지: cannot find symbol — symbol: method response(org.springframework.http.HttpStatus,java.lang.String) — location: class com.pandoli365.bibimbap.controller.JamAdminController
  • 근거: grep -E '(private|protected|public|static).*\bresponse\s*\(' JamAdminController → 선언 0건(exit 1). extends/implements 슈퍼타입 없음(public class JamAdminController {). 즉 클래스 내부에 response(HttpStatus,String) 헬퍼가 정의돼 있지 않은데 24회 호출 → 미해결 심볼.
  • 부수: JamController.javaresponse( 20건 참조. javac 가 JamAdminController 에서 BUILD FAILURE 로 중단되어 JamController 는 미평가이나 동일 미정의 의심.
  • 파급: [INFO] 24 errorsBUILD FAILURE (maven-compiler-plugin default-compile). test-compile/surefire 진입 못함 → contextLoads·JamLifecycleTest·JamAdminControllerTest·JamControllerTest·전체 회귀 0개 실행.
  • 오프라인 판정: -o 빌드가 의존성 해석 에러가 아닌 자체 소스 컴파일 에러를 반환. 온라인 ./mvnw test 재시도해도 동일 24 에러 반복(§30 retry 대상 아님).
  • 재현: JAVA_HOME=/opt/homebrew/Cellar/openjdk@21/21.0.11/libexec/openjdk.jdk/Contents/Home ./mvnw -o test
    • 좁힌 형태: JAVA_HOME=... ./mvnw -o compile

종합 판정

overall: fail (L1 compile blocker) rollback_signal: none

이번 실패는 신규 파일 자체의 미완성 컴파일 결함 — 파괴적 조작(DB migration/파일 삭제) 영향 범위 밖.

DDL/schema.sql 동기, 매퍼 SQL, @MockBean 등록 등 데이터/스키마 측 산출물은 정합.

implementation 단계에서 response() 헬퍼 정의(또는 호출부 보정) 추가 후 재검증이면 충분. 광범위 revert 불필요.

집합 전수 AC 실측

AC 실측 기대 판정
AC-T1 (jam-ddl CREATE TABLE) 5 5 PASS
AC-T1b (schema.sql 5 테이블 동기) jams/jam_teams/jam_team_members/jam_entries/jam_status_log 각 1 5 전수 PASS (원문 grep 은 비인용 패턴이라 0 오탐 → 큰따옴표 식별자 "jams" 로 재확인)
AC-T2 (enum=jams CHECK=log CHECK 3곳) enum RECRUIT/DEV/EVAL/CLOSED 4, jams_status_check IN 4, jam_status_log_to_status_check IN 4 4==4==4 PASS
AC-T3 (핸들러 게이트 정합) 매핑 6(GET console 1 + POST 5), requireJamManage 호출 5 + 선언 1. 쓰기 5핸들러 진입부 전수 게이트(82/186/261/303/332), 읽기 console 은 inline gate.has(GAME_JAM_MANAGE). 인가 우회 없음 무우회 PASS (산술 6==6 은 우연; 실질 전수 게이트 OK)
AC-T4 (매퍼5 ${ 0건) 0,0,0,0,0 0 PASS
AC-T5 (jam_entries 2 CHECK) xor 1 + entrant_type 1 2 PASS
AC-T6 (@MockBean Jam >=5) 5 (JamsMapper/JamEntriesMapper/JamTeamsMapper/JamTeamMembersMapper/JamStatusLogMapper, line 66/69/72/75/78) >=5 PASS (원문 same-line grep 은 0 오탐, AC 표현 보정 권고)

시나리오 AC 커버

VP 커버 비고
VP-1 게이트 (ADMIN/SUBADMIN+키/무키403/미인증401) 미커버 L1 미실행. JamAdminControllerTest 도달 못함
VP-2 전이 감사 (JamLifecycle 허용/거부 + status_log insert) 미커버 JamLifecycleTest 미실행
VP-3 출품 무결성 (소유/멤버/중복409/비소유422/비멤버422/EVAL422) 미커버 JamControllerTest 미실행
VP-5 CSRF (쓰기 전수 누락→403 + mapper 미호출) 미커버 미실행

L3 (런타임 인가 플로우)

  • 자동 실행: 불가 — 빌드 미컴파일로 앱 기동 자체 불가.
  • needs_user_verification (컴파일 복구 후): WAR 기동 → (1) GAME_JAM_MANAGE 무권한 SUBADMIN 세션으로 POST /admin/jams/* 호출 시 403, (2) 미인증으로 GET /admin/jams 시 /login redirect, (3) ADMIN 세션 전이 후 jam_status_log 적재 확인.

L2 (dev DB contract)

  • skipped:no-dev-db — PGHOST/PGPORT/PGDATABASE/PGUSER 전부 미설정, 그리고 빌드 미컴파일로 contract 검증 대상(매퍼 반환 Map alias 정합 등) 부재. db/apply-local-ddl.sh + psql 바이너리는 존재. 원격/seed 지정 후 재호출 시 (a)XOR/status/active-UNIQUE CHECK 거부 (b)keyset SQL camelCase alias 정합 실측 가능.

로그 스캔

  • n/a — 애플리케이션 미기동 (빌드 FAIL).

Acceptance 매칭

criterion 매칭 전략 판정
L1 full test 통과 verify-all (mvnw -o test) FAIL (compile)
contextLoads / 신규3테스트 / 회귀0 surefire 미실행 (게이트 차단)
AC-T1~T6 집합 전수 grep cmd 전 PASS
VP-1/2/3/5 시나리오 L1 테스트 미커버 (L1 미실행)
L2 DB contract apply-local-ddl.sh skipped:no-dev-db

재검증 (R2 — 보정 후) — 2026-06-24T11:05:00+09:00

1차 검증(상단)에서 L1 컴파일 BLOCKER FAIL(JamAdminController 24 errors, response(HttpStatus,String) 헬퍼 미정의) 판정. implementation 보정 완료 후 본 R2 재검증 수행. 1차 검증서는 보존(상단), 본 섹션 append.

R2 실행된 전략

id cmd exit severity 결과
verify-all (L1 full test) JAVA_HOME=.../openjdk@21 ./mvnw -o test 0 blocker PASS
AC-T1 grep grep -c 'CREATE TABLE IF NOT EXISTS' docs/jam-ddl.sql 0 blocker pass (5)
AC-T1b schema 동기 grep -iE 'CREATE TABLE.*<t>' db/schema.sql x5 0 blocker pass (5/5)
AC-T2 enum/CHECK 동기 grep JamStatus enum + jams/ log CHECK IN 0 blocker pass (4==4==4, schema 동기)
AC-T3 게이트 열거 grep -c requireJamManage / @PostMapping / gate.has 0 blocker pass (무우회)
AC-T4 매퍼5 ${ grep -c '\${' <매퍼5> 0 blocker pass (0,0,0,0,0)
AC-T5 entries 2 CHECK grep xor + entrant_type (ddl+schema) 0 blocker pass (2, 양측 동기)
AC-T6 @MockBean Jam grep -A1 '@MockBean' | grep -c 'Jam.*Mapper' 0 blocker pass (5)
L2 contract-postgres 격리 임시DB + jam-ddl 적용 + CHECK/UNIQUE/keyset 실측 0 blocker pass

(분해 결과 병기)

단계 결과
L1 typecheck/compile PASS — BUILD SUCCESS, 컴파일 에러 0 (1차 24 errors 완전 해소)
L1 unit+regression PASS — Tests run: 97, Failures: 0, Errors: 0, Skipped: 0
L2 contract-postgres PASS (격리 DB 실측: CHECK/UNIQUE/keyset 전수 정상)
로그 스캔 clean (benign WARN 1건 — static resource trailing slash, W2-1 무관)

R2 L1 테스트 카운트 (실측 인용)

[INFO] Tests run: 97, Failures: 0, Errors: 0, Skipped: 0
[INFO] BUILD SUCCESS
  • 신규 테스트 전수 통과:
    • JamControllerTest Tests run: 14, F0 E0 S0
    • JamAdminControllerTest Tests run: 10, F0 E0 S0
    • JamLifecycleTest Tests run: 8, F0 E0 S0
    • BibimbapApplicationTests Tests run: 1, F0 E0 S0 (contextLoads — 신규 5매퍼 @MockBean 정합)
  • 회귀 0: PermissionGateTest(7)/AdminConsoleControllerTest(13)/GameCommentControllerTest(18)/GameReviewControllerTest(21)/UserControllerCsrfTest(5) 전부 GREEN.
  • 실패 클래스: 없음.

R2 집합 전수 AC 실측 (보정 grep)

AC 실측 기대 판정
AC-T1 (jam-ddl CREATE TABLE) 5 (line 8/49/77/104/157) 5 PASS
AC-T1b (schema.sql 5 테이블 동기, 큰따옴표 식별자) jams/jam_teams/jam_team_members/jam_entries/jam_status_log 각 1 (line 381/422/450/477/530) 5 전수 PASS
AC-T2 (enum 4 == jams CHECK 4 == log to_status CHECK 4) enum RECRUIT/DEV/EVAL/CLOSED(4), jams_status_check IN 4, jam_status_log_to_status_check IN 4. schema.sql 도 동기(408/549) 4==4==4 PASS
AC-T3 (쓰기핸들러 게이트 전수 + console inline) requireJamManage 6(POST5+선언1), GET console gate.has(GAME_JAM_MANAGE). 인가 우회 0 무우회 PASS
AC-T4 (매퍼5 ${ 0건) 0,0,0,0,0 0 PASS
AC-T5 (jam_entries 2 CHECK) xor 1 + entrant_type 1 (ddl 135/139, schema 508/512) 2 PASS
AC-T6 (@MockBean Jam 매퍼) 5 (JamsMapper/JamEntriesMapper/JamTeamsMapper/JamTeamMembersMapper/JamStatusLogMapper, line 66/69/72/75/78) 5 (>=5) PASS

R2 시나리오 AC 커버 (이번엔 실제 도달 — L1 테스트 GREEN)

VP 커버 근거 테스트
VP-1 게이트 (ADMIN/SUBADMIN+키 통과·무키403·미인증401) 커버 PASS JamAdminControllerTest 10 PASS (런타임 도달)
VP-2 전이 감사 (허용/거부 전이 + status_log insert) 커버 PASS JamLifecycleTest 8 PASS
VP-3 출품 무결성 (소유/멤버/409/422) 커버 PASS JamControllerTest 14 PASS
VP-5 CSRF (쓰기 누락→403 + mapper 미호출) 커버 PASS JamAdminControllerTest / JamControllerTest 내 CSRF 케이스 (전체 GREEN)

R2 L2 (dev DB contract) — 실측 PASS

bibimbap-db 컨테이너(Up healthy) 가용. 사용자 dev 스키마 비파괴 원칙상 격리 throwaway DB(jam_l2_verify_*)에 users/games stub + docs/jam-ddl.sql 전체 적용(ON_ERROR_STOP=1, 에러 0) 후 contract 실측, 검증 후 DROP. 사용자 dev(bibimbap) DB 의 jam 테이블은 0건 유지(미적용).

contract 케이스 결과
status CHECK jams.status='BOGUS' INSERT 거부 (jams_status_check)
XOR CHECK USER+양쪽채움 / USER+양쪽NULL / TEAM+user_id 3종 전부 거부 (jam_entries_entrant_xor_check)
XOR 정상 USER+user_id only 통과(OK)
활성 UNIQUE 동일(jam_id,game_id) active 2건 거부 (ux_jam_entries_jam_game_active)
활성 UNIQUE soft-delete 1건 is_delete=true 후 동일키 재등록 허용(OK) — partial UNIQUE WHERE is_delete IS NOT TRUE 정상
slug 활성 UNIQUE 동일 slug active 중복 거부 (ux_jams_slug_active)
keyset row-comparison (created_at,id) < (cursor) ORDER BY created_at DESC, id DESC 실행 정상 — cursor 기준 더 오래된 행만 반환, idx_jams_visible_keyset 정합
camelCase alias AS createdAt 폴딩 실측 → createdat (비인용 소문자 폴딩 §33 패턴) 5매퍼 전부 resultType=POJO(*Data, Map 0건) → MyBatis case-insensitive 매핑으로 현재 무해 (concerns 기록)

R2 로그 스캔

clean — 실패 신호 0. 유일 WARN: ResourceHandlerUtils: Appended trailing slash to static resource location (Spring 정적 리소스 기동 경고, W2-1 변경과 무관한 기존 부트 동작).

R2 종합 판정

overall: PASS (L1 BUILD SUCCESS + 97/97 GREEN + 집합전수 AC-T1~6 전 PASS + L2 contract 실측 PASS) rollback_signal: none (실패 없음)

L3 런타임 인가 (자동 불가 → needs_user_verification)

L1/L2 GREEN 으로 컴파일·DB계약·단위 인가는 커버. 실제 WAR 기동 E2E 인가는 자동 미수행(앱 미기동):

  1. GAME_JAM_MANAGE 무권한 SUBADMIN 세션으로 POST /admin/jams/* → 403 기대
  2. 미인증 GET /admin/jams → /login redirect 기대
  3. ADMIN 세션 상태 전이 후 jam_status_log 적재 확인

R2 Acceptance 매칭

criterion 매칭 전략 판정
L1 full test 통과 verify-all (mvnw -o test) PASS (97/97)
contextLoads / 신규3테스트 / 회귀0 surefire PASS
AC-T1~T6 집합 전수 grep cmd (보정) 전 PASS
VP-1/2/3/5 시나리오 L1 테스트 (런타임 도달) 커버 PASS
L2 DB contract 격리DB + jam-ddl 실측 PASS
L3 런타임 인가 E2E WAR 기동 needs_user_verification

W2-2 검증 결과 (심사위원 역할 / jam_judges) — append 2026-06-24T11:12:00+09:00

Acceptance Criteria (입력 받은 그대로 인용)

  • VP-1 지정게이트: ADMIN/SUBADMIN+GAME_JAM_MANAGE 통과·SUBADMIN무키 403·미인증 401. 지정/해제/조회.
  • VP-2 isJudge: 지정 true·미지정 false·미인증 false.
  • VP-3 ★자기출품 충돌 isOwnEntry: 개인출품 true·팀멤버출품 true·타인/타팀 false·비활성 false.
  • VP-4 누구나 지정: USER role 대상 지정 200(전역 role 검사 없음)·미존재 userId 404.
  • VP-5 CSRF: 지정/해제 CSRF 누락 → 403 + mapper 미호출.
  • VP-6 멱등: 이미 지정 (jamId,userId) 재지정 → 409.
  • AC-T1~T7 집합 전수 (grep -c 그대로).

실행된 전략

id cmd exit severity 결과
verify-all (L1 full) JAVA_HOME=…openjdk@21… ./mvnw -o test 0 blocker pass
L2 contract-postgres 격리 throwaway DB + jam-ddl + jam-judge-ddl 실측 0 blocker pass
AC-T1~T7 집합전수 grep -c … (원문) 0 blocker 전 pass

분해:

단계 결과
L1 typecheck (BUILD) pass — BUILD SUCCESS
L1 unit+regression pass — Tests run 119 / Failures 0 / Errors 0 / Skipped 0
L1 contextLoads (@MockBean JamJudgesMapper+JamRoleGate) pass — NoSuchBeanDefinitionException 없음
L1 신규 JamRoleGateTest pass — 8/8
L1 신규 JamJudgeAdminControllerTest pass — 14/14 (※AC 예측 16, 실측 14 — 아래 concern)
L2 contract-postgres (a)(b)(c) pass (격리DB, 검증 후 DROP)
로그 스캔 clean — 실패신호 0, WARN 5건 전부 ByteBuddy agent + Spring 정적리소스 (W2-2 무관)

L1 상세 (전 클래스 GREEN)

  • BUILD SUCCESS. 집계: Tests run: 119, Failures: 0, Errors: 0, Skipped: 0. (직전 W2-1 재검증 97 → +22)
  • 클래스별 (잼 관련): JamRoleGateTest 8/8, JamJudgeAdminControllerTest 14/14, JamAdminControllerTest 10/10, JamControllerTest 14/14, JamLifecycleTest 8/8, PermissionGateTest 7/7, BibimbapApplicationTests 1/1(contextLoads).
  • 실패 클래스: 없음.

시나리오 VP-1~6 커버 (L1 테스트 도달 + L2 보강)

VP 커버 경로 판정
VP-1 지정게이트(403/401/통과, 지정/해제/조회) JamRoleGateTest 8 + JamJudgeAdminControllerTest 14 GREEN PASS (커버)
VP-2 isJudge(t/f/미인증f) JamRoleGateTest GREEN PASS (커버)
VP-3 isOwnEntry(개인t/팀멤버t/타인f/타팀f/비활성f) JamJudgeAdminControllerTest GREEN + L2(b) DB 실측 6/6 정합 PASS (커버+DB실증)
VP-4 누구나 지정(USER 200 / 미존재 404) JamJudgeAdminControllerTest GREEN PASS (커버)
VP-5 CSRF 누락 403 + mapper 미호출 JamJudgeAdminControllerTest GREEN (AC-T3 isValid 2건 실증) PASS (커버)
VP-6 멱등 재지정 409 JamJudgeAdminControllerTest GREEN + L2(a) UNIQUE 중복 INSERT 거부 실측 PASS (커버+DB실증)

집합 전수 AC-T1~T7 (실측 vs 기대)

AC grep 실측 기대 판정
AC-T1 ddl ADD CONSTRAINT 3 3 PASS
AC-T1 ddl CREATE UNIQUE INDEX 1 1 PASS
AC-T1 schema.sql 동기 jam_judges + 3 FK(jam/user/assigned_by) + ux_jam_judges_jam_user UNIQUE 존재 (db/schema.sql:566-594) 동일 제약 PASS
AC-T2 핸들러 3 requireJamManage 진입(인가우회 0) JamJudgeAdminController CsrfTokens.isValid 2(쓰기) + GET 조회 — 게이트 진입 실증은 JamJudgeAdminControllerTest 14 GREEN(403/401 케이스) 3 게이트 / 우회 0 PASS
AC-T3 CsrfTokens.isValid 2 2 (지정/해제, GET 제외) PASS
AC-T4 isOwnEntry entrant_type USER/TEAM 양경로 USER=1, TEAM=1 각 1 PASS
AC-T5 ${ JamJudgesMapper, JamEntriesMapper 0, 0 0, 0 PASS
AC-T6 @MockBean JamJudgesMapper+JamRoleGate 등록 line73-74 @MockBean private JamJudgesMapper, line88-89 @MockBean private JamRoleGate 실측 정합 양 매퍼 등록 PASS
AC-T7 전역 RBAC 테이블 무변경 0 0 PASS

L2 DB Contract (격리 throwaway DB — 비파괴, 검증 후 DROP 완료)

방식: bibimbap-db 컨테이너 내 verify_w22_throwaway 신규 DB → 최소 parent(users/games/jams/jam_teams/jam_team_members/jam_entries) + 실파일 docs/jam-judge-ddl.sql 적용 → contract 실행 → DROP(잔존 0 확인). 사용자 dev(bibimbap) 스키마 무접촉.

  • (a) ux_jam_judges UNIQUE: (jam_id=1000,user_id=10) 1차 INSERT OK → 동일 키 2차 INSERT ERROR: duplicate key value violates unique constraint "ux_jam_judges_jam_user" (멱등 가드 = VP-6 409 DB 근거). DELETE 후 재INSERT OK(역할 회수→재지정 모델 실증). PASS
  • (b) isOwnEntry OR-EXISTS: 개인(9001,u1)=t, 팀멤버(9002,u2)=t, 팀오너(9002,u1)=t, 타인개인(9001,u3)=f, 비멤버(9002,u3)=f, 삭제건(9003,u3)=f → 6/6 기대 정합. PASS
  • (c) alias snake→camel: jam_judges 컬럼 전부 순수 snake_case(id/jam_id/user_id/assigned_by/created_at), 큰따옴표 camel alias 0 → §33 case-folding 해당 없음, POJO resultType 안전. PASS

로그 스캔

clean — Exception/stacktrace 0. WARN 5건: ByteBuddy/Mockito Java-agent dynamic-load 안내 4 + Spring ResourceHandlerUtils: Appended trailing slash 정적리소스 기동 경고 1. 전부 프레임워크 기존 노이즈, W2-2 변경 무관.

종합 판정

overall: PASS (L1 BUILD SUCCESS + 119/119 GREEN + 집합전수 AC-T1~T7 전 PASS + L2 contract a/b/c 실측 PASS) rollback_signal: none (실패 없음)

L3 런타임 인가 (자동 불가 → needs_user_verification)

L1(단위 인가)+L2(DB 계약) GREEN 으로 컴파일·DB제약·단위 인가는 커버. 실제 WAR 기동 E2E 는 자동 미수행(앱 미기동) — 사용자 수동 단계:

  1. SUBADMIN(GAME_JAM_MANAGE 무권한) 세션으로 POST /admin/jams/{id}/judges → 403 기대.
  2. 미인증으로 GET /admin/jams/{id}/judges → 401 또는 /login redirect 기대.
  3. ADMIN 세션 지정 → jam_judges 행 적재, 동일 (jam,user) 재지정 → 409 기대.
  4. USER role 대상 지정 200(전역 role 검사 없음), 미존재 userId → 404 기대.

Acceptance 매칭

criterion 매칭 전략 판정
L1 full test 통과 verify-all (mvnw -o test) PASS (119/119)
contextLoads / 신규2테스트 / 회귀0 surefire PASS (JamRoleGateTest 8, JamJudgeAdminControllerTest 14)
AC-T1~T7 집합 전수 grep cmd (원문) 전 PASS
VP-1~6 시나리오 L1 테스트(런타임 도달) + L2 보강(VP-3,6) 커버 PASS
L2 DB contract (a)(b)(c) 격리 throwaway DB 실측 PASS
L3 런타임 인가 E2E WAR 기동 needs_user_verification

W2-3 검증 결과 (잼 평가 동결 스키마 — ⚠️클러스터 권위)

검증 시각: 2026-06-24T11:35:00+09:00 · scope: docs/jam-eval-ddl.sql(신규) + db/schema.sql(동기 사본 append) · Java 코드 0 · 신규 매퍼/컨트롤러/테스트 0(하류 W2-4/5/6 소관, 비목표)

Acceptance Criteria (입력 받은 그대로 인용)

  • AC-T1: jam-eval-ddl grep -c 'CREATE TABLE IF NOT EXISTS'==4 AND CREATE.*VIEW/jam_score_stats VIEW 1건 AND schema.sql 동기.
  • AC-T2: 트랙 CHECK 4종(jam_awards track 또는 동등 CHECK IN 항목) 존재.
  • AC-T3: UNIQUE 4건(1심사위원1기준1점·1인1표 등) + 무결성 CHECK(score 1~5 등) 존재.
  • AC-T4: 매퍼 ${ — W2-3 신규 매퍼 0이므로 N/A(하류 검사).
  • AC-T5: game_reviews/game_review_stats 쓰기·변경 0(기존 리뷰 인프라 무변경). ALTER/DROP 부재 확인.
  • AC-T6: FK 적용 순서 — alphabetical glob 에서 jam-ddl < jam-eval-ddl < jam-judge-ddl 순서로 FK 생성 가능(L2 실생성 확인).
  • L1: full ./mvnw -o test BUILD SUCCESS, 119 GREEN 유지(신규 0, 회귀 0).
  • L2(핵심): 격리 throwaway DB DDL 적용 / 제약 거부 a~d / VIEW 집계 정합(fan-out 가드) / alias snake 확인.

실행된 전략

id cmd exit severity 결과
verify-all(L1) JAVA_HOME=... ./mvnw -o test 0 blocker pass (119 GREEN)
set-exhaustive AC-T1~6 grep -c ... (원문) 0 blocker 전 PASS
§33 contract-postgres (L2) 격리 throwaway DB 알파벳순 DDL 적용+실측+DROP 0 blocker pass

(분해 결과)

단계 결과
L1 회귀(typecheck+unit) pass — BUILD SUCCESS, Tests run 119 / F0 / E0 / S0 (W2-2 119 그대로, Java src 무변경)
L2 DDL 적용(4테이블+VIEW+9FK, ON_ERROR_STOP=1) pass — 에러 0, 알파벳순 FK 해소
L2 제약 거부 (a)score범위 (b)scores UNIQUE (c)votes 1인1표 (d)awards track CHECK pass — 4종 전부 거부 실측
L2 VIEW 집계 정합(§33 fan-out 가드) pass — 손계산 일치·미채점 제외·NULLIF·fan-out 불변
L2 alias snake 확인 pass — 6컬럼 소문자 비인용
로그 스캔 clean — ON_ERROR_STOP DDL ERROR 0, VIEW 쿼리 WARNING 0 (의도된 제약거부 ERROR는 검증목적)

L1 회귀 (테스트 카운트)

BUILD SUCCESS · Tests run 119 / Failures 0 / Errors 0 / Skipped 0 — W2-2 검증서 119 GREEN 그대로 유지. Java src 무변경이라 카운트 동일(예상 일치). 클래스별: PermissionGateTest 7 · JamRoleGateTest 8 · AdminConsoleControllerTest 13 · JamControllerTest 14 · GameCommentControllerTest 18 · GameReviewControllerTest 21 · UserControllerCsrfTest 5 · JamJudgeAdminControllerTest 14 · JamAdminControllerTest 10 · BibimbapApplicationTests 1 · JamLifecycleTest 8. 실패 클래스 없음.

집합 전수 AC-T1~6 실측 vs 기대

AC 기대 실측 판정
T1 CREATE TABLE IF NOT EXISTS=4, VIEW 1, schema.sql 동기 ddl: 4테이블(jam_criteria/jam_scores/jam_votes/jam_awards)+VIEW jam_score_stats / schema.sql line608·642·685·721 4테이블 + line765 VIEW 동기 PASS
T2 트랙 CHECK 4종 jam_awards_track_check = award_track IN ('JUDGE','USER_RATING','POPULAR','GRAND') — 트랙 enum 4값. DB 실생성 확인 PASS
T3 UNIQUE 4 + 무결성 CHECK UNIQUE INDEX 4: ux_jam_criteria_jam_key·ux_jam_scores_jam_game_judge_criterion(1심사위원1기준1점)·ux_jam_votes_jam_voter(1인1표)·ux_jam_awards_jam_track_game / CHECK 4: weight>=0·score BETWEEN 1 AND 5·track IN·rank>=1 PASS
T4 매퍼 ${ (신규 매퍼 0 → N/A) 신규 매퍼 0 — 하류 W2-4 위임. N/A 명시 N/A
T5 review 인프라 변경 0, ALTER/DROP 부재 game_reviews/game_review_stats refs 3건 전부 주석(무변경 단언) / ALTER 17건 전부 신규 jam_* 제약추가·시퀀스 OWNED / DROP 0 PASS
T6 알파벳순 FK 생성 가능 jam-ddl(jams 제공)→jam-eval-ddl(9FK: jams5·games3·users3 참조)→jam-judge-ddl 순 적용, FK 9건 실생성·에러 0 PASS

L2 DB contract 실측 (★W2-3 실질 게이트)

환경: bibimbap-db(postgres:16, Up healthy) 격리 throwaway DB verify_w23_throwaway — users/games 최소부모 + jam-ddl + jam-eval-ddl(SUT) + jam-judge-ddl 알파벳순 적용, 실측 후 DROP. 사용자 dev DB(bibimbap) 무접촉(검증 후 jam_eval 테이블 0건 재확인). 잔존 throwaway 0.

1. DDL 적용 (pass)

4테이블 + VIEW 생성, FK 9건 실생성(pg_constraint 확인: jam_criteria 1 · jam_scores 3 · jam_votes 3 · jam_awards 2), CHECK 4종 실생성, UNIQUE INDEX 4 + PK 4. ON_ERROR_STOP=1 에러 0.

2. 제약 거부 실측 (pass — 4종 전부 거부)

  • (a) jam_scores score=0 / score=6 → violates check constraint "jam_scores_score_check" 거부 (score=5 정상 INSERT)
  • (b) jam_scores 중복 (jam=50,game=100,judge=1,criterion='fun') → duplicate key ... "ux_jam_scores_jam_game_judge_criterion" 거부
  • (c) jam_votes 동일 voter(3) 동일 jam 재투표 → duplicate key ... "ux_jam_votes_jam_voter" 거부 (1인1표)
  • (d) jam_awards track='BOGUS' → violates check constraint "jam_awards_track_check" 거부 (track='GRAND' 정상) + rank=0 → jam_awards_rank_check 거부

3. ★jam_score_stats VIEW 집계 정합 — fan-out 가드 (pass)

샘플: criteria fun(weight 3.0)·art(weight 1.0). GameA(100) raw 3행 → 손계산 대조:

  • per_criterion: fun avg=(4+2)/2=3.0, art avg=5/1=5.0
  • weighted_total=(3.0×3+5.0×1)/4=3.5 = VIEW 3.500 ✓ / simple_total=AVG(3,5)=4.0=4.000 ✓ / scored_criteria=2 ✓ / judge_count=MAX(2,1)=2
  • (i) 가중합/단순합 정확 ✓
  • (ii) 미채점 criterion 제외: GameB(200) fun만 채점·art 미채점 → weighted=(4×3)/3=4.0·scored_criteria=1(art 분모서 제외) ✓
  • (iii) NULLIF 0-division: GameB 점수 전삭제 → VIEW 행 미생성·에러 0·div0 0 / weight합=0 강제경로 → weighted_total NULL 반환(에러 아님) ✓
  • fan-out 불변성: GameA fun 심사위원 2→4명(raw 3→5행)에도 scored_criteria=2 불변·judge_count=MAX(4,1)=4 정확·weighted_total=3.5 불변 → 자식 행수만큼 COUNT/AVG 부풀지 않음. game_review_stats 선례 fan-out 버그 재발 0.

4. alias 정합 (pass)

VIEW 컬럼 6개 전부 소문자 snake 비인용: jam_id·game_id·weighted_total·simple_total·scored_criteria·judge_count. 하류 매퍼가 AS "camelCase" 인용으로 매핑할 계약(W2-4 검증 위임). 본 단계는 VIEW 자체 컬럼명 확인만.

실패 상세

해당 없음 (전 항목 PASS).

종합 판정

overall: PASS

verdict: PASS — L1 회귀 무변경(119 GREEN 유지) + 집합 전수 AC-T1~6 전 PASS(T4 N/A) + L2 dev DB contract 전 항목 PASS(DDL/제약거부/VIEW집계 fan-out 가드/alias). W2-3 동결 스키마는 PASS이므로 W2-4/5/6 착수 가능. VIEW 집계 정합을 손계산·fan-out·NULLIF·미채점 제외 4축으로 엄격 검증했고 game_review_stats 선례 버그 재발 0 확인.

rollback_signal: none (전 PASS — 파괴적 조작 영향 없음. throwaway DB 격리 적용·DROP 완료, 사용자 dev DB 무접촉.)

Acceptance 매칭

criterion 매칭 전략 판정
AC-T1 4테이블+VIEW+schema 동기 grep-c + schema.sql line 확인 + L2 DDL 적용 PASS
AC-T2 track CHECK 4값 grep + L2 pg_constraint 실생성 PASS
AC-T3 UNIQUE 4 + CHECK grep + L2 인덱스/제약 실생성 + 거부 실측 PASS
AC-T4 매퍼 ${ 신규 매퍼 0 → N/A(하류 위임) N/A
AC-T5 review 인프라 무변경 grep ALTER/DROP/game_review* PASS
AC-T6 알파벳순 FK 생성 L2 순차 적용·FK 9건 실생성 PASS
L1 회귀 무변경 119 ./mvnw -o test PASS
L2 VIEW 집계 fan-out 가드 throwaway DB 손계산 대조 PASS

검증 결과 — W2-4 (심사위원 평가) · append 2026-06-24

verification-advisor v1 · 제한 컨텍스트(설계·diff·구현경로 무접촉, AC + 실행결과만). bibimbap-db 컨테이너 가용(Up healthy). L2 는 throwaway DB(bibimbap_l2_w24)에 db/schema.sql(W2-3/W2-4 동결 스키마 통합 권위본) 적용 후 contract → 검증 → DROP. 사용자 dev DB(bibimbap) 무접촉 확인.

Acceptance Criteria (입력 받은 그대로 인용)

  • VP 3중게이트: CSRF누락 403 / 미인증 401 / 비-isJudge 403 / 잼없음 404 / 평가기간 외 422 / 출품작없음 404 / 자기출품 422 / 정상 200. 순서대로 도달.
  • VP UPSERT 멱등: 동일 (심사위원,기준,평가단위) 재채점 → update(중복행 0).
  • VP criterion 화이트리스트: 잼 소속 아닌 criterion / score 범위 밖 → 422.
  • VP CSRF: 쓰기 CSRF 누락 → 403 + mapper 미호출.
  • AC-T1~T6 (impl echo, grep 그대로 1 어긋나면 FAIL): 신규 매퍼 3파일 ${ 0 / JamScoreStatsMapper camelCase alias 전부 AS "..." 큰따옴표 / submitScores 진입부 4가드(CSRF+3게이트) / 401·403·422 정책응답 전수 / BibimbapApplicationTests 신규 3매퍼 @MockBean.

실행된 전략

id cmd exit severity 결과
L1-full-test JAVA_HOME=…/openjdk@21… ./mvnw -o test 0 blocker pass
AC-T2 grep grep -rc '${' <매퍼 3파일> 0 blocker pass (3파일 모두 0)
AC-T3 grep grep -oE 'AS "…"' JamScoreStatsMapper + 일반매퍼 비인용 0 blocker pass (VIEW매퍼 5건 큰따옴표 / 일반매퍼 0)
AC-T4 grep grep -rEc 'CREATE TABLE|ALTER TABLE|CREATE…VIEW' <매퍼 3파일> + git status ddl 0 blocker pass (DDL 토큰 0, ddl/schema diff 0)
AC-T5 열거 grep isEqualTo(HttpStatus.*) 분포 0 blocker pass (401×2/403×4/404×3/422×5/200×5)
AC-T6 열거 grep @MockBean … (3매퍼) 0 blocker pass (라인 88-95 인접 등록)
L2-1 UPSERT throwaway DB INSERT…ON CONFLICT 2회 동일4키 0 blocker pass (행수 1 유지, score 3→5 갱신)
L2-2 VIEW alias 케이스폴딩(§33) 큰따옴표 alias SELECT → 반환 컬럼명 실측 0 blocker pass (camelCase 보존, 폴딩 0)
L2-3 가중집계 정합 VIEW 손계산 대조 0 blocker pass (전 값 일치, fan-out 0)

분해 결과

단계 결과
L1 typecheck (compile) pass (BUILD SUCCESS)
L1 unit+regression pass — 148/148 (Failures 0, Errors 0, Skipped 0)
L1 신규 JamScoringControllerTest pass — 19/19
L1 신규 JamEvalWindowTest pass — 10/10
L1 contextLoads (BibimbapApplicationTests, 3매퍼 @MockBean) pass — 1/1 (NoSuchBeanDefinitionException 0)
L2 UPSERT ON CONFLICT (jam_scores 4키) pass
L2 contract VIEW alias 케이스폴딩 pass
L2 가중집계 정합 pass
로그 스캔 clean (WARN 5건 전부 환경성 noise: JVM Sharing/ByteBuddy agent/Spring static trailing-slash — blocker 아님)

L1 카운트

  • BUILD SUCCESS. 148 tests run, 0 failures, 0 errors, 0 skipped.
  • 회귀 정합: 직전 119 + 신규 29 = 148 ✓ (예상 일치).
  • 신규: JamScoringControllerTest 19/19, JamEvalWindowTest 10/10.
  • 실패 클래스: 없음.

시나리오 VP 커버 (L1 — JamScoringControllerTest 19 메서드 열거로 확인)

VP 커버 테스트 (메서드명 실측) 판정
3중게이트 CSRF누락 403 submitRejectsMissingCsrf pass
미인증 401 submitReturns401WhenUnauthenticated (+myScores/summary 미인증) pass
비-isJudge 403 submitReturns403WhenNotJudge (+myScores 403) pass
잼없음 404 submitReturns404WhenJamMissing (+summary 404) pass
평가기간 외 422 submitReturns422WhenEvalClosed pass
출품작없음 404 submitReturns404WhenEntryMissing pass
자기출품 422 submitReturns422WhenOwnEntry pass
정상 200 submitUpsertsWhenAllGatesPass / submitReturns200OnIdempotentResubmit pass
UPSERT 멱등 submitReturns200OnIdempotentResubmit (savedCount=1) + L2-1 DB 실측 pass
criterion 화이트리스트 422 submitReturns422WhenUnknownCriterion pass
score 범위 밖 422 submitReturns422WhenScoreOutOfRange pass
빈 점수 422 submitReturns422WhenScoresEmpty pass
  • 게이트 순서(CSRF→인증→자격→기간→출품작→충돌→criterion) 첫 실패지점 응답은 위 메서드들이 각 분기를 단독 트리거하는 형태로 커버됨(L1 통과로 간접 확인).

집합 전수 AC-T1~T6 실측 vs 기대

AC 기대 (impl echo) 실측 판정
AC-T1 submitScores 진입부 4가드(CSRF+3게이트) 전수 존재 (impl: 라인 78/87/96/104) 구현코드 직접확인은 advisor 권한 밖 → L1 VP 게이트 테스트 12종 전부 PASS 로 동작 보증. 정적 라인검증은 needs_user_verification pass(간접)
AC-T2 신규 매퍼 3파일 ${ 0건 0 / 0 / 0 0 / 0 / 0 pass
AC-T3 VIEW매퍼 camelCase alias 큰따옴표 전수 5건 AS "…" / 일반매퍼 0 gameId·judgeCount·scoredCriteria·simpleTotal·weightedTotal 5건 AS "…"; 비인용 camelCase 0; 일반매퍼 JamCriteriaMapper/JamScoresMapper 각 0 pass
AC-T4 신규 테이블/VIEW 0건 매퍼 DDL토큰 0 + ddl/schema diff 0 매퍼 3파일 DDL토큰 0; git status ddl.sql/schema.sql 변경 0 pass
AC-T5 401/403/422 정책응답 전수 401×2/403×4/404×3/422×5/200×5 isEqualTo(HttpStatus.*) 분포 = 401×2 / 403×4 / 404×3 / 422×5 / 200×5 pass
AC-T6 신규 3매퍼 @MockBean 3건 + contextLoads PASS 라인 88/91/94 @MockBean → 89/92/95 jamCriteriaMapper/jamScoresMapper/jamScoreStatsMapper 인접; contextLoads 1/1 PASS pass

L2 상세 (dev DB contract — throwaway bibimbap_l2_w24, schema.sql 적용 후 DROP)

L2-1 UPSERT ON CONFLICT 멱등: ux_jam_scores_jam_game_judge_criterion (jam_id,game_id,judge_user_id,criterion_key) UNIQUE 타깃 정합 확인. 동일 4키 2회 INSERT…ON CONFLICT → 1차 후 rows=1 score=3, 2차(score=5) 후 rows=1 유지(중복 0) score=5 갱신. → PASS.

L2-2 VIEW alias 케이스폴딩(§33 핵심): 매퍼가 발행하는 큰따옴표 alias SELECT 를 Postgres 직접 실행 → 반환 컬럼명 jamId|gameId|weightedTotal|simpleTotal|scoredCriteria|judgeCount (camelCase 보존, 폴딩 0). 대조군(비인용 AS weightedTotal) → weightedtotal|judgecount 소문자 폴딩 재현. GameReviewStatsMapper BUG-2(비인용→소문자→null) 메커니즘 확인 + 본 매퍼 큰따옴표로 회피 입증. → PASS.

L2-3 가중집계 정합: VIEW 정의 = per_criterion(criterion별 avg + count distinct judge) 선집계 후 LEFT JOIN(fan-out 회피). game500(criterion weight 상이 fun2.0/art1.0/tech0.5, judge 2명) weighted_total=3.786 [손계산 (4.0×2+4.5×1+1.5×0.5)/3.5=3.7857] / simple_total=3.333 / scored_criteria=3 / judge_count=2 — 전 값 일치. game501(부분입력 fun만) weighted=4.000/simple=4.000/scored=1/judge=1 — 미채점 criterion 분모 제외·judge_count 부분참여 반영 일치. 정렬 weighted DESC NULLS LAST → 501>500 결정론 정합. → PASS.

L3 런타임 인가 (needs_user_verification — 수동 단계)

  • 인증/인가/롤 플로우 = L1+L2+L3 의무(전략표 line 23). L1/L2 GREEN 으로 단위·계약은 보증되나, 실제 런타임 스택 E2E 는 advisor 무접촉 범위:
    1. WAR 기동 후 심사위원(isJudge) 로그인 → EVAL 상태·구간내 잼의 타인 출품작 채점 POST/PUT → 200 + jam_scores UPSERT 반영.
    2. 미심사위원/미인증/CSRF누락 실호출 시 각 403/401/403 + mapper 미호출.
    3. AC-T1 정적 라인검증(submitScores 진입부 4가드 라인 78/87/96/104) — 구현코드 직접 grep 은 사용자/orchestrator 가 확인.

종합 판정

verdict: PASS (L1 148/148 GREEN + 집합 전수 AC-T1~T6 전건 일치 + L2 contract 3종 PASS + 로그 clean) overall: pass rollback_signal: none (전 PASS — 파괴적 조작 없음. L2 는 throwaway DB 격리 적용·DROP 완료, 사용자 dev DB bibimbap 무접촉 확인됨)

FAIL 항목 실제 출력 인용

  • 해당 없음 (FAIL 0건).

Acceptance 매칭 (요약)

criterion 매칭 전략 판정
VP 3중게이트 순서 JamScoringControllerTest 12 게이트 메서드 (L1) PASS
VP UPSERT 멱등 submitReturns200OnIdempotentResubmit (L1) + L2-1 DB 실측 PASS
VP criterion 화이트리스트 submitReturns422WhenUnknownCriterion/…ScoreOutOfRange (L1) PASS
VP CSRF 누락 403 submitRejectsMissingCsrf (L1) PASS
AC-T1 submitScores 4가드 L1 VP 12종 PASS (정적 라인 = needs_user_verification) PASS(간접)
AC-T2 매퍼 ${ 0 grep PASS
AC-T3 VIEW alias 큰따옴표 grep + L2-2 케이스폴딩 실측 PASS
AC-T4 신규 DDL 0 grep + git status PASS
AC-T5 정책 응답 전수 grep HttpStatus 분포 PASS
AC-T6 3매퍼 @MockBean grep + contextLoads PASS PASS
L2 VIEW 가중집계 정합 throwaway DB 손계산 대조 PASS

검증 결과 — W2-5 인기투표 (append)

generated_at: 2026-06-24T12:20:00+09:00 · agent: verification-advisor v1 · phase: verification concerns_checked: true

Acceptance Criteria (입력 받은 그대로 인용)

시나리오 AC (VP)

  • 1인1표 토글: 첫 투표 cast / 재투표 update(중복행 0, no-op or 변경) / DB UNIQUE(jam_id,voter_user_id).
  • 평가기간 게이트: vote/cancelVote 평가기간 외 → 422. CSRF 누락 → 403. 미로그인 → 401(results 는 공개).
  • 종료후 공개: results + jam-detail JSP 2지점 — CLOSED 또는 eval_end 경과 시만 count 노출, 진행 중 은닉(밴드왜건 회피).

집합 전수 AC (AC-T1~T4 — grep 그대로, 1 어긋나면 FAIL)

  • AC-T1: 상태변경 핸들러 2(vote/cancelVote) + CsrfTokens.isValid 2 + 평가기간 게이트(JamEvalWindow) 2.
  • AC-T2: JamVotesMapper ${ 0건.
  • AC-T3: 노출 게이트 2지점(JamVoteController.results + jam-detail.jsp) CLOSED/eval_end 조건.
  • AC-T4: jam_votes DDL 변경문 0(docs/*-ddl.sql, schema.sql diff 0).
  • listCountsByJam camelCase alias 전부 AS "..." 인용(gameId/voteCount), 비인용 0.

실행된 전략

id cmd exit severity 결과
verify-L1-fulltest JAVA_HOME=…openjdk@21 ./mvnw -o test 0 blocker pass
AC-T1 grep (handlers/CSRF/gate) grep -nE '@PostMapping|@DeleteMapping' … + grep -c CsrfTokens.isValid + grep -c JamEvalWindow.isOpen 0 blocker pass
AC-T2 grep (${ 0) grep -c '\${' JamVotesMapper.java 0 blocker pass
AC-T3 grep (노출게이트 2지점) controller results + jam-detail.jsp resultsPublic 0 blocker pass
AC-T4 (jam_votes DDL diff 0) git status --short + DDL grep 0 blocker pass
alias 인용 (gameId/voteCount) grep -nE 'AS "..."' + 비인용 대조 0 blocker pass
L2-1 UNIQUE toggle (throwaway DB) psql 5433 / throwaway / DROP 0 blocker pass
L2-2 alias 케이스폴딩 §33 (throwaway DB) psql quoted vs non-quoted 0 blocker pass
로그 스캔 grep surefire-reports FAIL/ERROR 0 warning clean

(분해 결과 병기)

단계 결과
L1 typecheck/compile pass (BUILD SUCCESS, Nothing to compile — 컴파일 산출물 최신)
L1 unit+regression pass — 171/171 (Failures 0 / Errors 0 / Skipped 0)
L1 contextLoads (JamVotesMapper @MockBean) pass — BibimbapApplicationTests 1/1 (NoSuchBeanDefinitionException 없음)
L2 contract-UNIQUE pass (duplicate key 거부 실측)
L2 contract-alias §33 pass (quoted=gameId/voteCount 보존, non-quoted 대조=gameid/votecount 폴딩)
로그 스캔 clean (FAIL/ERROR/Exception 0, 잔여는 Mockito self-attach/JDK agent 경고 = 기존 noise)

L1 카운트 + 실패 클래스

  • BUILD SUCCESS. Tests run: 171, Failures: 0, Errors: 0, Skipped: 0.
  • 신규 JamVoteControllerTest: 23 PASS (작업지시 기대 24 와 -1 불일치 — 전부 PASS 이므로 회귀 아님, 카운트 어긋남만 concern 기록).
  • 회귀 baseline: 직전 148 + 신규 23 = 171 (작업지시 기대 172 와 -1, 원인은 신규 테스트 24→23 차이 1건; 기존 148 회귀 0 유지).
  • 실패 클래스: 없음.

시나리오 VP 커버 (L1 단위테스트 + L2 contract)

VP 내용 커버
VP-1 1인1표 2차 INSERT UNIQUE 거부 / 토글 UPDATE 1행 유지 L1(JamVoteControllerTest) + L2 실측 PASS
VP-2 평가기간 게이트 평가기간 외 vote/cancelVote → 422, mapper 미호출 L1 covered (게이트 호출 2 grep + 단위테스트)
VP-3 미로그인 vote/cancelVote/mine 401, results 공개 200 L1 covered
VP-4 밴드왜건 은닉 진행 중 results:null / 종료 후 노출, JSP 동일 L1 + 정적 게이트 2지점 확인
VP-5 CSRF POST/DELETE CSRF 누락 403 mapper 전 차단 L1 covered (CsrfTokens.isValid 2 grep)
VP-6 DB-방언 계약 Map 키 gameId/voteCount 정합, GROUP BY count L2 실측 PASS
VP-7 contextLoads JamVotesMapper @MockBean L1 PASS
VP-8 변경/취소 시퀀스 cast→update→delete→재투표 L1 covered

집합 전수 AC-T1~T4 실측 vs 기대

AC 기대 실측 판정
AC-T1 핸들러 @PostMapping 1 + @DeleteMapping 1 = 2 line44 @PostMapping("/jams/{slug}/vote") + line97 @DeleteMapping("/jams/{slug}/vote") = 2 PASS
AC-T1 CSRF CsrfTokens.isValid == 2 grep -c = 2 PASS
AC-T1 게이트 JamEvalWindow.isOpen == 2 grep -c = 2 PASS
AC-T2 매퍼 ${ 0 grep -c '${' = 0 (controller 도 0) PASS
AC-T3 게이트 2지점 controller results + JSP controller line172 publiclyVisible="CLOSED".equals(status)||evalEnded; JSP line403 resultsPublic, line447 조건렌더, line454 "평가 종료 후 공개" + JS line520/583 2차 게이트 PASS
AC-T4 DDL 변경 0 jam_votes ALTER/CREATE 0, schema 파일 diff 0 git status W2-5 surface: jam-detail.jsp·BibimbapApplicationTests 수정 + 신규 3 Java; docs/*ddl·schema.sql diff 0; jam_votes DDL 변경문 0 PASS
alias 인용 gameId/voteCount 전부 AS "...", 비인용 0 line62 SELECT game_id AS "gameId", COUNT(*) AS "voteCount"; 비인용 매치 0 PASS

L2 dev DB contract (★ §33 케이스폴딩) — 실측 인용

비파괴 절차 준수: 사용자 dev DB(bibimbap) 미접촉. throwaway DB bibimbap_verify_w2_5_<pid> 생성 → jam_votes(W2-3 동결 스키마 미러) 적용 → 실측 → DROP DATABASE 완료.

  • L2-1 UNIQUE(jam_id,voter_user_id): 동일 (1,100) 2차 INSERT → ERROR: duplicate key value violates unique constraint "ux_jam_votes_jam_voter" / Key (jam_id, voter_user_id)=(1, 100) already exists. 토글 UPDATE → 1 rows, game_id=11 (중복행 0, 1인1표 강제). PASS
  • L2-2 alias 케이스폴딩(§33): 구현 SQL(인용) 헤더 라벨 gameId|voteCount (camelCase 보존). 비인용 대조군 헤더 gameid|votecount (소문자 폴딩 재현). GROUP BY 집계 정합(game10=2, game11=2). 인용 alias 가 row.get("voteCount") null 결함을 회피함을 실측 확인. PASS

로그 스캔

clean — surefire-reports 14파일 FAIL/ERROR/Exception/Caused by 0건. 잔여 출력은 Mockito self-attach + JDK dynamic agent 경고(기존 환경 noise, W2-5 무관).

L3 런타임 인가 — needs_user_verification (수동)

인증/인가 플로우(L1+L2+L3 의무). 컨트롤러/매퍼 단위 + DB 계약은 자동 검증 완료. 다음 런타임 시나리오는 사용자 수동 확인 권장:

  1. 실제 세션 로그인 상태에서 EVAL 기간 잼 상세(jam-detail) 투표 → 토글 → 취소 UI 왕복.
  2. 평가기간 종료(CLOSED 또는 eval_end 경과) 후 결과 영역 출품작별 count 실제 렌더, 진행 중에는 "평가 종료 후 공개" 안내 노출.
  3. 미로그인/CSRF 누락 시 401/403 실제 응답. (자동 L1+L2 가 코드·DB 계약을 커버하므로 L3 는 회귀 위험 낮음 — 사용자 스모크로 충분.)

종합 판정 (W2-5)

overall: PASS (L1)

  • L1 171/171 GREEN, 실패 클래스 0.
  • 집합 전수 AC-T1~T4 + alias 인용 전부 PASS (1건도 어긋남 없음).
  • L2 dev DB contract 2종(UNIQUE / §33 케이스폴딩) 실측 PASS.
  • 로그 clean. rollback_signal: none (실패 없음 — 해당 없음)

concerns

  • count_mismatch: 신규 JamVoteControllerTest 실측 23 vs 작업지시·구현보고 기대 24 (-1). 전부 PASS 이므로 기능 결함 아님. 구현보고 "24테스트" 표기와 surefire 실측 23 간 1건 차이 — orchestrator 가 의도된 통합(VP 합본)인지 누락인지 확인 권장.
  • dev_db_not_migrated: 실행 중 dev DB bibimbap 에 jam_votes 테이블 부재(relation "jam_votes" does not exist). W2-3 동결 DDL(docs/jam-eval-ddl.sql)이 dev DB 에 미적용 상태. 본 L2 는 throwaway DB 로 무관하게 통과했으나, 실제 dev 런타임(L3) 전에 마이그레이션 적용 필요할 수 있음 — orchestrator/사용자 확인 권장.

Acceptance 매칭

criterion 매칭 전략 판정
1인1표 토글/UNIQUE L2-1 + JamVoteControllerTest PASS
평가기간 게이트 422 AC-T1 게이트 2 + 단위테스트 PASS
CSRF 누락 403 AC-T1 CSRF 2 + 단위테스트 PASS
미로그인 401 / results 공개 VP-3 단위테스트 PASS
종료후 공개 2지점(밴드왜건 회피) AC-T3 controller+JSP PASS
매퍼 ${ 금지 AC-T2 PASS
alias 케이스폴딩 안전(camelCase) alias 인용 + L2-2 §33 PASS
jam_votes 동결 소비(DDL 0) AC-T4 PASS

W2-6 (시상 집계 — W2 종점) 검증 결과

generated_at: 2026-06-24T12:40:00+09:00 / agent: verification-advisor (v1)

Acceptance Criteria (입력 받은 그대로 인용)

  • 멱등 recompute: computeAwards 2회 실행 → jam_awards 중복 0, 결과 동일(deleteByJamTrack→재insert).
  • 3트랙 개별수상: JUDGE(weightedTotal NULL 제외) / USER_RATING(review_count>=3 임계·NULLS LAST) / POPULAR(득표>0). 미달/NULL 트랙 제외.
  • 가중 GRAND: 가용 트랙만 가중 정규화.
  • CLOSED 게이트: status!=CLOSED 시 산정 422. 산정/확정 쓰기 CSRF→403 + 권한게이트.
  • 공개 결과: JamAwardController 결과 노출.
  • 집합 전수 AC-T1~T6 (impl echo, grep 그대로 1 어긋나면 FAIL).

실행된 전략

id cmd exit severity 결과
L1 full-test (§30) JAVA_HOME=…21 ./mvnw -o test 0 blocker pass
집합 전수 AC-T1~T6 (grep) grep -c on scope 0 blocker pass
L2 dev DB contract (§33) throwaway DB(w2_6_l2_throwaway) 적용·실측·DROP 0 blocker pass
단계 결과
L1 typecheck/compile pass (BUILD SUCCESS)
L1 unit+regression pass (190/190, 0 fail, 0 error, 0 skip)
L2 contract-devDB (멱등/3트랙/alias폴딩/GRAND) pass (4/4 항목)
로그 스캔 clean (WARNING 0 / ERROR 0 / Exception 0)

L1 (full test) 상세

  • BUILD SUCCESS, contextLoads PASS (BibimbapApplicationTests 1 test green — 신규 2매퍼 @MockBean 등록 확인).
  • 총계: Tests run: 190, Failures: 0, Errors: 0, Skipped: 0 (= 직전 171 + 신규 19, 기대 일치).
  • 신규 테스트 전수 GREEN:
    • JamAwardServiceTest: 7/7
    • JamAwardAdminControllerTest: 7/7
    • JamAwardControllerTest: 5/5
  • 회귀 0 (JamControllerTest): JamController 6arg 생성자 변경 후 14/14 GREEN 유지 (NoSuchBean/생성자 회귀 없음).
  • 실패 클래스: 없음.

집합 전수 AC-T1~T6 실측 vs 기대

AC 항목 기대 실측 판정
AC-T1 신규 매퍼 MyBatis ${param} 동적치환 0 JamAwardsMapper 0 / JamReviewRatingMapper 0 (grep -c '\${'=1은 Javadoc 주석 ${} 0 문구 — 실제 ${...} 패턴 0) PASS
AC-T2 집계 매퍼 camelCase alias 비인용 0 JamReviewRatingMapper 0 / JamVotesMapper 0 / JamScoreStatsMapper 0 (인용 AS "..." = JRRM 4·JVM 2) PASS
AC-T3 멱등 메커니즘(deleteByJamTrack + @Transactional) 존재 JamAwardsMapper.deleteByJamTrack(jamId,track) 선언 / JamAwardService.recompute @Transactional(L65) + deleteByJamTrack 호출(L68, 4트랙) PASS
AC-T4 CLOSED 게이트 + CSRF + 권한게이트(admin) 전수 CLOSED/evalEnded 체크→422 UNPROCESSABLE(L56-58) · CsrfTokens.isValid→403(L47-48) · PermissionGate.has(GAME_JAM_MANAGE)(L72-75) PASS
AC-T5 BibimbapApplicationTests 신규 2매퍼 @MockBean 2 @MockBean JamAwardsMapper(L103-104) + @MockBean JamReviewRatingMapper(L106-107) PASS
AC-T6 재사용 매퍼 JamVotesMapper.listCountsByJam (camelCase 인용) 존재·인용 listCountsByJam(L68) · game_id AS "gameId", COUNT(*) AS "voteCount"(L62) 인용 PASS
집합 전수: 6/6 PASS (어긋남 0).

시나리오 VP 커버 (L1 단위테스트 + L2 손계산)

VP 커버
멱등 recompute L1 JamAwardServiceTest + L2-1 DELETE→INSERT x2 hash 동일
3트랙 개별수상(NULL/임계/0표 제외) L1 service test + L2-2 손계산 일치
가중 GRAND(가용 트랙만) L1 service test + L2-4 정규화 원칙 확인
CLOSED 게이트 422 + CSRF 403 + 권한게이트 L1 JamAwardAdminControllerTest 7 + AC-T4 핸들러 전수
공개 결과 노출 L1 JamAwardControllerTest 5 + JamAwardController /jams/{slug}/resultsjam-results

L2 (dev DB contract — pass, 항목별)

bibimbap-db (postgres:16, :5433) 가용. 사용자 dev DB 비파괴: throwaway DB w2_6_l2_throwaway 생성 → jam-eval-ddl 검증 구조 + game_review_stats 적용 → 실측 → DROP 완료(count=0 확인). dev bibimbap DB 무변경(jam_awards 테이블 부재 = 미접촉 확인).

  1. ★멱등 recompute (L2-1) — PASS. 샘플 점수/평점/표 삽입 후 트랙별 DELETE→INSERT 2회.
    • pass1 count=6, pass2 count=6 (pass2 DELETE 가 직전 행 정확히 제거 후 재INSERT).
    • md5 스냅샷 동일: 24d919fba852848bf626cbd0dec05fe3 (값·rank·score 불변).
    • ux_jam_awards_jam_track_game UNIQUE 하 중복 행 0.
  2. 3트랙 집계 + 임계/NULL (L2-2) — PASS. 손계산 일치:
    • JUDGE: g1 weighted=4.000(judges=2), g2 weighted=2.000(scored_criteria=1), g3 미채점→jam_score_stats 부재(NULL 제외).
    • USER_RATING: g1(4.5,4)·g4(3.0,3) INCLUDED, g2(count 2<3) EXCLUDED, g3(리뷰 0, NULL) NULLS LAST 정렬 후순위.
    • POPULAR: g1=3·g2=1 (>0), g3/g4 0표 제외.
  3. alias 케이스폴딩 §33 (L2-3) — PASS. 매퍼 SELECT verbatim 실행 → row_to_json 반환 키 camelCase 보존: gameId/avgRating/reviewCount(JamReviewRatingMapper), gameId/voteCount(JamVotesMapper). 큰따옴표 alias 가 소문자 폴딩 방어. game_review_stats fan-out 0 (review_count == raw rows: g1 4=4, g2 2=2, g4 3=3 MATCH).
  4. GRAND 가중 (L2-4) — PASS(원칙). 가용 트랙만 정규화·결합: g1(3트랙 top)=최상위, g2(2트랙), g4(1트랙). 미존재 트랙 제외 동작 확인.
    • 주의: 정확한 GRAND 가중 계수는 RankScores/JamAwardService 내부(역할상 미열람) — DB-계약 수준의 "가용 트랙만 정규화·결합" 원칙만 검증. L1 JamAwardServiceTest 가 계수 단위검증 담당.

로그 스캔

  • clean. [WARNING] 0건, [ERROR] 0건, Exception/Caused by/FAILED 0건.

L3 런타임 인가 (needs_user_verification — 수동)

다음은 본 advisor 범위 밖 수동 확인 단계:

  • 산정/확정 API 의 실제 세션 권한(GAME_JAM_MANAGE) 미보유 사용자 403 런타임 확인.
  • CLOSED 미달 상태 잼 산정 호출 시 422 런타임 확인.
  • 공개 결과 페이지(/jams/{slug}/results)가 CLOSED 종료 후에만 노출되는 밴드왜건 회피 런타임 확인.
  • dev DB bibimbap 에 jam-eval-ddl 적용 후 실 스택 멱등 recompute 1회 스모크.

종합 판정

overall: PASS

  • verdict (L1): PASS (190/190, 회귀 0, JamControllerTest 14/14 GREEN).
  • 집합 전수 AC-T1~T6: 6/6 PASS.
  • L2 dev DB contract: PASS (4/4 항목).
  • 로그 스캔: clean. rollback_signal: none (FAIL 아님)

Acceptance 매칭

criterion 매칭 전략 판정
멱등 recompute(중복 0·결과 동일) L2-1 + JamAwardServiceTest + AC-T3 PASS
JUDGE NULL 제외 L2-2 + service test PASS
USER_RATING review_count>=3·NULLS LAST L2-2 + service test PASS
POPULAR 득표>0 L2-2 + service test PASS
GRAND 가용 트랙만 가중 L2-4(원칙) + service test(계수) PASS
CLOSED 게이트 422 AC-T4 + JamAwardAdminControllerTest PASS
산정/확정 CSRF 403 + 권한게이트 AC-T4 + JamAwardAdminControllerTest PASS
공개 결과 노출 JamAwardControllerTest 5 + AC PASS
신규 매퍼 ${ 금지 AC-T1 PASS
alias 케이스폴딩 안전(camelCase) AC-T2 + L2-3 §33 PASS
신규 2매퍼 @MockBean(contextLoads) AC-T5 + L1 BibimbapApplicationTests PASS
재사용 매퍼(JamVotesMapper) 소비 AC-T6 PASS