# task-3066 — 보험금 청구계산기 Phase 1: 실손계산기 이동 + 고객 연계

- **팀**: dev4 (비슈누) · **레벨**: Lv.2 · **일자**: 2026-08-30 KST
- **저장소**: `Jeon-Jonghyuk/InsuRo` · base `32ec25a` · 브랜치 `task/task-3066-dev4`
- **PR**: #278 (draft) — https://github.com/Jeon-Jonghyuk/InsuRo/pull/278 · HEAD `c4759aa`
- **결론**: **메뉴 이동 완료 · 고객 미선택 계산 무손상(렌더 테스트로 실증) · "검토 참고용" 문구 실재(스크린샷 확인)**

---

## 0. ★★ ANU 명세 반증 1건 — routes.ts 만 고치면 메뉴는 안 움직인다

명세 ①은 `routes.ts:326~331` 의 `section` 을 "고객관리"로 바꾸면 메뉴가 이동한다고 전제했다. **실측 결과 거짓이다.**

```
routes.ts 의 section 을 읽는 곳   = src/components/AppSidebar.tsx 뿐
AppSidebar.tsx 를 import 하는 곳  = 0건  ← 죽은 컴포넌트
실제 렌더되는 메뉴                 = src/components/navigation/navigationConfig.ts 의 domainTabs
navigationConfig 안의 실손계산기   = 아예 없음
```

따라서:
1. `routes.ts` 만 고쳤으면 **사용자 눈에는 아무 변화가 없는 no-op** 이 될 뻔했다.
2. 명세의 검증항목 "**분석&도구에서 사라졌는지 확인**" 은 성립하지 않는다 — 실손계산기는 **애초에 라이브 메뉴 어디에도 없었다.** URL 을 직접 입력해야만 도달 가능한 상태였다(= 회장님이 "메뉴 위치도 안 잡힌 거 같아" 라고 하신 관찰이 정확했다).
3. 조치: `routes.ts`(라우트 등록·죽은 메타데이터) 와 `navigationConfig.ts`(라이브 메뉴) **양쪽 모두** 수정했다. 두 파일 다 allowed_resources 안이다.

★ 이는 3문서 맥락노트 §5 의 "ANU 가 프론트를 안 봤다" 와 같은 계열의 누락이다. 이번엔 `AppSidebar` 를 봤으나 **그 컴포넌트가 죽었는지**를 안 봤다.

---

## 1. 만든 것

| 단계 | 내용 | 커밋 |
|---|---|---|
| ① | 메뉴 이동 + 기존 경로 리다이렉트 | `6e07f79` |
| ② | 실손/정액 선택 + 정액 "준비 중" | `b18be19` |
| ③ | 고객 선택 + 저장 증권분석에서 가입일 자동 채움 | `5bbe1cd` |
| ④ | 결과에 "검토 참고용" 명시 | `4338299` |
| 봉인 | 봉인 테스트 3종 | `62f1b1b` |
| 교정 | 봉인 테스트를 허용 경로로 이동 | `c4759aa` |

### ① 메뉴 이동
- `routes.ts`: `/tools/silson-calculator` → **`/crm/claim-calculator`**, title **"보험금 청구계산기"**, section **"고객관리"**, 배열 위치를 `신규설계 비교` 바로 다음으로.
- `navigationConfig.ts`: `crm` 탭 items 의 `신규설계 비교` **바로 아래**에 `보험금 청구계산기` 추가. `analytics-tools` 탭 무변경.
- **기존 경로 처리 = 리다이렉트** (ANU 권고 채택). `App.tsx` 에 `<Route path="/tools/silson-calculator" element={<Navigate to="/crm/claim-calculator" replace />} />`.
  - 근거 1: `RouteConfig` 타입에 `redirect` 필드가 없어 routes.ts 로는 표현 불가 → 라우터 계층이 유일한 지점.
  - 근거 2: 바로 윗줄에 `/info-keyword` 동일 패턴 선례가 이미 있다(`replace` 포함).
  - 근거 3: 앱 **내부** 링크는 `grep` 결과 0건 → 리다이렉트는 순수하게 **외부 북마크·기존 링크 보호용**이다. 내부 깨진 링크는 없다.

