# task-3069 — 보험금청구계산: 사이드바 메뉴명 잘림 해소

- **팀**: dev4 (비슈누) · **레벨**: Lv.1 · **일자**: 2026-08-30 KST
- **저장소**: `Jeon-Jonghyuk/InsuRo` · base `752fd36` · 브랜치 `task/task-3069-dev4`
- **PR**: #281 (draft) — https://github.com/Jeon-Jonghyuk/InsuRo/pull/281 · HEAD `e57cb08`
- **결론**: **잘림 해소 실측 확인(116px>110px → 98px=98px) · 봉인 테스트 새 문구로 통과 · 메뉴 위치 무이동**

---

## 0. ★★ ANU 명세 반증 1건 — `routes.ts` 만 고쳤으면 no-op 이었다

명세는 `routes.ts:336` 을 "**← 사이드바 메뉴명**" 이라고 단정했다. **거짓이다.**

```
routes.ts 의 title 을 읽는 곳      = src/components/AppSidebar.tsx 뿐
AppSidebar.tsx 를 import 하는 곳   = 0건   ← 죽은 컴포넌트
라이브 사이드바                     = src/components/navigation/navigationConfig.ts 의 domainTabs
                                     → ContextSubMenu / TopNavBar / MobileBottomNav 가 렌더
그 안의 메뉴명                      = navigationConfig.ts:42   ← 명세에 없음
```

명세의 "표시에 실제 영향" 목록 2건 중 **사이드바 항목이 틀렸다.** 명세대로 `routes.ts:336` + `SilsonCalculator.tsx:842` 만 고쳤으면 화면 상단 제목만 바뀌고 **회장님이 지적하신 사이드바 잘림은 그대로 남았을 것이다.**

★ 이는 task-3066 보고서 §0 이 이미 기록한 것과 **같은 계열의 누락**이다. 그때도 `AppSidebar` 가 죽은 컴포넌트라는 사실이 명세에 반영되지 않았다.

**조치**: `navigationConfig.ts:42` 를 포함해 수정했다. 이 파일은 t3066 에서 이미 허용 경로였고, **메뉴 위치·section·URL 은 건드리지 않고 title 문자열만** 바꿨다(금지사항 준수).

**교차 확인**: 봉인 테스트가 이 판단을 독립적으로 뒷받침한다. 상수만 새 문구로 바꿨을 때 실패한 3건 중 **2건이 `navigationConfig` 대상**이었다(§3). 즉 명세 누락분을 고치지 않으면 테스트 자체가 통과하지 못한다.

---

## 1. 만든 것 — 4파일, 문자열만 (커밋 `e57cb08`)

| 파일 | 라인 | 변경 |
|---|---|---|
| `src/components/navigation/navigationConfig.ts` | 42 | ★ 라이브 사이드바 `title` (명세 누락분) |
| `src/config/routes.ts` | 336 | `title` |
| `src/pages/SilsonCalculator.tsx` | 842 | `<h1>` |
| `src/pages/SilsonCalculator.tsx` | 883 | 정액 안내 문구 (§2 판단) |
| `src/config/__tests__/task-3066-claim-calculator-menu.test.ts` | 24 외 | 봉인 상수 갱신 + **강화** |

`보험금 청구계산기` → **`보험금청구계산`** — 회장 지정 문구 그대로. 띄어쓰기 없음. 임의 변형 0건.

diff 는 **10 insertions / 10 deletions**. 로직 변경 0줄.

### ★ 봉인 약화 위험 1건을 발견해 막았다

테스트 68행이 analytics-tools 탭의 잔존물을 `i.title.includes("청구계산기")` 로 탐지하고 있었다.
새 문구 `보험금청구계산` 에는 **`청구계산기` 가 substring 으로 없다.** 그대로 뒀으면 이름을 바꾼 순간 이 유일성 봉인이 **조용히 무력화**된다(테스트는 계속 초록).

