---
phase: implementation
agent: implementation-advisor
agent_version: 1
generated_at: 2026-06-24T11:05:00+09:00
workstream: W2-2-심사위원 역할 권한(jam_judges + JamRoleGate)
concerns: []
concerns_checked: true
workers_spawned: 7
planned_workers: 7
actual_workers: 7
self_verification:
  checklist_passed: true
  unused_diagnostics: 0    # -Xlint:all 재컴파일 + 수동 교차검증, W2-2 파일 warning 0
  test_compile: BUILD SUCCESS  # ./mvnw -o test-compile, MAVEN_EXIT=0 (실행 아님)
---

# 구현 보고: W2-2 심사위원 역할 권한 (jam_judges + JamRoleGate)

권위 설계도: `.atp/work-session/20260623-104307/implementation/W2-2-judge-role-design.md` (오픈질문 0, 정본 그대로 구현, 임의 변경 0).

## 변경 목록

| 파일 | worker | 결과 요약 |
|---|---|---|
| docs/jam-judge-ddl.sql (신규) | migration-writer w-001 | 권위 DDL. jam_judges(id/jam_id/user_id/assigned_by/created_at) + FK 3종(jams/users/users) + ux_jam_judges_jam_user UNIQUE + idx_jam_judges_jam. 멱등(SEQUENCE/TABLE IF NOT EXISTS, DO $$ pg_constraint guard, CREATE UNIQUE INDEX IF NOT EXISTS) — jam-ddl 선례 동형 |
| db/schema.sql (수정) | migration-writer w-001 | 파일 끝(558~596행) jam_judges 블록 동기 사본 추가. 헤더 주석 game_reviews/jam 블록 선례 동형. docs 본문과 바이트 일치(diff 0). jams 블록(374~) 뒤 → FK 순서 정합 |
| data/JamJudgeData.java (신규) | code-writer w-002 | POJO 6필드(id/jamId/userId/assignedBy/createdAt/displayName) + getter/setter. JamEntryData 동형(Lombok 미사용) |
| mapper/JamJudgesMapper.java (신규) | code-writer w-003 | @Mapper insert/delete/exists/listByJam. #{} only, §33 alias 가드(POJO 비인용 alias, 큰따옴표 0). exists=EXISTS boolean(UserPermissionsMapper 선례) |
| mapper/JamEntriesMapper.java (수정) | code-writer w-004 | isOwnEntry(jamId,gameId,userId) 1건 추가. 기존 3메서드 보존. USER+TEAM 양경로 OR-EXISTS. #{} only |
| security/JamRoleGate.java (신규) | code-writer w-005 | @Component 잼 스코프 게이트(별도 축). isJudge(session,jamId)/isOwnEntry(jamId,gameId,judgeUserId). 두 매퍼 생성자 주입. 요청당 직접 조회(epoch/캐시 0). sessionUserId PermissionGate 동형 |
| controller/JamJudgeAdminController.java (신규) | code-writer w-006 | @Controller. assign(POST)/remove(POST)/list(GET) 3핸들러. requireJamManage 전수 진입 게이트 + 상태변경 CSRF 2건. DuplicateKeyException catch→409. JamAdminController 헬퍼 동형 |
| webapp/.../admin-jam-list.jsp (수정) | code-writer w-007 | 심사위원 관리 섹션 + data-action 위임 핸들러(judges/judge-assign/judge-remove). fetch GET 목록 + post 지정/해제. 클라 렌더 textContent only(innerHTML 0). 기존 폼/스크립트 보존 |
| test/.../BibimbapApplicationTests.java (수정) | advisor 직접 | @MockBean 2건 추가(JamJudgesMapper, JamRoleGate). contextLoads 빈 의존 해소 (설계 §VP-8/AC-T6, concern 2) |

## Bash 단계 (advisor 직접)
- `./mvnw -o test-compile` (JAVA_HOME=/opt/homebrew/opt/openjdk@21/...) → **BUILD SUCCESS, MAVEN_EXIT=0**. main+test 전 소스 컴파일 통과(신규 @MockBean 포함).
- `./mvnw -o clean compile -Dmaven.compiler.compilerArgument=-Xlint:all` → **BUILD SUCCESS, 전체 WARNING 0, W2-2 파일 unused 0**. unused 진단 별도 게이트 통과.
- AC-T1~T7 + §33 정적 grep 게이트 전수 실행 → 전부 기대값 일치(아래 §검증 echo).
- **테스트 실행·DB 적용·git commit 안 함**(verification/수동/orchestrator 영역).

## 설계와의 차이
없음. planned_workers(7) == actual_workers(7). 설계 파일영향맵·시그니처·DDL·API 계약·시퀀스를 정본 그대로 구현.

세부 결정(설계 범위 내, 임의 변경 아님):
- **목록 조회 UI = 클라이언트 fetch GET**: 설계 §외부계약이 목록 조회를 JSON API(`GET /admin/jams/{jamId}/judges`)로 명시했고, W2-1 console() 핸들러(model 에 jams/csrfToken 만 주입)를 수정하면 안 되므로, JSP 는 비동기 fetch 로 목록을 로드(SSR 추가 데이터 0). console() 무수정 = W2-1 검증 동작(6핸들러 게이트) 보존.
- **JamRoleGate @MockBean 채택**: 설계 concern 2 가 "@Component 면 @MockBean 또는 실제빈+의존매퍼 MockBean" 명시. @MockBean 채택(W1 PermissionGate 동형). 의존 매퍼(JamJudgesMapper/JamEntriesMapper)도 @MockBean 이라 양립.