### ② 실손 / 정액
- `claimKind` 상태, **기본값 `"silson"`** (기존 동작 무손상의 출발점).
- 정액 선택 시 계산기 대신 "**정액 보험금 청구계산기는 준비 중입니다.**" 안내 카드만.
- `<h1>실손계산기</h1>` → **"보험금 청구계산기"** (메뉴명과 일치).
- ★ **"보장성" 단어 사용 0건** (`grep -rn "보장성" src/` = 0). 회장 용어 "정액" 준수.

### ③ 고객 선택 + 세대 자동판정
- 기존 API **재사용, 신설 0건**: `customerAnalyses.ts` 의 `searchCustomers` · `fetchCustomerAnalyses`.
- 새 순수 모듈 `src/components/policy/claimCustomerLink.ts` — `extractEnrollmentCandidates()` 가 저장된 증권분석에서 가입일 후보를 뽑는다(fetch 없음, 단위 테스트 8건).
- 적용 동작은 **`setEnrollmentDate(후보.date)` 뿐**이다. **세대 판정은 프론트가 하지 않는다** — 기존대로 서버 `generation_resolution` 이 판정한다(계산 로직 봉인 준수).
- 세대 불일치 처리: 기존 `L634` 의 `mismatch && warning` amber 박스가 이미 존재해 **중복 구현하지 않았다**.

**정직성 장치 (조용한 실패 방지)**
- 후보 2건 이상이면 **자동 선택하지 않고** 사용자가 고르게 한다(보험사·상품명·가입일 표시).
- 가입일이 "YYYY-MM" 까지만 확인되면 `-01` 로 채우되 **화면에 "1일로 가정했다"고 고지**한다(세대 경계 오판 위험 노출).
- 형식 판별 불가한 6자리(`202107` 등)는 **후보에서 제외** — 연-월/연-일 구분이 불가능해 추측하지 않는 쪽을 택했다.
- 후보 0건 / 검색 실패 / 조회 실패 / `droppedCount` 를 각각 화면에 표시한다.
- 사용자가 가입일을 손으로 고치면 "저장된 분석에서 가져옴" 출처 표시를 해제한다(거짓 표시 방지).

### ④ "검토 참고용"
- 상수 `REVIEW_REFERENCE_NOTICE`(`SilsonCalculator.tsx:179`), 화면 노출 `L1238` (`data-testid="review-reference-notice"`).
- 위치: 결과 카드 안, **총 추정 금액 바로 위**. 배지 "검토 참고용" + 본문 "이 금액은 확정 보험금이 아니라 1차 검토 참고용 추정치입니다…".
- 기존 `DISCLAIMER_TEXT` / `ANNUAL_CAP_NOTICE_TEXT` 는 **무변경**(engine.py 와 축자 일치 봉인 유지).

---

## 2. 검증

### 회귀 — base/head 같은 환경 재측정
```
base (32ec25a)  121 files / 1761 tests  전부 통과  exit 0
head (c4759aa)  124 files / 1789 tests  전부 통과  exit 0
델타            +3 files / +28 tests    실패·감소 0
```

### 봉인 변이 3종 — **실제 FAIL 출력 확보** (추정 아님)
| 변이 | 결과 |
|---|---|
| A: 메뉴를 `analytics-tools` 로 원위치 | **7 failed / 4 passed** — `getActiveTabFromPath` 가 `'analytics-tools'` 반환, 인덱스 `-1` |
| B: "검토 참고용" → "참고" 로 제거 | **1 failed** — `expected '…1차 참고 추정치입니다…' to contain '검토 참고용'` |
| C: `canCalculate` 에 `&& selectedCustomer !== null` 추가 | **7 failed / 2 passed** — `expect(element).not.toBeDisabled()` 실패 |
전부 `git checkout --` 로 원복, 원복 후 재통과 확인.

★ 변이 B 에서 **봉인 설계 결함 1건을 잡아 고쳤다**: 결과 영역에는 배지 리터럴 "검토 참고용" 과 상수 본문 두 곳에 문구가 있어, `textContent` 를 통째로 검사했으면 **상수를 죽여도 배지 때문에 통과**했을 것이다. 배지와 본문을 **분리 단언**하도록 바꿔 실제로 FAIL 을 잡았다.

### ★★ 고객 미선택 계산 무손상 — 최우선 회귀
- `canCalculate` 정의가 **base 와 바이트 동일**: `rows.length > 0 && (generation !== "" || enrollmentDate.trim() !== "")` (base L475 → head L667, 내용 무변경).
- **정적 검사가 아니라 렌더 테스트로 실증**했다. `@testing-library/react` 로 페이지를 마운트해 항목 추가 → 가입일 입력 → 계산 버튼 활성 확인 → **estimate 요청 실발신 + body 에 고객 식별자 부재 확인 → 결과 렌더("계산완료")** 까지 끝까지 통과.
- 반대 방향도 봉인: 항목 0건이면 비활성 / 가입일 지우면 비활성 (계약이 그냥 느슨해진 게 아님을 증명).

