# task-3076 — 플랜 스케일 off-by-one 완전 해소 + 권한 게이트 정상화 + A4 봉인

레벨: Lv.3 (권한/과금 경계) · repo: InsuRo · 담당: dev1
승인: 제이회장님 2026-08-31 — **"3075 작업 완성도 빨리 올리자"**
선행: t3075(`ac4b1d01`) 머지·배포 완료

## 배경 — t3075 는 9건 중 1건만 고쳤다

코드의 플랜 dict 전체가 **1-indexed**(`0=Fallback, 1=무료 … 5=히든`)인데
DB `subscription_plans.sort_order` 는 **0-indexed**(`0=Free … 4=Hidden`)다.
**모든 dict 가 한 칸씩 어긋나 있고, 값이 우연히 같은 자리만 증상이 없다.**

### 남은 어긋남 (t3075 봇 전수조사 · ANU 확인)

| dict | 어긋난 자리 | 건수 |
|---|---|---|
| `PLAN_SKILL_ACCESS` | Pro · Max | 2 (Hidden 은 t3075 해소) |
| `PLAN_LEVEL_MODEL_CAP` | Basic · Max | 2 |
| `PLAN_LEVEL_MODEL_TIER` | Pro · Max | 2 |
| `PLAN_LEVEL_CHANNELS` | Basic | 1 |
| `PLAN_LEVEL_CLI_MODEL` (죽은 코드) | Pro | 1 |

### 권한 게이트 (ANU 신규발견 — 더 큰 문제)
- `require_feature` **18/19 오게이팅** — Pro 8 · Max 7 · Basic 2 기능이 **부당하게 차단**
- `require_plan("히든")` **11 엔드포인트 도달 불가**
- **Max 가 opus 요청 시 "맥스 이상 필요" 자기모순 403** (`PLAN_LEVEL_MODEL_CAP` 어긋남)
- 공개 API 잔여석 `occupied 0` 고정 (실제 2명)

## ★★ 작업 1 — 개별 숫자 맞추기 금지. 구조로 해소하라

dict 9개를 손으로 하나씩 맞추면 **다음에 또 어긋난다.** 이번이 그 증거다.

**요구**: DB `sort_order` 와 코드 index 사이의 변환을 **한 곳으로 모으거나**,
플랜을 **이름 기반**(off-by-one 면역)으로 다루도록 바꿔라.
t3075 봇이 "이름 기반 소스 3개"로 교차확인에 성공했으므로 이름 기반 전환은 가능하다.

방식은 봇이 판단하되 **근거를 보고서에 남겨라.** 다만 아래는 반드시 만족해야 한다:
- 새 플랜이 추가돼도 **한 곳만 고치면** 되게
- **DB 와 코드가 어긋나면 즉시 실패**하는 회귀 테스트 (부팅 시 또는 테스트에서)

## ★★★ 작업 2 — 권한 게이트 정상화 (과금 경계 · 최고 주의)

`require_feature` · `require_plan` 오게이팅을 바로잡는다.

### 반드시 지킬 것
- **고치면 지금 차단되던 기능이 열린다.** 열리는 것이 **원래 그 플랜의 권한이 맞는지**
  각 항목마다 확인하고 **표로 보고**하라. 하나라도 상위 기능이 열리면 과금 사고다
- **Hidden 전용 기능이 하위 플랜에 열리면 안 된다** (회장 지시: 하위 플랜엔
  "이런 기능이 있다" 정도만 노출해 상위 플랜 유도)
- 변경 전/후 **플랜×기능 접근 매트릭스**를 만들어 대조하라. 이게 이번 작업의 핵심 산출물이다

### 검증은 상수가 아니라 실제 엔드포인트로
t3075 에서 M8 변이가 1차 SURVIVED 했다 — **테스트가 상수만 읽고 실제 결선을 통과하지 않아서**다
(t2970·t3014 와 동일 사각지대). **실제 엔드포인트를 통과시키는 테스트**를 넣어라.

## ★ 작업 3 — A4 봉인 (조용한 빈 프롬프트)

t3075 ANU 자작 변이 7종 중 **A4(조용한 빈 프롬프트)가 SURVIVED** 했다.
= **이번 사고와 같은 양식이 아직 봉인되지 않았다.**

스킬이 필터링돼 빠지면 `benchmark_prompt` 가 조용히 `""` 가 되고
환각 금지 규칙·분량·이미지 지시가 전부 사라진다. 화면상 스킬은 켜져 있고 에러도 없다.

**요구**: "요청한 스킬이 권한 필터로 빠졌는데 조용히 진행되는" 경로를 **테스트로 봉인**하라.
경고 로그든 예외든, **관측 가능해야 한다.**
★ 단 **400 전면 차단은 하지 마라** — 회장 워크플로를 끊을 수 있다(t3074 봇 판단 존중).
어떤 형태로 관측 가능하게 할지는 봇이 판단하고 근거를 남겨라.

## 검증

- [ ] dict 어긋남 **9건 → 0건** (전수표 재작성)
- [ ] **플랜×기능 접근 매트릭스 변경 전/후 대조표** ← 최우선 산출물
- [ ] Max 가 opus 요청 시 **403 자기모순 해소** 확인 (실호출)
- [ ] Hidden 전용 기능이 하위 플랜에 **열리지 않았는지** 전수 확인
- [ ] 실제 엔드포인트 통과 테스트 (상수만 읽는 테스트 금지)
- [ ] A4 유형 변이가 이제 **KILL 되는지**
- [ ] 회귀 0건 (base sha 기준선 재측정 후 대조)
- [ ] 변이 주입 전 `assert 원문 in 소스` 로 no-op 아님 입증

## 하지 말 것

- **DB `subscription_plans` 를 고치지 마라.** sort_order +1 은 기존 사용자 무상 승급 = 과금 사고
- 프로덕션 고객 데이터를 변경하지 마라
- SEO 노하우 on/off 기능은 **후속 t3077** 이다. 건드리지 마라

## 종결

- **PR 까지만. 머지·배포 금지** (ANU 가 한다)
- `CI_WATCH_HANDOFF` 12필드 · 콜백 prompt UTF-8 **≤3900 bytes**
- `gh` 호출 시 `GH_TOKEN="$BOT_GITHUB_TOKEN"` 주입
- 보고서에 **ANU 판단 요청 사항**을 분리해 명시하라
