#!/bin/sh
# Claude Code Skill 설치 스크립트
# 생성: 2026-08-13 · Skill 1개 · 파일 1개
#
#   ./install-skills.sh            ~/.claude/skills/ 에 설치 (모든 프로젝트에서 사용)
#   ./install-skills.sh ./myrepo   ./myrepo/.claude/skills/ 에 설치 (그 저장소 전용)
set -e

DEST="${1:+$1/.claude/skills}"
DEST="${DEST:-$HOME/.claude/skills}"
mkdir -p "$DEST"
echo "설치 위치: $DEST"


mkdir -p "$DEST/paper-merge-provenance"
cat > "$DEST/paper-merge-provenance/SKILL.md" <<'SKILL_PAYLOAD_EOF'
---
name: paper-merge-provenance
description: 여러 서지 소스(WoS·PubMed·OpenAlex)를 하나의 코퍼스로 병합하고, 각 필드가 어느 소스에서 왔는지 계보를 남긴다. 실측값이 추정값을 덮는 우선순위, 저자 매칭을 인덱스로 하면 안 되는 이유, 수치를 손으로 적지 않는 계보 산출. TRIGGER - "서지 병합", "WoS PubMed 합치기", "교신저자 보정", "출처 추적", "provenance", 소스가 여럿이라 어느 값이 맞는지 정해야 할 때.
---

# 서지 소스 병합 + 출처 추적

## 언제 쓰나

같은 논문이 WoS·PubMed·OpenAlex에 다 있는데 **값이 서로 다를 때.** 어느 값을 남길지 규칙이 필요하고, 나중에 "이 교신저자는 어디서 온 값이냐"에 답할 수 있어야 한다.

## 정본 코드

```
~/Documents/혜연/BTC_KOL_PoC/
  merge_wos.py         351줄  WoS 반출 → PubMed 스키마로 변환 + 병합
  merge_openalex.py    211줄  교신저자·ORCID·피인용 보강
  make_provenance.py   149줄  계보 수치 산출
```

## 원칙 1 — 실측이 추정을 덮는다

> 핵심: WoS의 RP(교신저자)는 추정이 아니라 실측이다. PubMed 휴리스틱을 덮어쓴다.
> C3(Clarivate 정규화 기관명)이 있으면 기관명 파싱도 필요 없다.

PubMed에는 교신저자 표시가 없어서 보통 **마지막 저자로 추정**한다. WoS의 `RP` 필드는 실제 표기다. 소스마다 어느 필드가 실측이고 어느 게 추정인지 정리해두고, **실측이 있으면 무조건 그것을 쓴다.**

| 필드 | 실측 소스 | 추정 폴백 |
|---|---|---|
| 교신저자 | WoS `RP`, OpenAlex `is_corresponding` | 마지막 저자 |
| 기관명 | WoS `C3`(Clarivate 정규화) | 주소 문자열 파싱 |
| ORCID | WoS `OI`, OpenAlex | 없음 |

## 원칙 2 — 저자 매칭을 인덱스로 하지 마라

`merge_openalex.py`에 박혀 있는 교훈:

> **저자 매칭을 인덱스로 하지 않는다.** OpenAlex의 저자 순서가 PubMed·WoS와 어긋나는 경우가 있어, 인덱스로 교신 표시를 옮기면 엉뚱한 사람이 교신저자가 된다 — 지금의 '마지막 저자 추정'보다 나쁜 오류다.

**성(姓) + 이름 첫 글자**로 맞추고, 후보가 둘 이상이면 붙이지 않는다. 잘못 붙이느니 비워두는 게 낫다.

보강은 **비어 있을 때만** 한다(`Authors[].orcid`, `Times_Cited`). 이미 실측이 있는 자리를 덮지 않는다.

## 원칙 3 — 종류가 다른 건 분리한다

```
out/<topic>_merged_papers.json   논문 (학회초록 제외)
out/<topic>_conference.json      학회 발표 (Meeting Abstract) — 별도 지표
```

학회초록을 논문과 섞으면 건수·피인용이 왜곡된다. 버리지도 않는다 — 별도 지표다.

## 제목 매칭

`title_ratio()` 는 `difflib.SequenceMatcher` 를 쓴다.

```python
# ponytail: AML 프로젝트는 thefuzz를 썼지만 stdlib difflib로 같은 일을 한다.
```

의존성을 추가할 이유가 없었다. 제목 비교 정도는 표준 라이브러리로 충분하다.

이름 처리는 `split_names` · `initials` · `name_key(last, fore)` 로 키를 만들어 비교한다. 주소는 `parse_c1`(저자-소속 블록) → `match_affiliation(addr, c3_list)`(토큰 겹침으로 C3와 연결).

## 계보(provenance) — 수치를 손으로 적지 마라

> 수치를 손으로 적지 않는다. 원천 파일(`raw/wos/*.xls`)과 산출물(`out/*.json`)에서 직접 세므로, 데이터가 바뀌면 페이지도 따라 바뀐다.

문서에 "총 2,583건"이라고 타이핑하는 순간 그 숫자는 썩기 시작한다. 세는 코드를 두고 페이지가 그 결과를 읽게 한다.

**손으로 적어야 하는 것도 있다 — 그때는 근거를 함께 적는다.**

```python
# 정밀 검색식 결손 실측 (search_queries.md 에 근거가 적혀 있다).
# 세 번의 독립 측정이며 재현하려면 그때의 원본 반출본이 필요하므로 값을 적어 둔다.
RECALL = [
    {"k": "WoS 정밀식이 버린 1,045건 → 담도암", "n": 47},
    ...
]
```

재현에 원본 반출본이 필요한 일회성 측정은 코드로 다시 셀 수 없다. 그럴 때만 값을 박고 **왜 박았는지와 근거 문서 위치**를 같이 남긴다.

## 함정

- 파이프라인을 다시 돌렸으면 `export_dashboard.py` 다음에 `make_provenance.py` 도 돌려야 계보 수치가 맞는다. 순서를 문서에 적어둬라.
- `--dry-run` 을 먼저 만든다. 병합은 되돌리기 어려우니 "몇 건이 바뀌는지"부터 본다.
- `--selfcheck` 가 각 스크립트에 들어 있다. 규칙을 고치면 이걸 먼저 돌린다.

## 연관 skill

`[[paper-source-collect]]`(앞 단계), `[[biblio-normalize]]`(뒤 단계), `[[excel-case-validator]]`
SKILL_PAYLOAD_EOF

echo ""
echo "완료 — Skill 1개를 설치했습니다."
echo "Claude Code를 다시 시작하면 인식됩니다."