### 실브라우저 E2E — 스크린샷 7장 (`memory/reports/task-3066-shots/`)
★ 팀장이 이미지를 **직접 눈으로 확인**했다. 로그인월 아님 — 실제 로그인 상태의 앱이다. PII 없음(실 CRM 데이터 미조회, Supabase REST 빈 배열 모킹).

| 파일 | 확인 내용 |
|---|---|
| `1-crm-sidebar-…png` | 고객관리 사이드바 실측 순서: 대시보드 → 고객 목록 → 파이프라인 → 고객 증권분석 → **신규설계 비교 → 보험금 청구계산기** → 메신저 → 디지털 명함 ✅ |
| `2-analytics-tools-…png` | 분석 & 도구 탭에 청구/실손 계산기 링크 **0건** ✅ |
| `3-legacy-path-redirect.png` | `/tools/silson-calculator` → 최종 URL **`/crm/claim-calculator`** ✅ |
| `4-fixed-claim-coming-soon.png` | 정액 → "준비 중" ✅ |
| `5a·5b·5c` | 고객 미선택 수동 계산 → 결과에 **"검토 참고용" 배지 실재** ✅ |

### 실물 영수증 계산 (회장 제공, 경찰병원 2025-07-30)
입력: 종합병원 · 외래 · 급여 본인부담 268,400(covered) · 비급여 40,058(uncovered)

```
계산 상태   partial (일부만 계산됨)
예상 수령액 10,058원
근거        비급여 40,058 → 공제 MAX(30,000, 30%=12,017) = 30,000 → 지급 10,058
제외        급여 본인부담 268,400 (미계산)
```
**타당성 판단: 계산된 부분의 산식은 맞다.** 비급여 공제 규정(정액 3만원과 30% 중 큰 값)이 정확히 적용됐다.

★★ **그러나 영수증 금액의 87%(268,400원)가 EXCLUDED 된다.** 사유는 "약관 원문에서 계산 가능한 수치가 확인되지 않았습니다" — **4세대 통원·급여·종합병원 셀이 UNKNOWN** 이기 때문이다.
- 화면 표기 자체는 **정직하다**: "일부만 계산됨" 배지 + "입력 총액 308,458원 중 계산된 40,058원 기준 (미계산 268,400원 제외)" 를 명시한다. 조용한 0원 처리가 아니다.
- 다만 **실효 커버리지 문제**다. 설계사가 실제로 가장 많이 쓸 조합(통원·급여)이 비어 있으면 이 계산기는 대부분의 영수증에서 "대부분 계산 못 함"을 반환한다. **지식DB 셀 채움 문제이며 이번 범위(프론트) 밖이다 — ANU 판단 요청.**

★ estimate 응답은 **모킹**이다(실 API 는 mock JWT 로 401). 다만 지어낸 값이 아니라 **프로덕션 엔진 `server/silson/calc/engine.py` + 프로덕션 데이터 테이블을 로컬에서 실행해 산출한 실제 응답**이다. 서버 코드는 읽기·실행만 했고 **수정 0줄**이다.

### CI (PR #278, HEAD `c4759aa`)
`merge-safety-check` `hidden-path-audit` `ci/guard` `cancel-kill-switch` `qc-check` `guard` `lock-in-check` `gemini-review-gate` = **success 8종**.
`diagnostic` = success. **`ci` · `e2e-test` = failure → 선재 확정(우리와 무관).**

