---
name: diff
description: Compares AS-IS and TO-BE designs, marks the changed elements with native Figma Dev Mode annotations, and writes up the linked task doc with a Figma link, an AS-IS/TO-BE table, and a scope callout. The tracker comes from the task_tracker section of figma-conventions.yaml and can be notion, github, or none; with none it stops at the Figma annotations and emits the table as markdown. AS-IS may sit in another section of the same page or on a different page entirely, so the source is settled first. It never creates annotation categories — it reuses a shared one or uses none, and carries classification in a label tag. Only the representative screen is marked; state variants inherit. Triggers - "/fig:diff", "mark what changed", "annotate the design changes", "as-is to-be 비교", "변경점 표시해줘", "바뀐 요소 annotation 달아줘".
allowed-tools: AskUserQuestion, Bash, mcp__plugin_figma_figma__use_figma, mcp__plugin_figma_figma__get_metadata, mcp__plugin_figma_figma__get_screenshot, mcp__claude_ai_Notion__notion-fetch, mcp__claude_ai_Notion__notion-update-page, mcp__claude_ai_Notion__notion-search, mcp__plugin_github_github__issue_read, mcp__plugin_github_github__issue_write, mcp__plugin_github_github__add_issue_comment
---

# fig:diff — AS-IS / TO-BE 변경점 표기 + 일감 문서 정리

AS-IS / TO-BE 시안을 비교해 **바뀐 요소를 찾아 Figma 네이티브 Dev Mode annotation 으로 핀을 박고**, 같은 내용을 **연결된 일감 문서**에 비교표로 정리한다. 어디에 정리하느냐는 설정 `task_tracker` 가 정한다.

**핵심 원칙**
- 변경점의 **단일 출처는 라벨 텍스트**. 카테고리 색은 보조일 뿐(유실될 수 있음).
- **카테고리를 새로 만들지 않는다.** 공용 카테고리 재사용 또는 무카테고리.
- **대표 화면만** 표기. 상태 변형(Default/Selected, Detail/Toast 등)은 대표 하나로 묶고 "상속" 문구로 처리.
- 모든 쓰기(Figma·Notion)는 **미리보기 → "go"** 게이트.
- AS-IS 위치를 **추측하지 않는다** — 같은 페이지에 없으면 검색하거나 묻는다.

## When to invoke

- AS-IS/TO-BE 시안이 있고 "바뀐 거 표시해줘 / 비교해서 annotation 달아줘"
- 변경점을 일감 문서에도 정리해야 할 때
- "/fig:diff" 명시적 호출

## When NOT to invoke

- 전체 프레임 구조만 파악 → `/fig:read`
- 구조·네이밍 정리, placeholder 채움 → `/fig:prep`
- 흐름 화살표 → `/fig:arrows`
- 규칙 위반 검증만 → `/fig:lint`
- 변경이 정본에 반영됐는지 감사·반영·이관 → `/fig:sync`

## Inputs

- `figma_url` (필수): TO-BE(또는 비교 대상) URL. 페이지·섹션·프레임 어느 것이든 가능 — 종류를 판별해 분기한다.
- `task_ref` (선택): 정리할 일감. 없으면 화면·기능명 키워드로 트래커에서 검색해 후보를 확인받는다. 그래도 없으면 트래커 단계를 건너뛴다(사용자 확인).
- `shared_category` (선택): 재사용할 공용 annotation 카테고리 이름. 생략하면 설정 `task_tracker.annotation_category`.

규칙 원천은 `figma-conventions.yaml` 이다 — `resolve-config.py --js <fileKey>` 로 `task_tracker`·`sync.pair_patterns` 를 읽는다.

`use_figma` 호출 전 항상 `figma:figma-use` 스킬을 로드한다.

## 카테고리 정책 (새로 만들지 않는다)

- annotation 카테고리를 **절대 새로 생성하지 않는다** (`addAnnotationCategoryAsync` 금지). 매 실행 생성하면 파일 카테고리 목록이 오염되고, 파일 sync 시 프리셋으로 유실·재매핑된다(실측).
- `getAnnotationCategoriesAsync()` 로 설정 `task_tracker.annotation_category` 이름을 찾는다 → 있으면 그 `categoryId` 사용, 없으면 `categoryId` 생략(무카테고리).
- **분류는 카테고리 색이 아니라 라벨 앞 `[태그]` 로 표현한다.** 태그 목록은 설정 `task_tracker.scope_tags`. 색은 유실될 수 있어 분류의 단일 출처는 라벨 텍스트다.

