# Verification Strategies Registry

`verification-advisor` 가 읽는 단일 레지스트리. 이 파일에 등록된 전략만 검증 대상이 된다. 전략 추가/수정 시 이 파일만 변경하면 에이전트 구조(tier) 는 건드릴 필요 없다.

## 검증 사다리 (L1 / L2 / L3)

프로젝트의 검증은 3단으로 나뉜다. 코드 변경의 성격에 따라 **적용할 최소 L 레벨** 이 결정된다.

| 레벨 | 대상 | 신뢰 범위 |
|---|---|---|
| **L1** | 단위·회귀 (타입체크 + 단위/회귀 테스트) | "내가 고친 라인이 깨지지 않음" + "과거 버그가 재발하지 않음" |
| **L2** | 원격/외부 의존 계약 (live contract 테스트) | "외부 서비스 스키마·필드가 우리 기대와 정합" |
| **L3** | End-to-end (실제 런타임·DB·외부 서비스 전부 도는 시나리오) | "사용자 시나리오가 실제 스택에서 끝까지 동작" |

### 버그 범주 → 적용 L 레벨 (예시 · 프로젝트 맞춤)

프로젝트 특성에 맞게 아래 표를 채운다.

| 버그/변경 범주 | 의무 레벨 |
|---|---|
| 외부 API 스키마/응답 파싱 수정 | L1 + L2 |
| 외부 서비스 설정 상수 변경 | L1 + L2 |
| 인증/인가/롤 플로우 수정 | L1 + L2 + L3 |
| 순수 도메인 로직 (외부 의존 없음) | L1 |
| MyBatis 매퍼 신규/SQL alias·집계 뷰 정의·변경 (DB-방언 계약) | L1 + L2 (dev DB contract) |
| 인프라 설정 (container/env/compose) | L1 + 수동 스모크 |

**회귀 테스트 의무**: 버그 수정 커밋은 해당 버그를 재현하는 테스트를 같이 포함한다. revert 시 테스트가 실패하고, 수정 후엔 통과해야 한다.

**신규 컨트롤러/매퍼 의존 변경 시 full test 의무**: 신규 컨트롤러를 추가하거나 컨트롤러의 매퍼 의존을 늘리면 implementation 단계에서 `test-compile` 만으로 끝내지 말고 반드시 full `./mvnw -o test` 를 실행한다. `BibimbapApplicationTests` 는 MyBatis/DataSource autoconfigure 가 exclude 된 컨텍스트라 컨트롤러가 주입하는 매퍼마다 `@MockBean` 을 수동 등록해야 하며, 누락 시 `contextLoads` 가 `NoSuchBeanDefinitionException` 으로 실패한다. 이 회귀는 `test-compile` 로는 탐지되지 않는다(W3-2 세션 20260618 실증).

**mock-vs-reality: DB-방언 계약은 L1 으로 못 잡는다**: 컨트롤러/애플리케이션 단위테스트가 MyBatis 매퍼를 `@MockBean` 으로 대체하면 "내가 부른 메서드가 불렸나"만 보고 "실제 Postgres 가 그 SQL 을 우리 기대대로 해석하나"는 검증하지 못한다. L1 통과율이 높을수록 DB-통합 계약 공백이 오히려 숨는다. W3-2 세션 20260622 에서 L1 43/43 GREEN 을 통과한 채 누출된 실버그 2종:
- **SQL alias 케이스 폴딩**: 따옴표 없는 `AS gameId` 는 Postgres 가 소문자(`gameid`)로 폴딩 → MyBatis `Map` 키가 소문자 → 컨트롤러의 camelCase `get("reviewCount")` 가 null → 집계 summary 전면 무력. camelCase 키가 필요하면 alias 를 **반드시 큰따옴표**(`AS "gameId"`)로 감싼다.
- **집계 뷰 1:N LEFT JOIN fan-out**: 부모(리뷰)에 자식(축 평점) 직접 JOIN 시 `COUNT(*)`/`AVG` 가 자식 행수만큼 왜곡(자식 0행일 땐 잠복, 입력 순간 발현). 집계는 자식을 서브쿼리에서 선집계 후 LEFT JOIN 한다.
→ 매퍼 메서드 신규/시그니처·SQL 변경, 집계 뷰 정의·변경 시 L1 GREEN 으로 끝내지 말고 dev DB 연동 L2 contract 로 (a) 매퍼 반환 `Map` 키가 컨트롤러 조회 키와 정합, (b) 뷰 집계가 샘플 데이터 손계산과 일치, 를 가드한다. (현재 L2 harness 미구축 — 신설이 open item.)