**선재 판정 근거 5가지**
1. **base main `32ec25a` 자체가 `ci=failure`** (2026-08-30T04:30Z). 우리 브랜치 이전부터 적색이다.
2. **최근 PR 전수 동일**: #273 · #275 · #276 · #277 **모두 `ci=failure` + `e2e-test=failure`**. 우리만의 현상이 아니다.
3. **실패한 E2E 3건이 전부 `existing-unlinked-banner`** (NewDesignComparison 화면) 이다 — `element(s) not found`. 우리는 `NewDesignComparison.tsx` 를 **건드리지 않았다**(금지 경로였고 실제 diff 0줄).
4. **교집합 검사**: 우리 변경 파일 중 NewDesignComparison 이 쓰는 것은 `existingAnalysisUpload.ts` **하나뿐**인데, 그 변경은 `meta?.enrollment_date?: string` **optional 타입 필드 1줄 추가**다. TypeScript 인터페이스는 컴파일 시 소거되므로 **런타임 동작이 바뀔 수 없다** → E2E 실패의 원인이 될 수 없다.
5. **우리가 바꾼 메뉴 쪽 E2E 는 통과했다**: 같은 `task-2998-menu-consolidation.spec.ts` 안에서 메뉴 검증 1·2·3번은 PASS 하고 **4번 배너 항목만 실패**했다. 메뉴 이동이 기존 메뉴 E2E 를 깨지 않았다는 반대 증거다.

---

## 3. 범위 준수

변경 파일 **9건 전부 allowed_resources 안**이다:
```
src/App.tsx · src/config/routes.ts · src/pages/SilsonCalculator.tsx        (exact)
src/components/navigation/navigationConfig.ts                              (src/components/**)
src/components/policy/claimCustomerLink.ts                                 (src/components/**)
src/components/policy/existingAnalysisUpload.ts                            (src/components/**)
src/components/policy/__tests__/claimCustomerLink.test.ts                  (src/components/**)
src/pages/__tests__/SilsonCalculator.task-3066.test.tsx                    (src/pages/__tests__/**)
src/config/__tests__/task-3066-claim-calculator-menu.test.ts               (src/**/__tests__/**)
```
- `server/**` 변경 **0건**. `.github/workflows/**`·`NewDesignComparison.tsx`·`Generate.tsx`·`planSkillMap.ts`·`policy_grouping/**`·`policy_extract/**`·벤치마킹 **무변경**.
- `SilsonCalculator.tsx` 는 **재작성하지 않았다** — 실손 본문 JSX 를 조건부로 감싸며 재들여쓰기했을 뿐, 실손 선택 시 렌더 결과는 원문과 동일하다.

### ★ allowed_resources 명세 결함 1건 (ANU 판단 요청)
명세는 **"실물 검증·스크린샷"을 요구**하면서 `allowed_resources.paths` 에 **`tests/**` 를 넣지 않았다.**
- 봉인 테스트가 처음 `tests/task-3066-…` 에 생성돼 범위 위반이었다 → `src/config/__tests__/` 로 **이동해 해소**(`c4759aa`).
- E2E 스펙(`tests/e2e/**`)은 **커밋하지 않았다.** 작성 → 실행 → 스크린샷 확보 → **파일 삭제** 로 처리해 diff 에 남기지 않았다. 최종 워킹트리 청결.
- **대가**: 이 E2E 는 CI 에 남지 않는다(재현하려면 다시 작성해야 함). 영구 보존을 원하시면 `allowed_resources` 에 `tests/e2e/**` 를 추가해 주시면 재커밋하겠다.

---

## 4. 남은 판단 / ANU 대기

1. ★★ **급여 통원 셀 UNKNOWN** — 실물 영수증의 87%가 계산 제외된다. 지식DB 셀 채움 문제(범위 밖). 이대로 배포하면 회장님이 화면에서 "대부분 계산 안 됨"을 보시게 된다. **선조치 여부 판단 필요.**
2. **머지·배포는 ANU** — 자동 머지 하지 않았다. draft 상태 유지.
3. **사이드바 라벨 잘림** — 사이드바 폭 때문에 "보험금 청구계…" 로 표시된다(스크린샷 1). 기능 문제는 아니나 회장 확인 시 눈에 띌 수 있다.
4. **선재 tsc 에러 10건** — `npx tsc --noEmit` 은 루트 tsconfig 가 `"files": []` 라 **아무것도 검사하지 않는다**(공허한 통과). 실제 검사는 `tsc -p tsconfig.app.json`이며 **작업 전부터 10건**이 있었고 작업 후에도 동일 10건, 우리 파일에서는 0건이다. 우리와 무관한 선재 결함.
5. **worktree `.env` 부재** — E2E 시 앱이 백지가 된다(`supabaseUrl is required`). 검증 중 임시 복사 후 삭제했다. 이 worktree 에서 E2E 를 돌릴 사람은 같은 조치가 필요하다.

---

## S — 상황

