# task-3075 — 플랜 스케일 off-by-one 복구 (벤치마킹이 아무에게도 안 나가는 문제)

레벨: Lv.1 (상수 정정 + 봉인) · repo: InsuRo · 담당: dev1
승인: 제이회장님 2026-08-31 **"A"** (코드를 DB 에 맞춘다)

## ★ 이것은 정책 변경이 아니라 의도 복구다

### 실측 (ANU 프로덕션 직접 조회)

**DB `subscription_plans.sort_order`**
```
0 = Free   1 = Basic   2 = Pro   3 = Max   4 = Hidden
```

**코드 `server/main.py:510` `PLAN_SKILL_ACCESS`**
```python
4: [... "cro_copy"],                  # ← Hidden 이 실제로 여기 걸린다
5: [... "cro_copy", "benchmark"],     # ← benchmark 는 여기. 도달 불가
```

`main.py:1883` `allowed_skills = PLAN_SKILL_ACCESS.get(sort_order, ["base"])`
→ Hidden(4) 은 benchmark 를 못 받는다. **`[5]` 는 아무도 도달할 수 없다.**

### 결과 — 조용한 전면 무력화

`benchmark` 가 `filtered_skills` 에서 빠지면 `benchmark_prompt = ""` 가 되고,
그 안에 있던 **환각 금지 규칙 · 분량 지시 · 이미지 프롬프트 규칙이 전부 사라진다.**
화면상 스킬은 켜져 있고 에러도 없다.

- 실사용자 10명 게이트 재현: **benchmark 수령 0/10** (Hidden 5명도 못 받음)
- t3074 실호출: 벤치마킹 블록 있으면 마커 **8/8**, 없으면 **0/6**
- **회장이 같은 증상을 3회 겪은 진짜 원인이다.** t3068·t3070·t3074 는 전부
  프롬프트 문구를 고쳤는데, 프롬프트 자체가 주입되지 않고 있었다.

## 작업

### 유일한 수정
`PLAN_SKILL_ACCESS[4]` 에 `"benchmark"` 를 추가한다. **3줄이면 끝난다.**

★ **`[5]` 키를 지우지 마라.** 방어적으로 남겨두되, 아래 봉인 테스트로 "4가 실권한"임을 못박는다.

### ★★ 하지 말 것 — DB 를 고치지 마라
`subscription_plans.sort_order` 를 +1 하는 방식은 **기존 사용자가 한 등급씩 무상 승급**된다.
과금 사고다. 회장이 A(코드를 DB 에 맞춘다)를 선택했다. **DB 는 건드리지 마라.**

### 폭발 반경 (ANU 사전 확인 — 재검증하라)
어긋난 dict 는 `PLAN_SKILL_ACCESS` **하나뿐**이다.
`PLAN_LEVEL_MODEL_CAP` 등 다른 dict 는 4와 5가 같은 값이라 상쇄돼 있었다.
→ **다른 dict 도 전수 확인하고 결과를 보고서에 표로 남겨라.** 하나라도 더 어긋나 있으면 보고하라.

### 봉인 테스트 (최소 2건)
1. **`sort_order=4`(Hidden) 가 `benchmark` 를 받는다** — 이게 핵심
2. **`sort_order=3`(Max) 이하는 `benchmark` 를 받지 못한다** — Hidden 전용 정책 유지
   (회장 지시: 하위 플랜에는 "이런 기능이 있다" 정도만 노출해 상위 플랜 유도)
3. 가능하면: DB 의 실제 최대 sort_order 와 코드 dict 의 최대 키가 일치하는지 검사하는
   회귀 테스트 — **같은 off-by-one 이 다시 생기면 즉시 잡히게**

### 프론트 대칭 확인
`src/config/planSkillMap.ts` 의 `PLAN_SKILL_MAP` 은 **플랜 이름 문자열**로 인덱싱하므로
off-by-one 과 무관하다. 다만 **서버와 프론트의 benchmark 허용 집합이 일치하는지** 확인하라.
불일치가 있으면 보고만 하고 고치지 마라(별건).

## 검증

- [ ] `sort_order=4` 로 실제 생성 요청 시 `benchmark_prompt` 가 **비어있지 않은지** 확인
      (문자열 길이 > 0 + 환각 가드 문구 포함 여부)
- [ ] `sort_order=3` 은 여전히 차단되는지
- [ ] 다른 플랜 관련 dict 전수 확인 결과 표
- [ ] 회귀 0건 (base sha 기준선 재측정 후 대조)
- [ ] **변이 테스트**: `[4]` 에서 `benchmark` 를 빼면 테스트가 죽는가
      (주입 전 `assert 원문 in 소스` 로 no-op 아님 입증)

## 종결

- **PR 까지만. 머지·배포 금지** (ANU 가 한다)
- `CI_WATCH_HANDOFF` 12필드 · 콜백 prompt UTF-8 **≤3900 bytes**
- `gh` 호출 시 `GH_TOKEN="$BOT_GITHUB_TOKEN"` 주입
