Skip to main content

config-menu-offers-options-nothing-implements

사용자가 고르는 선택지 목록(enum·레지스트리·카탈로그·allowlist)을 폼/검증기/LLM 프롬프트가 "고를 수 있는 메뉴"로 읽을 때, 그 목록의 항목이 **실제로 구현돼 있는지는 아무도 검사하지 않는다.** 사용자가 유령 항목을 고르면 에러도 없이 그냥 아무 일도 안 일어난다 — 설정은 저장되고, 화면에도 보이고, 영원히 발화하지 않는다. 트리거 - 설정 드롭다운/룰 작성/라우팅 대상/웹훅 이벤트/기능 플래그 목록, "설정했는데 왜 동작 안 하지", 스펙만 있고 호출자가 없는 상수, 카탈로그를 LLM 프롬프트에 메뉴로 주입.

Zur Installation springen

Quellinformationen

Repository
blas1n/claude-skills
Letzte Quellaktivität
17. August 2026 um 18:12
Erkannte Sprache von SKILL.md
Koreanisch
Sterne
2
Forks
0

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
config-menu-offers-options-nothing-implements
description
사용자가 고르는 선택지 목록(enum·레지스트리·카탈로그·allowlist)을 폼/검증기/LLM 프롬프트가 "고를 수 있는 메뉴"로 읽을 때, 그 목록의 항목이 **실제로 구현돼 있는지는 아무도 검사하지 않는다.** 사용자가 유령 항목을 고르면 에러도 없이 그냥 아무 일도 안 일어난다 — 설정은 저장되고, 화면에도 보이고, 영원히 발화하지 않는다. 트리거 - 설정 드롭다운/룰 작성/라우팅 대상/웹훅 이벤트/기능 플래그 목록, "설정했는데 왜 동작 안 하지", 스펙만 있고 호출자가 없는 상수, 카탈로그를 LLM 프롬프트에 메뉴로 주입.
# 메뉴에 있는데 주방에 없다 ## Problem BSVibe #766. 형님이 라우팅 룰을 하나 썼다: ``` design (plan) → opus caller_id = workflow.agent_loop.plan ``` **두 달간 조용히 아무 일도 안 했다.** 에러 없음, 경고 없음, 로그 없음. 원인은 룰이 아니라 **레지스트리**였다. `caller_registry` 는 자기 문서에 계약을 적어두고 있었다: > A *caller* is any code site that **invokes an LLM** through the dispatch mechanism. 그런데 실제 목록에는 두 종류가 섞여 있었다: 1. **설정할 줄 아는** 항목 — 스펙·타임아웃·required_methods 가 다 적힌 것 2. **런타임이 진짜 호출하는** 항목 그리고 **사용자 입력 표면 셋 다** (1)을 (2)의 증거로 취급했다: | 표면 | 하는 일 | |---|---| | REST 검증기 | `if value in KNOWN_CALLERS: return value` | | NL 컴파일러 | 같은 목록을 **LLM 프롬프트에 "고를 수 있는 메뉴"로 주입** | | PWA 룰 폼 | `/callers` 응답을 드롭다운으로 렌더 | ∴ **목록 등재 = 사용자에게 하는 약속** — *"이걸 고르면 발화한다."* **10개 중 2개가 그 약속을 어기고 있었다.** ## 왜 아무도 못 잡는가 - **타입 체커**: 상수는 존재한다. 문자열도 유효하다. mypy 는 만족한다. - **유닛테스트**: 오히려 **유령 항목을 샘플로 쓴다.** 이번 경우 룰 작성 테스트 7개 파일 33곳이 `workflow.agent_loop.plan` 을 "유효한 caller 예시"로 사용했다 — 즉 테스트가 그 유령을 **정상이라고 증언하고 있었다.** - **검증기**: 목록 대조는 통과한다. 목록이 거짓말인 게 문제다. - **런타임**: 매칭이 안 될 뿐이므로 **예외가 안 난다.** fail-silent 가 기본값이다. - **git blame**: 이 항목을 건드린 커밋 3개가 전부 **레지스트리 내부**였다(선언·타임아웃·플래그). **배선 커밋은 존재한 적이 없다.** 아무도 "이거 누가 부르지?"를 묻지 않았다. ## Solution ### 1. 고아를 센다 (grep 말고 AST) 주석·docstring 언급이 배선으로 오인되면 안 된다 — *설명은 있는데 호출이 없는 것*이 정확히 이 결함의 모양이다. ```python def _dispatched() -> set[str]: """실제로 ``caller_id=`` 인자로 넘어가는 값만.""" dispatched = set() for path in BACKEND.rglob("*.py"): if path == REGISTRY: # 선언 파일 자신은 제외 continue for node in ast.walk(ast.parse(path.read_text())): if isinstance(node, ast.Call): for kw in node.keywords: if kw.arg == "caller_id": ... # 리터럴이면 값, Name 이면 상수 테이블에서 해석 return dispatched def test_every_declared_option_is_implemented(): orphans = sorted(set(KNOWN_CALLERS) - _dispatched()) assert not orphans, f"{orphans} 는 메뉴에 있는데 아무도 호출하지 않는다" ``` ### 2. ⚠️ 양성 대조를 반드시 같이 넣어라 스캐너가 망가지면 "고아 없음"으로 **조용히 통과**한다 — 게이트가 스스로 무장해제된다. ```python def test_the_scanner_actually_finds_wiring(): d = _dispatched() assert "workflow.agent_loop.act" in d # 확실히 배선된 것 assert len(d) >= 5, f"스캐너가 {d} 만 찾음 — 레지스트리가 아니라 스캐너가 고장" ``` ### 3. 면제 목록을 만들지 마라 `ALLOWED_ORPHANS = {...}` 는 **이 테스트가 금지하려는 상태 그 자체**다. 선택지는 둘뿐이다 — **배선하거나, 지우거나.** ### 4. 고아마다 둘 중 하나로 판정 | 상태 | 처방 | |---|---| | 스펙·설명·튜닝값이 **특정 호출 지점을 지목**한다 | **배선하라.** 없는 기능을 만드는 게 아니라 끊긴 링크를 잇는 것 | | 그 개념이 아키텍처에서 **사라졌다** | **지워라.** 죽은 코드 | BSVibe 실제 판정: `knowledge.query` 는 설명이 *"frame 이 knowledge_only 로 분류했을 때"* 라고 그 자리를 정확히 지목하고 90 초 타임아웃까지 *"사용자가 기다린다"* 로 튜닝돼 있었다 → **배선**. (그 경로는 엉뚱하게 300 초짜리 `frame` 을 쓰고 있었다 — **유령 항목이 진짜 성능 결함을 숨기고 있었다.**) `agent_loop.plan` 은 plan 턴 자체가 아키텍처에서 사라짐 → **삭제**. ### 5. 지울 때 딸려오는 것들을 세라 사용자 표면에 노출된 목록이므로 **번역 키·라벨 맵·샘플 픽스처**가 같이 산다: ``` backend 레지스트리 상수 + 스펙 + __all__ + 모듈 docstring tests "유효한 예시"로 쓰인 곳 (BSVibe: 7파일 33곳) frontend 라벨 맵 + i18n 메시지 키 (locale 마다!) + 컴포넌트 테스트 ``` ## Key Insights - **사용자가 고를 수 있는 목록은 계약이다.** "설정 가능하다"와 "동작한다"는 다른 명제인데, 폼·검증기·프롬프트는 전자를 후자로 읽는다. 그 간극을 검사하는 코드는 대개 **존재하지 않는다.** - **fail-silent 가 기본값이라 사용자만 피해를 본다.** 매칭이 안 되면 그냥 매칭이 안 될 뿐이다. 시스템은 건강하고, 설정은 저장돼 있고, 화면에도 보인다. **틀렸다는 신호가 어디에도 없다.** - **유닛테스트가 유령의 알리바이가 된다.** 유효한 샘플이 필요하니 목록에서 하나 집어오는데, 하필 고아를 집으면 그때부터 테스트 스위트가 그 고아를 정상이라고 증언한다. - **"이 상수를 누가 부르지?"를 선언 시점에 한 번만 물었어도** 두 달을 아꼈다. `git log -S <상수>` 로 배선 커밋이 있었는지 보면 5초다. ## Red Flags - 상수/스펙은 풍부한데(타임아웃·설명·플래그) **호출자를 못 찾겠다** - 사용자가 *"설정했는데 아무 일도 안 일어난다"* 고 한다 — 에러 없이 - 카탈로그를 **LLM 프롬프트에 메뉴로 주입**한다(모델이 유령을 고르면 그대로 저장된다) - 목록 항목이 **테스트에서만** 쓰인다 - `git log -S <상수>` 결과가 **선언 파일 내부 커밋뿐**이다 - 스펙 설명이 특정 호출 지점을 **말로 지목**하는데 그 지점 코드엔 그 상수가 없다
Auf GitHub ansehen