### 실행 수단

프로젝트 루트에 통합 검증 스크립트를 둘 것을 권장한다 (예: `scripts/verify.sh`, `make verify`, `cargo xtask verify`). 스크립트는 L1 → L2 → 로그 스캔 순차 실행을 담당.

- L2 를 조건부로 비활성화하는 환경 변수를 제공 (원격 꺼진 CI 대비). 예: `SKIP_LIVE_CONTRACT=1`.
- 원격 테스트의 seed/live URL 미지정 시 L2 는 자동 skip 처리되도록 구성.

## 사용 규약

- `verification-advisor` 는 호출 시 변경 scope 를 받아 매칭되는 전략만 실행한다.
- 전략 개수에 따라 tier 가 자동 결정된다:
  - 1개 → advisor 가 직접 실행 (tier 2)
  - 2개 이상 → `worker:` 필드가 있는 전략은 해당 worker spawn (tier 3), 나머지는 advisor 순차 실행
- 신규 worker 도입은 [agent-team-protocol.md §9](./agent-team-protocol.md) 의 확장 트리거 참조.

### 집합 전수 AC 실행 지침

design.md 의 "검증 포인트" 에 다음 형식의 AC 가 있으면 "집합 전수 체크" 유형으로 분류한다.

```
AC-N: <집합명> 전수 N건 ... (grep -c ... == N)
```

실행 방법:

1. AC 에 명시된 `grep -c` 명령을 그대로 실행한다.
2. 반환값이 AC 에 명시된 기대 수와 다르면 **blocker FAIL** 로 판정한다.
3. 실패 시 출력: 실제 매치 수 + 누락된 항목을 diff 또는 목록으로 제시.

집합 전수 AC 는 특성상 "부분 통과"가 없다 — 기대 수에서 1 이라도 어긋나면 전체 FAIL. 규약 근거는 프로토콜 §4.3 + `design-advisor.md` "집합 전수 체크 AC 패턴" 섹션.

## 전략 (템플릿)

아래는 YAML 스켈레톤이다. 프로젝트 기술 스택에 맞게 `cmd` 를 교체한다 (`pnpm` / `yarn` / `npm` / `cargo` / `go test` / `pytest` / `mvn test` 등).

```yaml
strategies:
  # ── L1 ────────────────────────────────────────
  - id: typecheck
    cmd: <프로젝트 타입체크 명령>       # e.g. pnpm typecheck / cargo check / mypy .
    scope: all
    timeout_s: 180
    failure_severity: blocker

  - id: unit
    cmd: <프로젝트 단위 테스트 명령>    # e.g. pnpm test / cargo test / pytest
    scope: <src glob>                   # e.g. src/**
    timeout_s: 600
    failure_severity: blocker

  # ── L2 ────────────────────────────────────────
  - id: contract-<external>
    cmd: <계약 테스트 명령>
    scope:
      - <외부 의존이 집중된 경로>
    timeout_s: 60
    failure_severity: blocker
    preconditions:
      - "<외부 서비스 기동 확인 방법>"
      - "<필요 환경 변수>"
    note: |
      원격 미기동 또는 seed 미지정 시 runtime 에서 skip 처리.
      blocker 로 두는 이유는 "원격이 떠 있는데 계약 위반" 이 사용자 장애로 직결되기 때문.

  # ── 통합 ──────────────────────────────────────
  - id: verify-all
    cmd: <통합 검증 스크립트>           # e.g. pnpm verify / make verify
    scope: all
    timeout_s: 900
    failure_severity: blocker
    note: "L1 + L2 + 로그 스캔 통합. ATP task 세션 종료 전 의무."
```

## 필드 정의

| 필드 | 의미 |
|---|---|
| `id` | 전략 식별자 (unique) |
| `cmd` | 실행 명령 |
| `scope` | glob. 이 패턴에 변경이 걸칠 때만 실행 |
| `timeout_s` | 초 단위 타임아웃 |
| `failure_severity` | `blocker` (실패 시 전체 검증 실패) \| `warning` (기록만) |
| `worker` (optional) | spawn 할 worker 이름. 미지정 시 advisor 직접 실행 |

## 확장 시