## Procedure

### 1. AS-IS / TO-BE 소스 확정 (추측 금지)
입력 노드 종류를 판별한다(페이지 = 여러 프레임/섹션 담긴 canvas / 섹션 / 단일 프레임).

TO-BE를 먼저 잡고, **AS-IS를 다음 순서로 찾는다**:
1. **같은 페이지의 다른 섹션**: 섹션명을 설정 `sync.pair_patterns`(fig:sync 와 공용)로 매칭한다.
2. 같은 페이지에 없으면 **다른 페이지 검색**: `figma.root.children`에서 이름에 AS/현행/current/live/before가 있거나, **같은 화면명 프레임을 가진 페이지**를 후보로 제시.
3. 그래도 불명확하면 **사용자에게 질문**(AskUserQuestion): "AS-IS가 어디 있나 — ①같은 파일 다른 페이지 ②다른 파일 ③현행 라이브(비교본 없음)". 후보가 있으면 권장안으로 제시.
4. AS-IS가 **아예 없으면**(순수 신규 화면) 비교 불가 → 변경점 대신 "신규 화면"으로 처리할지 확인.

> 여러 페이지를 읽어야 하면 `setCurrentPageAsync`는 스크립트당 1회 — 페이지별로 `use_figma` 호출을 병렬로 나눈다(figma-use 규칙).

### 1-2. 비교 구조 정비 (AS-IS가 다른 곳에 있을 때)

AS-IS가 운영 페이지 등 **다른 페이지·파일에 있으면, 작업 페이지로 복제해 와 한 화면에서 대조되게 만든다.** 리뷰어·개발이 두 페이지를 오가야 하면 비교 자체가 안 된다.

- **섹션은 AS-IS / TO-BE 두 덩어리로 나눈다.** 기능·도메인별로 쪼개지 않는다 — 비교 페이지의 분류 축은 '변경 전/후' 하나다.
- 섹션 이름은 1번의 매칭 패턴에 걸리게 짓는다 (예: `NN. AS-IS` / `NN. TO-BE`).
- **프레임 이름은 양쪽 동일하게 둔다** — 이름이 페어링 키이고 구분자는 섹션이다. 프레임에 `[AS-IS]` 같은 prefix를 붙이면 페어링이 깨진다.
- 두 섹션은 **세로로 쌓고 화면별 x를 맞춘다** — 같은 화면이 위아래로 마주봐야 대조가 쉽다.
- TO-BE에만 있는 신규 화면·상태 변형은 TO-BE 섹션에만 둔다(AS-IS 자리는 비운다).
- AS-IS는 참고본이라 **수정하지 않는다.** 정본에서 복제만 하고 원본은 건드리지 않는다.
- 같은 이름 프레임이 두 섹션에 하나씩 있는 것은 정상 — 이름 중복은 **섹션 내부에서만** 위반으로 본다(`/fig:lint` 호출 시 이 점을 명시).

### 2. 프레임 페어링
- `get_metadata`(대상 스코프)를 부른다. 응답이 커서 파일로 저장되면 Bash python/jq로 `<section>`/`<frame>` 태그만 추출(스니펫 참조).
- AS-IS ↔ TO-BE 프레임을 **이름으로 페어링**(같은 화면명끼리). 하위 섹션이 있으면 한 단계 더 내려간다.
- **크기(width/height) 차이는 변경 후보 신호** — TO-BE가 더 크면 콘텐츠 추가 확률↑.

### 3. 화면별 diff
- 페어별 **프레임 단위** 고해상 `get_screenshot`을 **병렬**로 받아 curl→로컬 저장→육안 비교(큰 캔버스 전체는 해상도가 낮아 못 읽음).
- 바뀐 요소를 목록화: 신규 컬럼/필드/섹션, 값·단위·스타일 변경, 아이콘 추가 등.
- **상태 변형은 대표 1개로 묶는다**(Default/Selected, Detail/Toast → 대표에만 핀 + "변형은 동일 변경 상속" 문구). 형제 변형 중복 표기 금지.
- 더미 데이터 값 차이(합계 숫자만 다름 등)는 **설계 변경 아님 → 제외**.