→ `includes("청구계산")` 으로 **강화**했다. 옛 문구·새 문구 **양쪽 다** 잡는다. 계약을 약화시킨 곳은 없다(`expect` 20건 유지, 테스트 11건 유지).

---

## 2. 명세 ④ 판단 — `SilsonCalculator.tsx:883` 정리했다

```
변경 전: "정액 보험금 청구계산기는 준비 중입니다."
변경 후: "정액 보험금 청구 계산은 준비 중입니다."
```

**근거 3가지**
1. 이 작업 후 `보험금 청구계산기` 라는 제품명은 UI 어디에도 존재하지 않는다. 이 문장만 남겨두면 사용자가 메뉴에서는 "보험금청구계산", 카드에서는 "보험금 청구계산기" 라는 **두 개의 이름**을 보게 된다.
2. 새 문구를 그대로 넣은 `"정액 보험금청구계산은 준비 중입니다"` 는 어색하다(`계산` + `은`). 문장 안에서는 띄어쓴 서술형이 자연스럽다.
3. 바로 다음 줄이 이미 `"지금은 실손 청구만 계산할 수 있습니다."` 로 **계산이라는 기능**을 가리킨다. 제품명이 아니라 기능을 가리키는 서술로 통일했다.

**메뉴명은 회장 지정 문구를 글자 그대로 유지**했다. 정리한 것은 문장 안의 서술 표현뿐이다.
`data-testid="fixed-coming-soon"` 로 잡는 테스트라 문구 변경에 영향 없다(문자열을 고정하는 테스트 0건).

---

## 3. 봉인 검증 — no-op 아님 선증명 → 변이 2종

### ① 선증명 (구현 전, 상수만 새 문구로 교체)
소스는 옛 문구 그대로 둔 채 `CLAIM_CALCULATOR_TITLE` 만 바꿨다.

```
Tests  3 failed | 8 passed (11)
  ✘ crm 탭에 /crm/claim-calculator 항목이 정확히 1건 존재한다      ← navigationConfig
  ✘ 타이틀 리터럴이 url 과 짝지어 유지된다                          ← navigationConfig
  ✘ /crm/claim-calculator 의 section·title                        ← routes.ts
  AssertionError: expected '보험금 청구계산기' to be '보험금청구계산'
```
→ 이 테스트는 **문구 리터럴에 실제로 걸려 있다.** 통과가 공짜로 나오는 no-op 이 아니다.

### ② 구현 후
```
task-3066-claim-calculator-menu.test.ts   11 passed
SilsonCalculator.task-3066.test.tsx        9 passed
                                          20 passed (2 files)
```

### ③ 변이 2종 — 전건 KILL
| 변이 | 조작 | 결과 |
|---|---|---|
| M1 | `routes.ts:336` title 을 **옛 문구로 되돌림** | **1 failed** / 10 passed → KILL |
| M2 | `navigationConfig.ts:42` title 을 **옛 문구로 되돌림** | **2 failed** / 9 passed → KILL |

양쪽 모두 `git checkout` 으로 복원 확인했고, 최종 워킹트리는 **clean** 이다.

---

## 4. vitest 전수 재측정 (같은 환경, 같은 worktree)

```
base 752fd36   Test Files 131 passed (131)   Tests 1879 passed (1879)   exit 0
HEAD e57cb08   Test Files 131 passed (131)   Tests 1879 passed (1879)   exit 0
델타           0 / 0   ← 문자열 변경이므로 테스트 증감 없음이 정상
```
base 1879 는 t3067 보고서의 1879 와 일치한다(회귀 없음 교차 확인).

---

## 5. 실브라우저 E2E — 잘림을 **픽셀로 실측**했다

★ 팀장이 스크린샷을 **직접 눈으로 확인**했다. 로그인월 아님(상단 네비·플랜 배지·프리미엄 카드가 보이는 실제 로그인 상태). PII 없음.
스크린샷: `memory/reports/task-3069-shots/`