InsuRo 에 실손 계산기(`SilsonCalculator.tsx` 984줄)가 이미 완성돼 있었으나, 라이브 메뉴(`navigationConfig.ts`)에 등록되지 않아 URL 직접 입력으로만 도달 가능했다. 회장은 이를 "고객관리 > 신규설계 비교 아래 '보험금 청구계산기'" 로 옮기고, 내부에서 실손/정액을 고르며, 금액은 "검토 참고용" 으로 표기할 것을 지시했다.

## C — 복잡성

ANU 명세는 `routes.ts` 의 `section` 만 바꾸면 메뉴가 이동한다고 전제했다. 그러나 `section` 을 읽는 `AppSidebar.tsx` 는 import 하는 곳이 0건인 죽은 컴포넌트였다. 명세대로만 고쳤으면 사용자 눈에 아무 변화가 없는 no-op 이 될 뻔했다. 또한 기존 수동 계산 사용자를 잃지 않으려면 고객 연계가 계산의 전제조건이 되어서는 안 됐다.

## Q — 질문

라이브 메뉴를 실제로 이동시키면서, 기존 수동 계산 동작을 한 톨도 깨뜨리지 않고, 결과 금액이 확정치로 오인되지 않게 하려면 무엇을 바꿔야 하는가?

## A — 답변

`routes.ts`(라우트 등록)와 `navigationConfig.ts`(라이브 메뉴) 양쪽을 수정하고, 기존 경로는 `App.tsx` 리다이렉트로 보존했다. 고객 연계는 `canCalculate` 를 건드리지 않는 순수 추가 기능으로 만들었고(base 와 바이트 동일), 결과 카드 금액 바로 위에 "검토 참고용" 배지를 넣었다. 봉인 변이 3종으로 각 계약이 실제로 FAIL 하는지 실증했다.

## 수정 파일별 검증 상태

| 파일 | 변경 내용 | 검증 방법 | 상태 |
|---|---|---|---|
| src/config/routes.ts | 경로·title·section 변경 + 위치 이동 | 봉인 A 11건 + 브라우저 스크린샷 1 | PASS |
| src/components/navigation/navigationConfig.ts | crm 탭에 청구계산기 추가 | 봉인 A(인덱스 단언) + 스크린샷 1·2 | PASS |
| src/App.tsx | 레거시 경로 리다이렉트 | 실브라우저 URL 실측 /crm/claim-calculator | PASS |
| src/pages/SilsonCalculator.tsx | 실손/정액 선택 + 고객 연계 + 검토 참고용 | 봉인 B·C 렌더 테스트 9건 + 스크린샷 4·5 | PASS |
| src/components/policy/claimCustomerLink.ts | 가입일 후보 추출 순수 모듈 | 단위 테스트 8건 | PASS |
| src/components/policy/existingAnalysisUpload.ts | optional 타입 필드 1줄 추가 | 타입 전용(런타임 무영향) + 전체 회귀 | PASS |
| src/config/__tests__/task-3066-claim-calculator-menu.test.ts | 봉인 A | 변이 A 로 7 failed 실증 | PASS |
| src/pages/__tests__/SilsonCalculator.task-3066.test.tsx | 봉인 B·C | 변이 B 1 failed / 변이 C 7 failed 실증 | PASS |
| src/components/policy/__tests__/claimCustomerLink.test.ts | 후보 추출 경계값 | vitest | PASS |

## Why 분석 — 왜 메뉴가 없었나

- **1st Why**: 실손계산기가 라이브 메뉴에 없었다. → `navigationConfig.ts` 에 항목이 등록된 적이 없다.
- **2nd Why**: 왜 등록되지 않았나? → 메뉴 소스가 `routes.ts` 와 `navigationConfig.ts` 두 벌로 갈라져 있고, 개발자가 `routes.ts` 에만 등록했다.
- **3rd Why**: 왜 두 벌인가? → 사이드바를 `navigationConfig` 기반으로 교체하면서 `routes.ts` 의 `section` 과 `AppSidebar.tsx` 를 제거하지 않고 남겨, 죽은 메타데이터가 살아 있는 것처럼 보이는 상태가 됐다. **이 이중 소스가 남아 있는 한 같은 사고는 재발한다 — `AppSidebar`/`section` 정리를 별도 태스크로 권고한다.**

---

## L1 스모크테스트

실브라우저(Chromium/Playwright)로 빌드된 앱을 `http://localhost:8080` 에 띄우고 직접 조작해 확인했다. jsdom 단위 테스트가 아니라 **실제 렌더된 화면**이다.

