# Catalog Intake

> Google Forms(식탁보 사이트 추가/수정/삭제 요청) 응답 CSV를 읽어 docs/Catalog.xml 등록을 돕는다. 헤더를 정규화하고, 브라우저(Edge)로 위장해 보안 플러그인 URL 생존/파일명을 확인하고, 공식 로고를 투명 정사각 PNG로 만들고, catalogutil.cs 검증을 거쳐 사람이 검토할 PR 후보를 만든다. "제보 CSV 처리", "폼 응답 등록", "catalog intake" 요청 시 사용.

- Skill: `yourtablecloth/catalog-intake` (Agent Skill, multi-file: 6 files)
- Install (CLI): `npx skillmds@latest add yourtablecloth/catalog-intake`
- Raw SKILL.md: https://api.skillmd.com/api/skills/yourtablecloth/catalog-intake/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Data & Analytics
- Author: yourtablecloth (https://skillmd.com/u/yourtablecloth)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/yourtablecloth/catalog-intake

---


# 카탈로그 제보 인테이크 (catalog-intake)

Google Forms 제보(비-GitHub 사용자용)를 `docs/Catalog.xml` 변경으로 옮기는 반자동 절차.
`docs/SITE_HEALTH_WORKFLOW.md`와 같은 철학을 따른다: **기계는 정규화·검증·후보 생성까지,
최종 판정과 반영(merge)은 사람.**

## ⚠️ 안전 경계선 (먼저 읽기)

- 이 카탈로그의 `<Package>`는 **사일런트 스위치로 무인 설치되는 보안 프로그램**이다.
  제보는 익명은 아니지만(폼 로그인 필수) **미검증 외부 입력**이다.
- 따라서 이 스킬은 **PR 후보까지만** 만든다. **자동 commit/merge 금지.** 사람이 검토 후 반영.
- `check_urls.py`·`fetch_logo.py`는 URL을 **읽기만** 한다. 설치 파일을 실행하지 않는다.
- 데이터 수집 원칙은 [docs/DATA_BOUNDARY.md](../../../docs/DATA_BOUNDARY.md)를 따른다.
  특히 **3절 차단 우회 금지**와 **4절 인증/유료 게이트 뒤 리소스 제외**는 이 스킬의 URL 검증
  단계에도 그대로 적용된다. 접근이 막혀 확인이 안 되는 항목은 우회하지 말고 gap으로 남긴다.

## 입력

- 필수: Google Forms 응답을 내보낸 **CSV 경로** (Sheets → 파일 → 다운로드 → CSV).
- 참고 문서: [references/field-mapping.md](references/field-mapping.md) — 필드 매핑·Category·
  gap 정의·설치기/스위치 시드표. 처리 규칙의 근거는 전부 여기에 있다.

## 사전 준비 (도구)

- **uv** (Python) — `scripts/*.py` 실행. PEP 723 자체완결이라 `uv run` 시 의존성 자동 설치.
- **[ussfc](https://www.nuget.org/packages/ussfc)** — 무인 설치 스위치 탐지(.NET 전역 도구). 최초 1회 설치:
  ```bash
  dotnet tool install -g ussfc      # 갱신: dotnet tool update -g ussfc
  ```
  설치 없이 쓰려면 `.NET 10 SDK`의 `dnx ussfc`도 가능(`detect_switch.py`가 자동 폴백).
- **gh** (GitHub CLI) — PR/이슈 생성. **dotnet 10 SDK** — `catalogutil.cs` 등 검증 도구.

## 파이프라인

```text
[0] 최신화 (필수)            main pull → 작업 브랜치 생성  ← 스킬 시작 전 반드시
      │
[1] normalize   (오프라인)   CSV → 정규화 JSON + gap 플래그
      │
[2] 게이트 확인               consent/dedup 누락 건은 보류 표시
      │
[3] 항목별 처리              빈칸 4종 채우기(아래 규칙) — URL확인/로고/스위치/분류
      │
[4] 편집                     Catalog.xml + 이미지 배치 (변경 유형별 commit 분리)
      │
[5] 검증                     catalogutil.cs 스키마 검증 + (선택) checksites.cs probe
      │
[6] PR + 미완 이슈           완전반영은 PR diff / 미완은 추적 이슈 생성 후 PR에 링크. merge는 사람.
```

### 0. 최신화 — 스킬 실행 전 반드시

제보를 처리하기 전에 **로컬 main을 원격 최신으로 맞추고 새 작업 브랜치에서 시작**한다.
stale한 베이스 위에서 편집하면 이미 반영된 항목을 중복 추가하거나 충돌 검사가 어긋난다.

```bash
git switch main
git pull --ff-only origin main          # 최신 카탈로그 확보 (fast-forward만)
git switch -c catalog-intake/<날짜-또는-배치명>   # 예: catalog-intake/2026-07-forms
```

- `--ff-only`가 실패(로컬에 이탈 커밋 존재)하면 사람에게 알리고 멈춘다 — 임의 merge/rebase 금지.
- 이 최신 main 기준으로 [1] normalize의 `--catalog docs/Catalog.xml` 충돌 검사가 정확해진다.

### 1. normalize — CSV 정규화 (오프라인)

```bash
uv run .claude/skills/catalog-intake/scripts/normalize_csv.py \
  <responses.csv> --catalog docs/Catalog.xml --out intake.json --pretty
```

각 레코드에 `change_type`, `display_name_ko/en`, `url`, `category`, `id_suggested`,
`id_collision`, `package_urls[]`, `gaps[]`가 채워진다. **요약의 "매칭 안 된 CSV 열"과
`gaps`를 먼저 확인**하고 처리 계획을 세운다.

### 2. 게이트 확인

`consent-missing` 또는 `dedup-missing`이 있으면 스팸/오제출 의심 → 그 건은 **처리 보류**로
표시하고 사람에게 확인을 요청한다. 임의로 진행하지 않는다.

### 3. 항목별 처리 — 빈칸 4종 채우기

[references/field-mapping.md](references/field-mapping.md) 3~5절을 근거로:

- **`arguments-missing` + `package-name-derive`** — 각 `package_urls`에 대해:
  1. 생존/최종 URL/파일명 확인:
     ```bash
     uv run .claude/skills/catalog-intake/scripts/check_urls.py <url> [<url> ...] --json
     ```
     `check_urls`가 403/차단이면 JS 게이트일 수 있음 → 무거운 케이스이므로
     `dotnet run --file src/checksites.cs -- probe ...`(Playwright Edge)로 폴백.
  2. **무인 설치 스위치 자동 추론** — 설치 파일을 브라우저(Edge)로 위장해 내려받아
     `ussfc`로 판별한다(사전 준비 절 참고):
     ```bash
     uv run .claude/skills/catalog-intake/scripts/detect_switch.py <installer-url>
     ```
     출력의 `▶ 후보`가 `Package Name`/`Arguments` 제안이다. 판정 규칙:
     - **시드표 매칭(source: seed)**: 알려진 벤더 → 시드표 값을 채택. `ussf`가 달라도(conflict)
       시드표 우선(국내 보안 설치기는 ussf의 일반 판별이 틀리기 쉬움. TouchEn=`/silence`, IPInside=`/nodlg`).
     - **시드표에 없음(source: ussf, needs_verification)**: ussf 추정값을 넣되, **넘겨짚지 말고**
       실제 무인 설치 테스트(Windows Sandbox/VM)로 확정할 것을 사람에게 요청.
     - 어느 쪽이든 스위치는 **자동 확정이 아니라 후보**다 — 최종은 실제 설치 테스트로 사람이 확인.

- **로고 → 투명 정사각 PNG** — CSV의 `icon_ref`는 인증이 필요한 Drive 링크이므로 보통
  사이트에서 직접 만든다:
  ```bash
  uv run .claude/skills/catalog-intake/scripts/fetch_logo.py \
    <대표 URL> docs/images/<Category>/<Id>.png --size 256 --bg auto
  # 이미 로컬로 받은 아이콘이 있으면:
  uv run .claude/skills/catalog-intake/scripts/fetch_logo.py \
    --from-image <path> docs/images/<Category>/<Id>.png --bg auto
  ```
  플랫 로고가 아니거나 배경이 복잡하면 `--bg rembg`(무거움: `uv run --with rembg …`).
  결과가 어색하면 사람에게 공식 로고 교체를 권한다.

- **`category-needs-decision`(증권 등)** — 자동 결정 금지. Other 잠정값 그대로 두고
  사람에게 "Other로 둘지 / XSD에 새 enum 추가할지" 결정을 요청.

- **`id-collision`** — 이름을 조정하거나, 사실상 기존 항목 수정이면 `change_type`을
  modify로 재해석. 새 Id를 임의로 만들지 말고 사람과 합의.

### 4. 편집 (변경 유형별 commit 분리)

`references/field-mapping.md` 1절 매핑대로 `docs/Catalog.xml`(또는 `docs/sites.xml`)을
편집하고 이미지를 배치한다. commit은 의미 단위로 분리:
`(1) 신규 추가  (2) 수정  (3) 삭제`.

### 5. 검증

```bash
dotnet run --file src/catalogutil.cs -- ./docs/ ./outputs/     # 스키마/리소스 검증
dotnet run --file src/checkimages.cs -- ./docs/Catalog.xml ./docs/images   # 이미지 존재 검증
# 새로 추가/수정한 Id만 실제 접속 재검증(선택):
dotnet run --file src/checksites.cs -- probe ./docs/ ./health-report/ --only <Id1,Id2>
```
Error/Warning이 남지 않을 때까지 정리한다.

### 6. PR 후보 제출

**모든 제보 항목은 아래 3가지 중 하나로만 분류한다. "수동 확인 필요"를 이유로 조용히
드롭하지 않는다 — 드롭하면 그 제보는 유실된다.**

| 결과 | 처리 | PR에서 |
|---|---|---|
| **완전 반영** | 검증까지 끝나 Catalog.xml에 반영 | diff에 포함 |
| **미완(수동 검토 필요)** | 자동으로 확정 못 한 항목 | **제외 금지.** PR 본문에 남기고 + **추적 이슈를 반드시 생성해 링크** |
| **중복/무효로 제외** | 기존 항목과 중복이거나 정크인 것 | 본문에 사유만 기록(진짜 제외) |

- 미완과 제외는 **다르다**: "확정 못 함"(미완)을 "제외"로 처리하면 안 된다. 제외는 중복·정크에만.

**미완 항목마다 추적 이슈를 생성한다**(유실 방지의 핵심 — PR 본문만으론 merge 후 사라진다):

```bash
gh issue create \
  --title "[제보 미완] <이름> (<도메인>) — <막힌 지점 요약>" \
  --body-file <이슈본문.md> \
  --label "help wanted" --label "enhancement"
# 이슈 본문에 담을 것: 제보 요약 / 자동으로 확인한 것 / 막힌 지점 / 사람이 해야 할 일 / 관련 PR #<n>
```

- 이슈 본문은 **자기완결**로(site-health `issue` 단계와 동일 철학) — 후속 처리에 필요한 컨텍스트를 모두 담아 PR/대화 없이도 처리 가능하게.
- 생성한 이슈 번호를 PR 본문의 해당 미완 항목 옆에 `추적: #<n>`으로 링크한다.
- PR 본문 구성: 변경 요약 · 완전 반영 목록 · **미완(수동 검토 필요) + 추적 이슈 링크** · 중복 제외 목록 · 검증 결과.
- 완전 반영 항목이 기존 제보 이슈를 닫아야 하면 `Closes #<n>`로 연결.
- **여기서 멈춘다.** merge는 사람이 한다.

## 자동 vs 사람 승인 경계 (site-health와 동일)

| 작업 | 자동 | 사람 승인 |
|---|:---:|:---:|
| CSV 정규화 / URL 생존 확인 / 로고 생성 | ✓ | |
| 같은 도메인 내 URL 한 줄 갱신 | ✓ | |
| 새 `Service` 추가 · Id 신설 | | ✓ |
| `Service` 삭제 | | ✓ |
| 증권 등 Category 결정 · XSD 변경 | | ✓ |
| 사일런트 스위치가 시드표에 없어 조사한 값 | | ✓ |
| 게이트(consent/dedup) 누락 건 반영 | | ✓ |
| commit / merge | | ✓ |

## 스크립트 요약

| 스크립트 | 런타임 | 역할 | 네트워크 |
|---|---|---|:---:|
| `normalize_csv.py` | uv(무의존) | CSV→정규화 JSON·gap 플래그·Id 파생·충돌검사 | ✗ |
| `check_urls.py` | uv(httpx) | Edge 위장 URL 생존/파일명 확인 | ✓ |
| `detect_switch.py` | uv(httpx) + ussfc | Edge 위장 설치기 다운로드→ussfc 스위치 탐지→시드 교차검증 | ✓ |
| `fetch_logo.py` | uv(pillow/numpy/bs4) | 로고 수집→배경 투명화→정사각 PNG | ✓* |

`*` `--from-image`는 네트워크 불필요. UA 프로파일은 `src/checksites.cs`의 Edge 131과 동일.
`detect_switch.py`는 `ussfc`(dotnet 전역 도구)가 필요하다 — 사전 준비 절 참고.