`ContextSubMenu` 의 `<span className="truncate">` 에서 `scrollWidth > clientWidth` 면 말줄임이 발동한다. 이를 직접 쟀다.

| 상태 | 표시 문구 | scrollWidth | clientWidth | **잘림** |
|---|---|---|---|---|
| **before** (옛 문구 주입) | 보험금 청구계산기 | **116** | 110 | **true** ← 회장 지적 재현 |
| **after** (이번 변경) | 보험금청구계산 | **98** | 98 | **false** |

- `before-sidebar.png` — 사이드바에 **"보험금 청구계…"** 로 잘려 표시됨. 회장님 스크린샷과 동일한 증상을 코드로 재현했다.
- `after-sidebar.png` — **"보험금청구계산"** 전체 표시. 말줄임 없음. 여유 12px.
- `after-full.png` — 화면 상단 `<h1>` 도 **"보험금청구계산"** 으로 동일.
- `after-fixed-coming-soon.png` — 정액 카드 **"정액 보험금 청구 계산은 준비 중입니다."** · 옛 문구 페이지 내 **0건**.

### 메뉴 위치 무이동 (DOM 실측 순서)
```
대시보드 → 고객 목록 → 파이프라인 → 고객 증권분석 → 신규설계 비교 → 보험금청구계산 → 메신저 → 디지털 명함
```
**신규설계 비교 바로 아래** 그대로다. before/after 순서 완전 동일. URL 도 `/crm/claim-calculator` 유지.

※ `after-full.png` 의 "가입정보 — Failed to fetch" 는 주입 세션이 실 API 를 호출할 수 없어서 나는 것이고, 이번 변경과 무관하다.

---

## 6. CI (PR #281, HEAD `e57cb08`)

**success 9종** — `merge-safety-check` `hidden-path-audit` `ci/guard` `cancel-kill-switch` `qc-check` `guard` `lock-in-check` `gemini-review-gate` `diagnostic`
**failure 2종** — `ci` · `e2e-test` → **선재 확정**

**선재 판정 근거 4가지**
1. **base main `752fd36` 자체가 `ci=failure`** 다. 우리 브랜치 이전부터 적색이다.
2. **최근 PR 전수 동일**: #277 · #279 · #280 **모두 `ci` + `e2e-test` failure**.
3. **실패 지점이 우리와 교집합 0**:
   - `ci` 는 **"Python 의존성 설치" 스텝**에서 죽는다. 우리 변경은 **TS/TSX 문자열 4줄**이고 Python 파일 0건이다.
   - `e2e-test` 실패 3건은 `existing-unlinked-banner`(NewDesignComparison) 와 `keyword-analysis` 다. 저장소 전체에서 **`청구계산`·`claim-calculator` 를 참조하는 Playwright 스펙은 0건**(`tests/e2e/` grep 실측).
4. **메뉴 E2E 는 통과했다**: `task-2998-menu-consolidation.spec.ts` 의 1·2·3번(고객관리 탭 노출·이동 포함)이 **모두 ✓**, 4번 배너 항목만 실패했다. 메뉴명 변경이 기존 메뉴 E2E 를 깨지 않았다는 반대 증거다.

---

## 7. 금지사항 준수

| 금지 항목 | 실제 |
|---|---|
| 메뉴 위치·section·경로 변경 | **0** — DOM 순서·`section:"고객관리"`·`/crm/claim-calculator` 전부 동일 |
| t3066·t3067 산출물(고객연계·"검토 참고용"·업로드/파싱) | **0** — diff 에 해당 파일 없음 |
| `server/**` | **0** |
| 다른 메뉴 항목 | **0** — `navigationConfig` diff 는 42행 1줄 |
| 봉인 테스트 약화·삭제 | **0** — 오히려 §1 대로 **강화** |

