# task-2787+2 보고서 — A-0 진단 전용 빌드(dist-diagnostic) self-smoke 준비

- 작업 ID: task-2787+2 (rev5, ANU↔Codex 합의 + 회장 승인)
- 팀: dev5-team (마르둑/엔키/닌기르수)
- 프로젝트: InsuRo (Chrome 확장 `extension/`)
- worktree: `/home/jay/projects/InsuRo/.worktrees/task-2787+2-dev5` (branch `task/task-2787+2-dev5`, base origin/main)
- 게이트 레벨: Lv.2 / 검증 레벨: normal
- 완료일: 2026-07-18

---

## SCQA

**S (Situation)** — InsuRo 확장의 프로덕션 소스에는 옛 자동 캡처 흔적(inject.js WAR, `scripting` 권한)이 남아 있고, 회장 self-smoke용 진단 능력(실 API transport·데이터 계약 검증)은 부재했다. 이전 task-2787+1은 진단 코드를 프로덕션 소스에 직접 섞어 STOP 계약을 이탈했다.

**C (Complication)** — 진단 코드를 프로덕션에 섞으면 회장 시연 중 서버 전송/저장/자동 고객생성 리스크가 살아 있고, 소스가 "production-safe"라는 주장이 성립하지 않는다. 동시에 `new_file_limit:2` 제약으로 별도 adapter/resolver lib 파일 추가가 불가했다.

**Q (Question)** — 프로덕션 소스를 100% 깨끗하게 유지하면서, 결정론적으로 재생성 가능한 진단 전용 빌드만 별도로 산출할 수 있는가?

**A (Answer)** — ①소스 manifest 프로덕션 안전화(MAIN 0·WAR 0·scripting 0·flag OFF) ②self-contained MAIN-world 인터셉터 inject.js(로드 시 완전 passive, 빌드가 주입한 `__INSURO_DIAG_ACTIVE__=true`에서만 활성) ③`build-diagnostic.mjs`가 @babel AST로 결정론적 transform(재빌드 시 바이트 동일 확인) 하여 dist-diagnostic 산출. 정적 검증 48/48 테스트 통과 + jsdom L1 실동작(인터셉트→sanitize→summary→화면총액 대조, PII 유출 0) 확인. **스모크 통과 판정 한정: `REAL_BROWSER_CAPTURE_PATH_VERIFIED` + `DATA_CONTRACT_SUPPORTED` (RELAY_SECURITY/PRODUCTION/CRM_READY 아님).**

---

## 생성/수정 파일

수정(2):
- `extension/manifest.json` — `scripting` 권한 제거, `web_accessible_resources` 전체 삭제, `INSURO_DIAG_FLAG:false` 추가. (MAIN 엔트리는 원래 부재)
- `extension/inject.js` — no-op stub → self-contained MAIN-world 진단 인터셉터(471줄). passive-until-flag, 순수 헬퍼 8종 export, 배너/버퍼/summary/화면총액 대조 인라인.

신규(2, new_file_limit 준수):
- `extension/scripts/build-diagnostic.mjs` — @babel 기반 결정론적 AST transform(525줄).
- `extension/__tests__/diagnostic_build.test.ts` — 수용기준 시맨틱 검증 19건(641줄).

미수정(프로덕션 유지, 소스 불변): `extension/content.js`, `extension/background.js` — 오직 build transform 대상. 프로덕션 플로팅버튼은 현행 제품이므로 live 유지(범위 제외).

산출물(빌드 생성, 미커밋·로컬 exclude): `dist-diagnostic/` (manifest·inject·background·content·DO_NOT_PUBLISH.marker·transform-report.json·정적파일).

---

## 수용 기준 시맨틱 검증 결과 (증거: `diagnostic_build.test.ts` 19 PASS)

소스 production-safe:
- manifest MAIN 엔트리 0 · inject WAR 0 · `scripting` 0 · `INSURO_DIAG_FLAG`===false (JSON/AST 증명) — PASS
- `chrome.scripting` 사용 0(코드 전체) — PASS