- 새 전략 추가: 위 YAML 블록에 항목 추가.
- Worker 분리가 필요한 수준에 도달 (브라우저 테스트, 장시간 E2E 등): `worker:` 필드 붙이고 해당 worker 파일 신설 + `verification-advisor.md` 의 tools 에 `Agent` 추가.
- 기준은 [agent-team-protocol.md §9 확장 트리거 레지스트리](./agent-team-protocol.md#9-확장-트리거-레지스트리).

## 설계·테스트 단계 체크리스트 (구조적 교훈)

세션 회고에서 수용된 재현성 있는 교훈을 규칙형으로 기재한다. 이 섹션은 누적 append-only.

### 영향맵에 SSR 호출지점 포함

매퍼 메서드 시그니처·data 클래스 필드 등 **심볼을 변경할 때**, design 단계 파일 영향맵은 API 컨트롤러뿐 아니라 그 심볼을 호출하는 **SSR/뷰모델 지점**(예: Spring MVC `@Controller` 의 뷰 렌더 메서드)도 `rg` 전수 확인으로 포함해야 한다. 누락 시 implementation 단계 컴파일 깨짐.

> 근거: W3-2 고도화 세션(20260622) — `GameController.gameDetail` SSR 호출지점을 설계 영향맵에서 누락 → 컴파일 깨짐, implementation-advisor 직접 보정.

### 계약 강화 시 기존 fixture 전수 감사

입력 검증 계약을 강화(최소 길이·필수 필드 추가)하면, 신규 테스트 케이스 추가만으로 부족하다. **기존 테스트 fixture 전수**가 새 계약에 정합하는지 감사하는 단계를 W-TEST 체크리스트에 명시해야 한다.

감사 절차:
1. 새로 도입된 검증 계약 목록화 (최소길이·필수필드·enum 범위 등).
2. 기존 테스트 fixture(요청 본문·파라미터) 전수 스캔 — 신규 계약 조건 충족 여부 확인.
3. 미충족 fixture 갱신 (행위 assertion 보존 전제).
4. 거부 경로 테스트의 fixture 도 신규 계약을 충족하는 값으로 올려 실제 거부 사유(상위 분기)까지 도달함을 보장.

> 근거: W3-2 고도화 세션(20260622) — B2(본문 10자)·6축 필수 계약 도입 시 기존 fixture 8건이 미갱신되어 L1 RED. 컨트롤러는 무버그.

### freeze/동결 분류는 근거 문서 확인 선행

어떤 변경을 "동결 영역 해제·고위험 게이트"로 분류하기 전에, 동결 범위를 정의한 **근거 문서(ADR·roadmap·work-log)를 줄 번호까지 직접 확인**한다. 표면적 유사성("평점 집계" 등)만으로 freeze 인접 추론 금지.

절차:
1. 동결 선언 근거 문서를 실제로 열어 동결 범위 정의를 줄 번호로 확인.
2. 변경 대상 테이블/뷰/심볼이 그 범위에 **명시적으로** 포함되는지 판단.
3. 포함 확인 시만 §6 게이트 표기.

> 근거: W3-2 고도화 세션(20260622) — `game_review_stats` 집계뷰가 phantom 고위험 게이트로 오분류 → `roadmap:63/201/202` 직접 확인으로 일반 DDL 정정.

### (긍정 패턴) frontend-design 스킬 fork 위임

production-grade UI(SVG·a11y·다중 JS 인터랙션 포함)를 구현할 때, `frontend-design` 스킬을 **fork(컨텍스트 상속)로 서브에이전트에 위임**하면 스킬 호출 + 단일파일 폴리시 + 프리뷰 render-verify 를 컨텍스트 오염 없이 수행 가능하다. 검증된 패턴.

조건:
- L1 전체 GREEN 확인 후 진입.
- 단일파일 폴리시: JSP 1파일 안에서 완결(신규 파일 0).
- 가드레일 명시: 기존 JS 로직 보존 / a11y / BE API 계약 무변경 / 외부 JS 라이브러리·CDN 도입 금지.
- 산출물에 프리뷰 HTML 포함(`artifacts/`).

> 근거: W3-2 고도화 세션(20260622) — 육각형 SVG 레이더·6축 radiogroup·C1~C6 를 fork 위임으로 단일 JSP 파일 승격 + L1 43/43 GREEN 유지.

### 신규 `*Test.java` 는 "(검증)" 소유태그여도 구현 산출물

design 파일 영향맵에서 신규 테스트 파일이 owner 칸에 `(검증)` 으로 표시되더라도, **테스트 작성은 implementation 단계의 산출물**이다. `verification-advisor` 는 테스트를 **실행만** 하며 Write 권한이 없다. `(검증)` 은 "검증 관련 파일"이라는 용도 라벨일 뿐 작성 주체가 아니다. implementation 단계에서 파일 영향맵의 신규 `*Test.java` 를 전수 작성해야 하며, 빠뜨리면 verification 단계에서 시나리오 AC(VP) 가 "미커버"로 떨어진다(테스트가 없으니 실행할 게 없음).

> 근거: W2 세션(20260624) — W2-2 에서 impl-advisor 가 설계 파일영향맵의 `JamJudgeAdminControllerTest`/`JamRoleGateTest` `(검증)` 태그를 "verification-advisor 가 작성"으로 오독 → 테스트 0건 산출. 별도 fix 라운드로 24테스트 보완 후 통과.

### 매퍼 alias 케이스폴딩 기준 = Map resultType / 집계 SELECT (VIEW 여부 아님)

`mock-vs-reality` 의 BUG-2(alias 케이스폴딩) 정밀화: camelCase alias 를 큰따옴표로 인용해야 하는 기준은 "집계 VIEW 인가"가 **아니라 매퍼 반환이 `Map`(또는 키 이름 의존 집계)인가** 이다. Postgres 는 비인용 `AS gameId` 를 소문자(`gameid`)로 폴딩하므로, `resultType=Map` 이면 `row.get("gameId")` 가 null 이 된다. 반면 **POJO 직접 매핑 매퍼는 MyBatis 가 case-insensitive 로 매핑**하므로 비인용 alias 라도 무해하다. 따라서: **Map resultType·집계 SELECT 매퍼는 `AS "camelCase"` 인용 필수, POJO 직접매핑은 비인용 허용**.

> 근거: W2 세션(20260624) — W2-1 5매퍼(POJO) 비인용 alias 67건 무해 확인 → W2-5 에서 `JamVotesMapper.listCountsByJam`(Map resultType) 은 인용 필수로 정정(설계 concern 은 "VIEW만 인용"으로 부정확). L2 contract 에서 비인용 대조군 폴딩(`gameid`/`votecount`) 재현으로 입증. W2-4/6 의 집계 매퍼도 동일 적용.

### 신규 컨트롤러가 호출하는 공유 헬퍼는 정의 존재를 grounding 으로 선확인

신규 컨트롤러가 기존 컨트롤러의 패턴(예: `response(HttpStatus, String)` 응답 헬퍼)을 따를 때, 그 헬퍼가 **상속/공유되는지 아니면 클래스마다 재정의해야 하는지**를 implementation 전에 `rg` 로 확인한다. bibimbap 컨트롤러는 공통 베이스 클래스가 없어 각 컨트롤러가 헬퍼를 자체 정의해야 한다. 호출만 하고 정의를 빠뜨리면 `test-compile` 로는 늦게 잡히고(또는 다른 컴파일 에러에 가려), full `./mvnw test` 의 컴파일 단계에서 BUILD FAILURE 로 발현한다.

> 근거: W2 세션(20260624) — W2-1 `JamAdminController` 가 `response(HttpStatus,String)` 44회 호출하나 정의 누락(`JamController` 는 보유) → L1 컴파일 24 errors. backward 보정으로 헬퍼 1개 추가 후 통과.

---

## 프로토콜 개선 권고 (외부 번들 — 미적용)

아래 항목은 ATP 플러그인 번들(`~/.claude` 전역) 대상이다. 본 프로젝트 파일에서 직접 수정하지 않고 기록만 한다.

- **design-advisor 체크리스트**: §1 파일 영향맵 작성 규약에 "SSR 컨트롤러·뷰모델 호출지점 포함" 항목 추가 필요.
- **W-TEST worker 지시**: 컨트롤러 검증 계약 강화 시 "기존 fixture 전수 계약 정합 감사" 단계를 의무 체크리스트 항목으로 포함 필요.
- **agent-team-protocol §6 게이트 분류**: "근거 문서 확인 없이 freeze 인접 = 고위험 추론 금지" 조항 추가 권고.
- **design-advisor 파일영향맵 표기**: 신규 `*Test.java` 의 owner 를 `(검증)` 용도 라벨과 분리해 `owner=implementation` 으로 명시 필요(단계 경계 오독 유발 — 구조적). 근거: W2 세션(20260624) W2-2 테스트 미작성 갭.
- **implementation-advisor 체크리스트**: 신규 컨트롤러가 호출하는 공유 헬퍼/심볼은 정의 존재를 grounding(`rg`)으로 선확인하는 조항 추가(test-compile 비신뢰). 근거: W2 세션(20260624) W2-1 `response()` 헬퍼 누락 컴파일 FAIL.