## W2-1 기존 파일 수정 여부 + 근거
1. **JamEntriesMapper.java (수정)** — 설계 §파일영향맵 명시(K-MAPPER, crossRefs). isOwnEntry 메서드 **1건 추가만**, 기존 insert/listByJam/exists 3메서드 한 글자도 변경 0(자기출품 충돌 판정 = W2-2 소관). W2-1 호출지점 깨짐 0(신규 메서드).
2. **admin-jam-list.jsp (수정)** — 설계 §파일영향맵 명시(K-ADMIN, crossRefs). 심사위원 섹션·버튼·스크립트 핸들러 **추가만**. 기존 생성 폼/목록 테이블/transitionStatus·toggleVisibility·removeJam 스크립트 보존(grep 7매치 확인).
3. **BibimbapApplicationTests.java (수정)** — 설계 §파일영향맵 (검증) 명시. @MockBean 2건 추가만(contextLoads 보존 의무).
4. **db/schema.sql (수정)** — 설계 §데이터모델 명시. jam_judges 블록 추가만(전역 RBAC/jams/jam_entries 블록 무변경).

**JamController/JamAdminController/InterceptorConfig/W2-1 5매퍼는 무수정** — 설계 J3-A(InterceptorConfig 의 `/admin/jams/**` exclude 가 `/admin/jams/{id}/judges` 트리 커버 → 추가 수정 불요)에 따라 건드리지 않음. W2-1 검증 동작(requireJamManage·CSRF·#{}) 회귀 0.

## concerns 처리 결과
설계 frontmatter concerns 5건은 본 구현이 모두 해소(설계가 결정으로 고정한 항목):
- **concern 1 (시그니처 inflate)**: isJudge(session,jamId)/isOwnEntry(jamId,gameId,judgeUserId)/매퍼 시그니처 전부 최소 인자, dead parameter 0. -Xlint:all unused 0 + 수동 교차검증(JamRoleGate 두 매퍼 7회 사용, Controller 4 주입필드 전부 사용).
- **concern 2 (신규 빈 @MockBean / full test)**: JamJudgesMapper + JamRoleGate @MockBean 등록. test-compile BUILD SUCCESS. **full ./mvnw -o test 실행은 verification-advisor 몫**(본 advisor 실행 금지) — VP-8 로 이관.
- **concern 3 (§33 alias)**: JamJudgesMapper resultType=POJO(JamJudgeData) → snake→camel 직접 alias, **큰따옴표 alias 0건**(grep 확인). isOwnEntry/exists boolean(EXISTS) 매핑.
- **concern 4 (자기출품 충돌 = 계약, enforce=W2-4)**: 본 W2-2 는 JamRoleGate.isOwnEntry + JamEntriesMapper.isOwnEntry **헬퍼·계약만 제공**. 지정 시점 충돌 미차단(J7). W2-4 점수입력 소비점은 verification 시 crossRefs 재확인.
- **concern 5 (배포 순서 의존)**: InterceptorConfig 무수정(J3-A). W2-1 의 `/admin/jams/**` exclude 선배포 의존 — 롤아웃 순서 문서화(설계 §롤아웃). 코드 결함 아님.

## Verification 을 위한 힌트
- acceptance criteria 는 design.md 의 "검증 포인트" + "집합 전수 체크 AC" 참조(아래 verbatim echo).
- 이번 변경으로 영향받는/신규 테스트 파일(설계 §파일영향맵 (검증) — 본 advisor 미작성, verification 작성 대상):
  - src/test/.../JamJudgeAdminControllerTest.java (신규 — VP-1/4/5/6)
  - src/test/.../JamRoleGateTest.java (신규 — VP-2/3)
  - src/test/.../BibimbapApplicationTests.java (수정 완료 — VP-8/AC-T6 contextLoads)
- full `./mvnw -o test` 의무(설계 §30/concern 2). JAVA_HOME=/opt/homebrew/opt/openjdk@21/libexec/openjdk.jdk/Contents/Home 사용(시스템 java 없음).
- DB-방언 L2 실측 권장: isOwnEntry OR-EXISTS, JamJudgesMapper EXISTS boolean, listByJam JOIN users alias (dev DB contract).

## 자가확인 정적 게이트 결과 (verification echo 근거)
- AC-T1: docs ADD CONSTRAINT=3, CREATE UNIQUE INDEX=1 / schema.sql jam_judges ADD CONSTRAINT=3, UNIQUE INDEX=1 → PASS
- AC-T2: @Post/@GetMapping 핸들러=3, requireJamManage=4(정의1+호출3) → 전수 게이트 PASS
- AC-T3: CsrfTokens.isValid=2(상태변경 2핸들러) → PASS
- AC-T4: entrant_type 'USER'=1, 'TEAM'=1 → 양경로 PASS
- AC-T5: JamJudgesMapper+JamEntriesMapper `${`=0 → PASS
- AC-T6: JamJudgesMapper @MockBean=1, JamRoleGate @MockBean=1 → PASS
- AC-T7: docs 전역테이블(users/user_permissions/permissions) ALTER/CREATE=0 → 전역 RBAC 무변경 PASS
- §33: JamJudgesMapper 큰따옴표 alias=0 → PASS

## 미해결 / 리스크
- DB 미적용(NOT APPLIED). `db/apply-local-ddl.sh` 로 jam-judge-ddl 적용은 수동 영역(jam-ddl < jam-judge-ddl 알파벳 순 → FK 선행 jams 자동 보장).
- 테스트 코드(JamJudgeAdminControllerTest/JamRoleGateTest) 미작성 — verification-advisor 작성·실행 대상.
- W2-4 점수입력의 isJudge/isOwnEntry 소비점은 W2-4 워크스트림에서 검증(crossRefs).