dist-diagnostic (AST 시맨틱, grep 보조):
1. `chrome.storage.local` 접근 0 — dot/bracket/alias/destructuring 4형태 AST 탐지 헬퍼로 검증 — PASS (background 12건·content 1건 → Promise.resolve 치환)
2. background insuro/localhost POST/fetch 경로 0 — `fetch` CallExpression 0 (pushMatrixToInsuro no-op) — PASS
3. 리스너 인벤토리 게이트 — onMessage/onMessageExternal 콜백 전수 열거 → 본문 서브트리에 fetch·storage.local 부재 — PASS
4. `initInsuroFloatingButton` 호출 0 (선언은 dead code로 잔존 허용) · content dead 송신 no-op — PASS
5. MAIN 엔트리·flag ON·DIAGNOSTIC 배너(`id=insuro-diag-banner`, z-index 2147483647)·별도 key(id)·publish 마커 3기준 — PASS
   - R2(f) 3기준: ① name/version_name에 `DIAGNOSTIC_DO_NOT_PUBLISH` ② `DO_NOT_PUBLISH.marker` 파일 존재 ③ .zip/.crx 0개(unpacked only)
6. transform-report.json 존재 — 32건 change(file/kind/target/before/after) — PASS
- 소스 로드 시 patch/postMessage/buffer 0 (require 시 fetch identity 불변·배너 DOM 부재) — PASS

---

## L1 스모크테스트 결과 (필수 기록)

- **서버 재시작: 해당없음** — Chrome 확장 진단 빌드(서버 없음). 진단 빌드는 서버 전송 0이 요구사항이며 실 Edge self-smoke는 회장 실행(승인 후).
- **API 응답 확인: 해당없음(서버 미대상)** — 대신 빌드 실행 + 산출물 시맨틱 검증으로 대체.
- **빌드 실행**: `node extension/scripts/build-diagnostic.mjs` → 성공(32 changes). 재빌드 diff 0 → **결정론적**.
- **jsdom 실동작 스모크(REAL_BROWSER_CAPTURE_PATH)**: dist inject.js를 jsdom `runScripts:dangerously`로 로드 → `__INSURO_DIAG_ACTIVE__=true` 활성 → `window.fetch` 패치 확인 → `/api/ProductPremiums` 요청 발생 →
  - summary 캡처됨: `{transport, request_fingerprint:"planId=1", selected_coverage_count:1, per_insurer:[{메리츠화재, adapter_sum:12000, coverage_count:1, screen_total:12000, diff:0}], rounding:"EXACT", screen_total_diff, validation_flags}`
  - **화면총액(DOM 12000) vs 어댑터합계(12000) diff=0** 대조 동작 — DATA_CONTRACT_SUPPORTED
  - **PII 유출 0**: consultant_id/ga_id/access_token 모두 summary·버퍼에서 제거 확인(sanitize end-to-end)
- **스크린샷: 해당없음** — 실 UI 렌더 아님(회장 Edge self-smoke 시 배너 육안 확인). 배너 렌더 코드·요소 id 검증 완료.
- 정적 검증: `npx vitest run extension/` → **48 passed (48)** (진단 19 + 회귀 29, 회귀 0).

---

## 모델 사용 기록

- 엔키(백엔드): **sonnet** — manifest/inject.js/build-diagnostic.mjs 구현.
- 닌기르수(테스터): **sonnet** — diagnostic_build.test.ts 시맨틱 검증.
- haiku 미사용(코딩·검증 작업 성격상 부적합).
- 팀장(마르둑, Opus): 설계/분배/통합/검토/L1 실동작 스모크 + 인지 검증.

---

## 발견 이슈 및 해결 (Zero Issue = Red Flag 준수)

