Skip to main content

reaper-inherits-the-tools-refusal

자동 정리기(리퍼·GC·워크스페이스 회수)를 만들 때, "지워도 안전한가"를 직접 판정하지 말고 도구가 이미 하는 거부를 물려받아라. `git worktree remove`·`rmdir`·`docker rm`(force 없이)은 지우면 안 되는 상태에서 스스로 거부한다. `--force` 한 글자가 그 안전장치를 데이터 손실로 바꾼다. 그리고 정리 명령의 exit 0 은 반드시 관측이어야 한다. 트리거: 워크트리/워크스페이스/컨테이너/temp 회수, 고아 정리, 디스크 누수, "언제 지워도 되나" 판단 로직 작성.

الانتقال إلى التثبيت

معلومات المصدر

المستودع
blas1n/claude-skills
آخر نشاط في المصدر
١١ أغسطس ٢٠٢٦ في ٠٣:٤٩
لغة SKILL.md المكتشفة
الكورية
النجوم
٢
التفرعات
٠

خيارات التثبيت

يُحدَّد Prompt الذي يراجع المصدر أولًا بشكل افتراضي. يمكنك التبديل إلى أمر مباشر أو تنزيل نسخة محلية.

مراجعة ملفات المصدر

اقرأ SKILL.md وأي ملفات مرافقة يعرضها SkillsMP قبل أن تقرر التثبيت.

عرض SKILL.md

