카탈로그 제보 인테이크 (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를 따른다. 특히 3절 차단 우회 금지와 4절 인증/유료 게이트 뒤 리소스 제외는 이 스킬의 URL 검증 단계에도 그대로 적용된다. 접근이 막혀 확인이 안 되는 항목은 우회하지 말고 gap으로 남긴다.
입력
- 필수: Google Forms 응답을 내보낸 CSV 경로 (Sheets → 파일 → 다운로드 → CSV).
- 참고 문서: references/field-mapping.md — 필드 매핑·Category· gap 정의·설치기/스위치 시드표. 처리 규칙의 근거는 전부 여기에 있다.
사전 준비 (도구)
- uv (Python) —
scripts/*.py실행. PEP 723 자체완결이라uv run시 의존성 자동 설치. - ussfc — 무인 설치 스위치 탐지(.NET 전역 도구). 최초 1회 설치:
설치 없이 쓰려면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등 검증 도구.
파이프라인
[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한 베이스 위에서 편집하면 이미 반영된 항목을 중복 추가하거나 충돌 검사가 어긋난다.
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 정규화 (오프라인)
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 3~5절을 근거로:
arguments-missing+package-name-derive— 각package_urls에 대해:- 생존/최종 URL/파일명 확인:
uv run .claude/skills/catalog-intake/scripts/check_urls.py <url> [<url> ...] --jsoncheck_urls가 403/차단이면 JS 게이트일 수 있음 → 무거운 케이스이므로dotnet run --file src/checksites.cs -- probe ...(Playwright Edge)로 폴백. - 무인 설치 스위치 자동 추론 — 설치 파일을 브라우저(Edge)로 위장해 내려받아
ussfc로 판별한다(사전 준비 절 참고):
출력의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)로 확정할 것을 사람에게 요청.
- 어느 쪽이든 스위치는 자동 확정이 아니라 후보다 — 최종은 실제 설치 테스트로 사람이 확인.
- 시드표 매칭(source: seed): 알려진 벤더 → 시드표 값을 채택.
- 생존/최종 URL/파일명 확인:
로고 → 투명 정사각 PNG — CSV의
icon_ref는 인증이 필요한 Drive 링크이므로 보통 사이트에서 직접 만든다: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. 검증
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 후 사라진다):
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 전역 도구)가 필요하다 — 사전 준비 절 참고.