주석은 최소로만 손댔다. `routes.ts:331` · `claimCustomerLink.ts:1` 의 task-3066 이력 주석은 **의도적으로 보존**했다(선택 사항이며 과거 기록으로서 정확하다). 테스트 파일의 서술 문자열은 **거짓 계약을 서술하게 되므로** 갱신했다.

---

## 8. ANU 판단 요청

1. **머지 순서** — 이 PR 은 base `752fd36`(현재 main tip) 기준이라 충돌 요인이 없다. 단독 머지 가능.
2. **`ci` 워크플로 Python 의존성 설치 실패**가 main 에서 상시 적색이다. 이번 범위 밖이나 **필수체크가 아닌데도 계속 적색이라 신호로서 죽어 있다.**
3. **`AppSidebar.tsx` 는 여전히 죽은 컴포넌트**다(import 0건). 지금은 `routes.ts` 와 `navigationConfig.ts` 에 메뉴명이 **2벌** 존재하며, 이번엔 양쪽을 맞췄지만 **구조적으로 다음에도 같은 누락이 재발한다.** 죽은 `AppSidebar` 제거 또는 단일 소스화를 별도 task 로 제안한다.

머지·배포는 ANU. **자동 머지 하지 않았다.**

---

## S — 상황 (SCQA)

**S (Situation)** — 회장님이 사이드바 스크린샷에서 메뉴명이 "보험금 청구계…" 로 잘리는 것을 확인하고, `보험금청구계산` 으로 바꾸라고 지시하셨다.

**C (Complication)** — ANU 명세는 사이드바 메뉴명의 출처를 `routes.ts:336` 으로 지목했으나, 그 파일을 읽는 `AppSidebar.tsx` 는 import 0건인 죽은 컴포넌트였다. 명세대로만 고치면 회장님 화면은 그대로였다.

**Q (Question)** — 실제로 잘림이 해소되는가? 봉인 테스트는 새 문구로 통과하는가? 메뉴 위치는 그대로인가?

**A (Answer)** — 라이브 소스 `navigationConfig.ts:42` 를 함께 수정해 **scrollWidth 116>110(잘림) → 98=98(안 잘림)** 로 실측 해소했다. 봉인 11건 전건 통과, 변이 2종 전건 KILL, 메뉴 순서 before/after 동일.

### 수정 파일별 검증 상태

| 파일 | 검증 방법 | 상태 |
|---|---|---|
| src/components/navigation/navigationConfig.ts | 브라우저 DOM 실측(scrollWidth/clientWidth) + 봉인 2건 + 변이 M2 KILL | PASS |
| src/config/routes.ts | 봉인 1건 + 변이 M1 KILL | PASS |
| src/pages/SilsonCalculator.tsx | 브라우저 스크린샷(h1 · 정액 카드) + 렌더 봉인 9건 | PASS |
| src/config/__tests__/task-3066-claim-calculator-menu.test.ts | 선증명 3 failed → 구현 후 11 passed · expect 20건 유지 | PASS |

### Why 분석

- **1st Why** — 왜 메뉴명이 잘렸는가? 사이드바 컨테이너 가용 폭이 110px 인데 "보험금 청구계산기" 가 116px 이라 `truncate` 가 발동했다.
- **2nd Why** — 왜 116px 이 되었나? task-3066 에서 메뉴를 신설할 때 문구 길이를 컨테이너 폭(180px 고정 aside − padding − 아이콘 − gap) 대비로 검증하지 않았다. 폭 제약이 코드 어디에도 단언되어 있지 않다.
- **3rd Why** — 왜 검증되지 않았나? 봉인 테스트가 **문자열 동일성만** 단언하고 **렌더 폭**은 단언하지 않는다. 메뉴명 길이 제약은 현재 어떤 자동 게이트도 잡지 못하며, 회장님 육안 확인이 유일한 탐지 경로다(§8 후속 제안 대상).

---

## L1 스모크테스트