SKILL.md
تعليمات المصدر · معاينة للقراءة فقط
name
reaper-inherits-the-tools-refusal
description
자동 정리기(리퍼·GC·워크스페이스 회수)를 만들 때, "지워도 안전한가"를 직접 판정하지 말고 도구가 이미 하는 거부를 물려받아라. `git worktree remove`·`rmdir`·`docker rm`(force 없이)은 지우면 안 되는 상태에서 스스로 거부한다. `--force` 한 글자가 그 안전장치를 데이터 손실로 바꾼다. 그리고 정리 명령의 exit 0 은 반드시 관측이어야 한다. 트리거: 워크트리/워크스페이스/컨테이너/temp 회수, 고아 정리, 디스크 누수, "언제 지워도 되나" 판단 로직 작성.
# 리퍼는 도구의 거부를 물려받아라 — 안전 판정을 다시 구현하지 마라 ## Problem 런/작업마다 리소스(워크트리·워크스페이스·컨테이너)를 만들어 놓고 아무도 안 지우면 디스크가 샌다. 그래서 리퍼를 짠다. 자연스러운 첫 설계는 이렇다: ```python # ❌ 안전 판정을 직접 구현 if is_clean(path) and run_is_terminal(run) and pushed(branch): force_remove(path) # 판정을 통과했으니 강제로 지운다 ``` - **증상 A (손실)**: 판정이 틀린 한 경우에서 **그곳에만 존재하는 작업**이 사라진다. 취소된 런의 몇 시간짜리 산출물, 아직 커밋 안 된 편집. 리퍼가 없느니만 못한 상태가 된다. - **증상 B (조용한 누수)**: 지웠다고 보고하는데 안 지워진다. `git worktree remove` 는 등록되지 않은 경로에 대해 실패하고, 어떤 경로에서는 **exit 0 을 내면서 아무것도 안 한다**. exit 0 을 "회수했다"로 읽으면 누수가 **수개월간 성공으로 보고되며** 지속된다. - **흔한 오해**: "`--force` 를 안 쓰면 정리가 자주 실패해서 리퍼가 쓸모없어질 것이다." → **실측하면 아니다.** 아래 표를 보면 정리를 막는 것은 정확히 막아야 할 것뿐이다. ## Solution ### 1. 도구의 거부가 곧 안전 술어다 — `--force` 를 쓰지 마라 `git worktree remove` 를 실 git 2.52 에 대고 재본 결과: | 트리 상태 | force 없는 `remove` | |---|---| | 깨끗함 | 삭제, exit 0 | | **untracked 파일 있음** | **거부** exit 128, 디렉터리 유지 | | **tracked 수정 있음** | **거부** exit 128, 디렉터리 유지 | | ignored 만(`.venv`, `node_modules`) | **삭제**, exit 0 | | 디렉터리가 이미 없음 | exit 0 (등록만 해제) | | 등록 안 된 디렉터리 | `not a working tree` exit 128 | | 브랜치·커밋 | **건드리지 않는다** — push 안 된 커밋도 살아남는다 | 읽는 법: - **거부하는 것 = 그곳에만 존재하는 작업을 가진 트리.** 정확히 지키고 싶은 것. - **거부하지 않는 것 = 찌꺼기.** `.venv`/`node_modules` 가 회수를 막았다면 뭐라도 설치한 런은 전부 체크아웃을 영구 고정했을 것이다. 즉 force 없이도 리퍼는 실질적으로 동작한다. - **커밋된 작업은 위험하지 않다.** remove 가 브랜치도 오브젝트도 안 건드리므로, **push 가 실패한 런의 작업조차** 브랜치에 남는다 → 회수 조건을 "push 성공"까지 좁힐 필요 없다. ### 2. exit 0 은 관측이어야 한다 ```sh git -C "$REPO" worktree prune # 손삭제가 남긴 stale 등록 정리 if [ ! -d "$PATH" ]; then exit 0; fi # 회수할 게 없는 것은 실패가 아니다 if git -C "$REPO" worktree remove "$PATH"; then [ ! -d "$PATH" ] || exit 3 # ← #665 의 교훈: 진짜 없어졌는지 본다 exit 0 fi # 거부당했다 — 왜인지를 도구 자신의 용어로 되묻는다 (메시지 텍스트 매칭 금지: 버전·로케일) if [ -n "$(git -C "$PATH" status --porcelain 2>/dev/null)" ]; then exit 2; fi exit 3 ``` ### 3. "작업이 있어 남김"은 실패가 아니라 자기 이름을 가진 결과다 exit 코드를 셋으로 나눈다: `0`=회수됨 / `2`=보류(작업이 있어 남김) / `3`=회수 실패(관측 불가). `2` 를 `3` 에 뭉뚱그리면 **"사용자가 찾아야 할 작업이 저기 있다"**는 사실이 로그에서 사라진다. "아무 문제 없음"과 "관측 자체가 실패함"은 다른 사실이다. ### 4. 회수 지점은 `finally` — 모든 종료 경로가 지나는 seam 정상 종료 경로에 달면 **정작 잔해를 남기는 나쁘게 끝난 실행**에서 아무것도 회수하지 못한다. ```python try: return await drive_loop(...) finally: await manager.release(...) # proved·refuted·취소·크래시 전부 여기를 지난다 ``` ⚠️ 그 자리에서 **절대 raise 하지 마라.** `finally` 안의 예외는 원래 결과를 덮어써서, 디렉터리 하나 때문에 성공한 작업이 크래시로 보고된다. 전부 기록하고 삼킨다. ## Key Insights - **안전 판정을 다시 구현하는 것 자체가 결함원이다.** 도구는 이미 그 판정을 갖고 있고, 우리 판정은 도구의 것과 드리프트한다. `--force` 는 "내 판정이 도구 것보다 낫다"는 주장이다. - **거부 조건은 문서가 아니라 실 도구로 재라.** "ignored 파일이 remove 를 막는가"는 직관으로 갈리는데 답이 설계를 결정한다(막는다면 force 없는 리퍼는 무용지물). 5분짜리 scratch 레포 실험이 설계 판단을 확정시킨다. - **회수와 보존이 같은 명령의 두 결과다.** 리퍼를 "지우는 것"으로 보면 force 로 가고, "돌려받으려 시도하는 것"으로 보면 거부가 자연스러운 결과가 된다. - 다음에 먼저 확인할 것: ①이 도구는 force 없이 무엇을 거부하는가 ②그 거부가 내가 지키려는 것과 일치하는가 ③exit 0 이 관측인가 주장인가. ## Red Flags - 정리 코드에 `--force` / `-f` / `rm -rf` 가 있다 - 정리 전에 `is_clean()` / `is_safe_to_delete()` 같은 자체 판정 함수가 있다 - 정리 명령의 성공을 exit code 만으로 판단하고 **결과 상태를 안 본다** - 정리가 happy path(성공 종료)에만 달려 있다 - 로그에 "reclaim failed" 한 종류만 있고 "보류"가 없다 - 디스크 사용량이 실행 횟수에 비례해 단조 증가한다
عرض على GitHub