### 4. 일감 범위 대조 → 분류
- 일감이 있으면 **범위 In/Out·주요 변경 요약**을 읽어 각 변경을 분류한다. 태그는 설정 `task_tracker.scope_tags` 순서대로 — 통상 「이 일감」 / 「범위 밖」 / 「별도 예정」 세 갈래다
- 설정에 태그가 없으면 분류 없이 변경 목록만 내고, 범위 판단이 필요하면 사용자에게 묻는다
- 범위 밖 변경은 임의 판단하지 말고 **"이 일감 포함 의도인지" 확인**을 남긴다.

### 5. 앵커 노드 확보 (읽기)
- `use_figma` **읽기 스크립트**로 각 변경 요소 노드를 텍스트 내용 `findOne`(정확·부분 일치)으로 잡고 `absoluteBoundingBox` 확인.
- 인스턴스 내부 sublayer도 `node.annotations` 부착 가능. 단 `get_metadata`의 `0:xxxx` 내부 id는 `getNodeByIdAsync`로 **직접 주소지정 불가** → 텍스트 매칭으로 확보.

### 6. 미리보기 → go (Figma)
- 변경표(요소 / AS-IS / TO-BE / 분류) + **각 핀 라벨 문구** + 어느 프레임에 박을지 + 쓸 카테고리(공용명 or 무)를 제시하고 "go" 대기.

### 7. Figma 쓰기 (annotation)
- `figma-use` 로드 후 `use_figma`로: currentPage 전환 → 공용 카테고리 조회 → 각 앵커 부착. **카테고리 조회+전 핀 부착을 한 스크립트로**(중간 유실 방지). 스니펫 참조.
- **라벨 앞 `[분류]` 태그 필수** — 색이 풀려도 분류가 텍스트로 남는다.
- **되읽기 검증**: `node.annotations` 재조회로 확인. 핀은 **Dev Mode에서만** 보여 `get_screenshot`으론 확인 안 됨.

### 8. 일감 문서 미리보기 → go → 쓰기

넣을 것은 트래커와 무관하게 셋이다. ①Figma 링크(`node-id` 포함) + "Dev Mode annotation 표기, 대표 화면·상태 변형 상속" 한 줄 ②**AS-IS/TO-BE 비교표** ③**범위 콜아웃**(모두 범위 내면 `✅`, 범위 밖이 있으면 `⚠️` + TBD).

어디에 어떻게 넣느냐만 설정 `task_tracker.type` 으로 갈린다.

| type | 대상 | 방식 |
|---|---|---|
| `notion` | 일감 페이지의 `ui_section_heading` 절 (보통 비어 있는 콜아웃 뒤) | `update_content` 로 헤딩·콜아웃을 `old_str` 로 잡아 **뒤에 삽입**. 전체 교체 금지 |
| `github` | 일감 이슈 | 본문에 해당 절이 있으면 그 아래 삽입, 없으면 코멘트로 추가 |
| `none` | — | 쓰지 않는다. 비교표·콜아웃을 **응답에 마크다운으로 출력**하고 끝낸다 |

- 일감을 못 찾았으면 이 단계를 건너뛴다(사용자 확인).
- 대상 절을 **탐색**한다. `ui_section_heading` 이 없거나 이름이 다르면 어디에 넣을지 확인받는다. 이미 내용이 있으면 덮지 말고 뒤에 이어 넣는다.

### 9. 검증
- `none` 이 아니면 되읽어 표·콜아웃·링크를 확인한다.
- **한글 인코딩 오타 주의**: 직접 입력 한글이 깨질 수 있다. 깨진 글자의 코드포인트를 확인해 `\u` 이스케이프로 치환(직접 재입력하면 또 깨짐). [[reference_notion_korean_input_typos]]

### 10. 범위 확정 후속 (해당 시)
- 범위 밖 변경이 이후 "포함·제외" 로 확정되면: Figma 라벨 `[태그]`·일감 비교표 분류·범위 In 항목·범위 콜아웃(⚠️↔✅)을 **함께 갱신**한다(각각 미리보기→go).

## 라벨 문구 규칙