실브라우저(Playwright + Chromium)로 로그인 상태의 앱을 띄워 직접 확인했다. 팀장이 스크린샷을 육안으로 검토했다.

| # | 항목 | 증거 | 결과 |
|---|---|---|---|
| 1 | 사이드바 메뉴명이 잘리지 않는다 | after-sidebar.png · scrollWidth 98 = clientWidth 98 | 성공 |
| 2 | 잘림이 실재했음(대조군) | before-sidebar.png · scrollWidth 116 > clientWidth 110 | 성공 (재현 확인) |
| 3 | 화면 상단 제목이 같은 문구 | after-full.png · h1 = 보험금청구계산 | 성공 |
| 4 | 메뉴 위치 무이동 | DOM 순서: 신규설계 비교 → 보험금청구계산 | 성공 |
| 5 | 정액 안내 문구 정리 | after-fixed-coming-soon.png · 옛 문구 0건 | 성공 |
| 6 | 로그인월 아님 | 상단 네비·플랜 배지·프리미엄 카드 렌더 · 로그인 버튼 0건 | 성공 |
| 7 | 회귀 | vitest 1879 passed (base 1879 passed, 델타 0) | 성공 |
| 8 | 봉인 변이 | M1 1 failed · M2 2 failed — 전건 KILL | 성공 |

스크린샷 4장: memory/reports/task-3069-shots/ (before-sidebar.png, after-sidebar.png, after-full.png, after-fixed-coming-soon.png)

**판정: PASS** — 회장님이 지적하신 잘림이 해소되었고, 대조군으로 잘림이 실재했음을 같은 코드베이스에서 재현해 증명했다.

## 수정 파일

- src/components/navigation/navigationConfig.ts
- src/config/routes.ts
- src/pages/SilsonCalculator.tsx
- src/config/__tests__/task-3066-claim-calculator-menu.test.ts

---

## 종결 게이트 결과

```
QC (qc_verify --gate)   7 PASS · 0 FAIL · 15 SKIP · 1 WARN   overall=WARN
  WARN 내용 = "design/CLAUDE.md — 310줄 (100줄 초과)"  ← 타팀 파일, 이번 변경과 무관
git_evidence            PASS (커밋 1건 · uncommitted 0 · 변경 파일 4건)
FINALIZE_ONLY=1         머지 미수행 (지시대로 PR 까지가 범위)
```

### ★ scope-guard FAIL = 오탐 (자가해소하지 않음)

```
[scope-guard] ERROR: snapshot 없음 — dispatch 미수행?
              (/home/jay/workspace/memory/capabilities/task-3069.json)
```

판별 2종 모두 오탐 조건에 부합한다.
1. `memory/capabilities/task-3069.json` **부재** — 이 작업은 dispatch 를 경유하지 않은 cron 실행이라 스냅샷이 만들어진 적이 없다.
2. `memory/tasks/task-3069.md` **자체가 부재** → `allowed_resources` grep **0건**.

즉 범위 위반이 탐지된 것이 아니라 **대조할 스냅샷이 없어서** 실패한 것이다. 실제 변경은 4파일 전부 t3066 에서 이미 허용 경로였던 곳이며, 금지 경로(`server/**`, 다른 메뉴, t3066·t3067 산출물) 침범 0건이다(§7).

운영 규율대로 **자가해소하지 않고** 근거만 박제한다. override 판단은 ANU 몫이다.
`memory/events/task-3069.escalate` · `task-3069.scope-diff.txt` 생성됨.

※ 1차 종결 시도에서 `task-3069.supervisor-crash-marker.json` 이 생성되었다. 원인은 보고서에 `## L1 스모크테스트` 섹션이 없어 `l1_smoketest_check` 가 FAIL 한 것이며, 섹션 보강 후 2차 실행에서 **0 FAIL** 로 해소됐다. 잔존 마커는 1차 시도의 흔적이다.