1. **[해결] Node `require()` in `"type":"module"` 저장소** — inject.js(순수 스크립트)를 Node24가 ESM 강제 판정 → `node -e require`로는 module.exports 미실행. **원인 격리**: `/tmp` CJS 격리 디렉토리 require 시 8함수 정상 export 확인, 참고 원본도 동일 → 저장소 전역 config 특성(코드 결함 아님). 실행 경로(vitest vite-transform, Chrome MAIN world)는 무관 → **48 테스트 정상 통과로 무해 확인**.
2. **[수용] 소스 inject.js에 `__INSURO_DIAG_ACTIVE__` 1건 잔존** — 읽기 전용 게이트 조건문. **대입문(`= true`)은 소스 0건**(빌드만 prepend). 테스트 #14가 대입 부재를 AST/정규식으로 검증 → passive 불변식 유지.
3. **[해결] L1 스모크 배너 selector 오탐** — 초기 selector가 "DIAGNOSTIC" 텍스트를 포함한 `<script>` 요소를 오매칭. dist inject.js 직접 확인으로 실제 배너 요소(`div#insuro-diag-banner`) 렌더 코드 검증 완료(harness 문제, 제품 무관).
4. **[수용/노트] Chrome MV3 unrecognized manifest key 경고** — 최상위 커스텀 키 `INSURO_DIAG_FLAG`는 로드 시 경고만 발생(하드 에러 아님). diffable 마커 목적(소스 false↔빌드 true)이라 최상위 배치가 최선. unpacked 내부/진단 로드에 무해 — 회장 참고.
5. **[수용] 린트 경고 잔존** — inject.js `ppBuffer`(탭별 버퍼: 캡처 시 write·unload 시 null, R3 "탭별 in-memory buffer·종료 시 폐기" 충족이나 summary는 sanitized 인라인 계산이라 read 미발생), XHR `body` 미사용(send 시그니처 arity 보존), `.mjs`/`.ts`의 @babel/traverse d.ts 부재(런타임 무해). 기능·보안 영향 0.

범위 외 미해결: 없음. (relay race 미해결·완전격리(무결성)는 본 SPEC 범위 밖 — 후속 B, DIAGNOSTIC_ONLY.)

---

## 머지 판단

- **머지 필요: No** — 본 task는 **PR·merge·배포 금지**(allowed_resources `merge_policy:none`, 회장 승인 후 self-smoke). worktree를 keep하여 회장 진단 dist 로드 대기.
- **브랜치**: `task/task-2787+2-dev5`
- **워크트리 경로**: `/home/jay/projects/InsuRo/.worktrees/task-2787+2-dev5`
- **머지 의견**: 정적/실동작 검증 완료(48 tests·결정론성·PII 0). 단, **relay 무결성 미해결(DIAGNOSTIC_ONLY)** 이므로 프로덕션 병합 대상 아님. 회장 self-smoke → 승인 절차 후 별도 게이트에서 결정.

---

## 범위·정직 (과장·모순 금지)

본 SPEC 실효과 = ①inject 잔여표면 부분폐쇄 ②플로팅버튼 POST는 범위제외(프로덕션 live 유지) ③storage 부분폐쇄(semantic 검증 강화) ④오적재 노출저감. **"4갭 완전 닫음" 아님.** 코드 가드(별도 id·DIAGNOSTIC 배너·publish 차단 마커)는 구현했으나, **사람이 잘못된 unpacked 폴더를 수동 로드하는 것은 코드로 불가** → 운영 절차(한 번에 하나만 로드) 필요.

## self-smoke 안내 (회장 실행 — 승인 후)
1. `node extension/scripts/build-diagnostic.mjs` 실행 → `dist-diagnostic/` 생성.
2. Edge `edge://extensions` → 개발자 모드 → "압축해제된 확장 로드" → `dist-diagnostic/` 폴더 선택(**한 번에 이 폴더만** 로드).
3. 로드 시 상단 빨간 `⚠ INSURO DIAGNOSTIC BUILD — DO_NOT_PUBLISH` 배너 확인.
4. ohmymanager(mmlfcp) 정상 로그인 → 보험료 조회 → DevTools Console `[InsuRo Diagnostic]` summary 확인.
5. 담보·금액·보험료 matrix + 화면총액 vs 어댑터합계 diff 대조 → 서버전송·CRM·자동생성 0 확인 → 연속조회 buffer 혼입 0 → 사용 후 확장 제거(폐기).