- 형식: `**[분류] 무엇이 바뀌었나** — 어떻게/왜(값·형식·위치).`
- 개발이 산출물만 보고 알 수 있게 구체값 포함: 위치(어디 뒤/옆), 값 형식(예: 분:초), 예외(미완료 시 표기) 등.
- 분류 태그는 설정 `task_tracker.scope_tags` 에서 고른다 — 임의로 새 태그를 만들지 않는다.

## 구현 스니펫

**섹션·프레임 추출** (저장된 metadata 파일에서):

    # f = 저장된 get_metadata 결과 파일 경로
    python3 -c "
    import json,re
    t=json.load(open('$f'))[0]['text']
    for m in re.finditer(r'<(section|frame)\s+id=\"([^\"]+)\"\s+name=\"([^\"]+)\"\s+x=\"(-?[0-9.]+)\"\s+y=\"(-?[0-9.]+)\"\s+width=\"([0-9.]+)\"\s+height=\"([0-9.]+)\"', t):
        tag,i,n,x,y,w,h=m.groups()
        if tag=='section' or float(w)>=1400: print(f'{tag} {i} w={float(w):.0f} h={float(h):.0f}  {n}')
    "

**카테고리 조회 + 전 핀 부착 + 되읽기** (한 스크립트, 정책 반영):

    // targets = [{ id, md }] — md는 "[분류]"로 시작. SHARED='Changed'
    const page = await figma.getNodeByIdAsync(PAGE_ID);
    await figma.setCurrentPageAsync(page);
    const cats = await figma.annotations.getAnnotationCategoriesAsync();
    const shared = cats.find(c => c.label === SHARED);   // 없으면 undefined → 무카테고리
    const res = [];
    for (const t of targets) {
      const n = await figma.getNodeByIdAsync(t.id);
      if (!n) { res.push({ id: t.id, ok: false }); continue; }
      const ann = { labelMarkdown: t.md };
      if (shared) ann.categoryId = shared.id;            // 있을 때만 부여, 생성 금지
      n.annotations = [ann];
      res.push({ name: n.name, cat: shared ? shared.id : null, ok: true });
    }
    return res;   // 되읽기 검증은 별도 호출로 node.annotations 재조회

## 함정

- **카테고리 생성 금지**: 새 커스텀 카테고리는 파일 sync 시 사라지고 핀이 프리셋으로 재매핑, 일부 핀은 통째로 유실됨. → 공용 재사용/무 + 라벨 태그.
- **핀은 Dev Mode 전용**: 일반 스크린샷·편집 모드엔 안 보임. 검증은 `node.annotations` 되읽기.
- **AS-IS 위치 추측 금지**: 같은 페이지에 없으면 다른 페이지 검색 or 질문.
- **비교 페이지를 기능별 섹션으로 짜지 않기**: 축이 둘(기능×전후)이 되면 대조가 불가능해진다. AS-IS/TO-BE 두 섹션이 유일한 축(1-2절).
- **대표만 표기**: 형제 상태 변형에 중복 핀 금지. 대표 + "상속" 문구.
- **metadata 대용량**: 파일로 저장되면 python/jq 파싱. `0:xxxx` 내부 id는 주소지정 불가 → 텍스트 매칭.
- **한글 인코딩 오타**: 외부 문서에 직접 입력할 때 음절이 깨질 수 있다. 삽입 후 되읽어 검증하고, 깨진 글자는 `\u` 이스케이프로 치환한다.

## Constraints

- Figma·일감 문서 각 쓰기 전 **미리보기 → "go"**(Figma 는 단계 많으면 분할). annotation 카테고리 **생성 금지**. 대상 노드 외 부모/형제 미변경. 검증 전 "완료" 단정 금지.

## 완료 조건

- TO-BE 변경 요소마다(대표 프레임 기준) `[분류]` 태그 라벨 annotation이 부착·되읽기 확인됨.
- 일감 문서의 지정 절에 Figma 링크 + 비교표 + 범위 콜아웃 삽입·되읽기 확인(한글 오타 없음). `type: none` 이거나 일감이 없으면 마크다운 출력으로 대신하고 그 사실을 명시.
- 범위 밖 변경은 플래그로 남고, 확정 시 Figma·Notion 함께 갱신.