| # | 시나리오 | 실측 결과 | 판정 |
|---|---|---|---|
| 1 | 고객관리 탭 진입 → 사이드바 항목 순서 확인 | 신규설계 비교 → 보험금 청구계산기 순으로 노출 | PASS |
| 2 | 분석 & 도구 탭 진입 → 청구/실손 계산기 검색 | 링크 0건 | PASS |
| 3 | /tools/silson-calculator 접속 | 최종 URL = /crm/claim-calculator 로 리다이렉트 | PASS |
| 4 | 정액 선택 | "정액 보험금 청구계산기는 준비 중입니다." 노출 | PASS |
| 5 | 고객 미선택 + 영수증 수치 입력 → 계산 | 결과 렌더, "검토 참고용" 배지 노출, 10,058원 | PASS |

- 실행: `npx playwright test` (chromium), 4 spec 전부 통과. 인증은 기존 정본 패턴(Supabase mock JWT + localStorage 주입) 재사용.
- 스크린샷 7장을 팀장이 **눈으로 직접 확인**했다 — 로그인월 아님, 실제 로그인 상태의 앱이다. PII 없음.
- estimate 응답은 모킹이다(실 API 는 mock JWT 로 401). 단 모킹 페이로드는 프로덕션 엔진을 로컬 실행해 산출한 실제 응답이며, 서버 코드 수정은 0줄이다.
- ★ 한계: 이 E2E 스펙은 `tests/e2e/**` 가 allowed_resources 밖이라 **커밋하지 않았다**(실행 후 삭제). CI 에는 남지 않는다.

## 수정 파일

CODE_ROOT = `/home/jay/projects/.worktrees/InsuRo-task-3066` (worktree, base 32ec25a)

- /home/jay/projects/.worktrees/InsuRo-task-3066/src/App.tsx
- /home/jay/projects/.worktrees/InsuRo-task-3066/src/config/routes.ts
- /home/jay/projects/.worktrees/InsuRo-task-3066/src/pages/SilsonCalculator.tsx
- /home/jay/projects/.worktrees/InsuRo-task-3066/src/components/navigation/navigationConfig.ts
- /home/jay/projects/.worktrees/InsuRo-task-3066/src/components/policy/claimCustomerLink.ts
- /home/jay/projects/.worktrees/InsuRo-task-3066/src/components/policy/existingAnalysisUpload.ts
- /home/jay/projects/.worktrees/InsuRo-task-3066/src/components/policy/__tests__/claimCustomerLink.test.ts
- /home/jay/projects/.worktrees/InsuRo-task-3066/src/pages/__tests__/SilsonCalculator.task-3066.test.tsx
- /home/jay/projects/.worktrees/InsuRo-task-3066/src/config/__tests__/task-3066-claim-calculator-menu.test.ts

---

## scope-guard FAIL — 스냅샷 부재 오탐 (★ 자가해소하지 않았음, ANU 판단 요청)

```
[scope-guard] ERROR: snapshot 없음 — dispatch 미수행?
              (/home/jay/workspace/memory/capabilities/task-3066.json)
[SCOPE-GUARD] FAIL — 머지 차단 + .escalate 생성
```

**판정: 범위 위반이 아니라 capabilities 스냅샷 부재로 인한 오탐이다.** 근거 3가지:

1. **스냅샷 파일이 애초에 없다.** `memory/capabilities/` 의 최신은 `task-3052.json` 이고 `task-3066.json` 은 생성된 적이 없다. 이 태스크는 `dispatch.py` 를 경유하지 않고 **스케줄 실행으로 착수**돼 dispatch 시점 immutable snapshot 이 만들어지지 않았다. 가드는 **대조 대상이 없어서** FAIL 한 것이지 위반을 발견한 것이 아니다.
2. **명세 자체는 정상이다.** `memory/plans/tasks/task-3066.md` 에 `allowed_resources` 블록이 존재한다(grep 2건). 명세 결함이 아니다.
3. **수동 전수 대조 결과 위반 0건.** `scope-diff.txt` 의 9개 파일을 허용 glob 과 1:1 대조했고 **9/9 전부 IN-SCOPE** 다(보고서 §3 표 참조). `server/**` 등 금지 경로 변경은 0건이다.

★ 운영 규율에 따라 **봇이 자가해소하지 않았다.** `.escalate` 를 그대로 두고 ANU 판단(override 박제)을 요청한다.
