Community연구 & 데이터 분석github.com

chrislacey89/closeout

승인된 PR을 병합하고 워크트리를 정리하며 작업 공간을 깨끗한 기준 브랜치로 되돌리는 메인 파이프라인의 마지막 단계입니다.

closeout란 무엇인가요?

이 스킬은 코드 리뷰가 완료된 PR을 안전하게 병합하고 개발 환경을 초기화하는 파이프라인의 종착점입니다. 사용자가 'close out', 'merge and clean up', 'get back to a clean main/base' 등의 명령을 내리면, PR 병합, 워크트리 제거, 병합된 브랜치 정리, 기준 브랜치 풀, 최종 상태 확인을 순차적으로 수행합니다. 특히 워크트리를 제거하기 전에 셸을 기준 브랜치로 이동시키는 순서가 중요하며, 이를 지키지 않으면 고아 디렉토리에서 발생하는 거짓 MODULE_NOT_FOUND 오류로 인해 실제 회귀 버그로 오인될 수 있습니다. 또한 `/pre-merge` 스킬이 남긴 리뷰 시점 스탬프를 현재 HEAD와 비교하여 리뷰 이후 커밋이 추가되었는지 확인함으로써 '검토된 아티팩트를 배포해야 한다'는 지속적 딜리버리 원칙을 준수합니다. GitHub, Conductor, Codespaces 등 다양한 호스트 환경에서 동작하며, 호스트가 워크트리 수명 주기를 관리하는 경우에는 파일 시스템 정리 단계를 생략하고 GitHub 네이티브 병합만 수행합니다.

지원 대상✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/chrislacey89/skills/tree/HEAD/closeout

Installed? Explore more 연구 & 데이터 분석 skills: affaan-m/uspto-database, affaan-m/scholar-evaluation, affaan-m/literature-review · View all 6 →

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

마무리 (Closeout)

리뷰된 PR을 가져와 작업 공간을 깨끗한 기준 브랜치로 되돌립니다 — 병합, 워크트리 제거 전 셸 전환 후 제거, 병합된 브랜치 정리, 기준 브랜치 풀, 최종 상태 확인. 이는 파이프라인의 꼬리 부분입니다: 가장 반복되는 동작("다 됐어요, 병합하고 깨끗한 메인 브랜치로 돌려주세요")을 사용자의 머리에서 스킬로 옮긴 것입니다.

왜 존재하는가

/execute Step 0은 모든 슬라이스에 대해 워크트리를 생성합니다; 이 스킬이 있기 전까지는 아무것도 제거하지 않았습니다. 워크트리는 유입은 있지만 유출이 없는 재고(stock)이므로 재고는 제한 없이 증가합니다(Meadows, Thinking in Systems — 유입-무-유출 재고에 대한 구조적 탈출구는 누락된 피드백 루프를 추가하는 것이지, 더 열심히 설득하는 것이 아닙니다). 병합 → 해체 → 풀 시퀀스는 오직 사용자의 기억 속에만 존재했으며, "예상 기억은 조용히 실패합니다"(Norman, The Design of Everyday Things — 지식을 머리에서 세상으로 옮기십시오). 순서는 실제로 오류가 발생하기 쉽습니다: 셸의 현재 작업 디렉토리가 여전히 워크트리 안에 있는 상태에서 워크트리를 제거하면 셸이 삭제된 디렉토리에 갇히게 되고, 거기서 실행되는 node_modules 의존 Stop 훅은 실제 회귀처럼 읽히는 거짓 MODULE_NOT_FOUND "린트/타입 오류"를 쏟아냅니다. Step 4의 제거 전 기준 브랜치로 cd하는 순서가 바로 그 실수에 대한 하중 지지 수정입니다.

이 스킬은 기존 도구를 조율합니다(gh pr merge, wt remove / git worktree remove, commit-commands:clean_gone). 파일 시스템 상태를 도입하지 않습니다. 결과를 확인합니다 — 깨끗한 트리, 새로운 기준 브랜치, PR 병합됨 — 단순히 정리가 실행되었는지 여부가 아닙니다(Meadows의 "잘못된 목표 추구": "실행되었는가"로 측정된 마무리는 프록시를 최적화하고 깨지기 쉬운 의식을 만듭니다).

호출 위치

이것은 기본 제공 경로의 병합 → 정리 꼬리를 소유하는 기본 파이프라인 스킬입니다:

… → /pre-merge → /compound (PR 내부, 교훈이 있는 경우) → /pre-merge (복합 커밋에 대해 재실행; 재스탬프) → [병합 + 워크트리 해체 — 이 스킬] → [이슈 종료 — Step 9 산문]

다음과 같은 경우 /closeout을 사용하십시오:

  • /pre-merge가 PR을 생성했고 리뷰가 완료된 경우 (또는 변경 사항이 리뷰가 필요 없을 정도로 사소한 경우)
  • PR이 승인되고 병합 가능하며, 병합되고 작업 공간이 깨끗한 기준 브랜치로 돌아가기를 원하는 경우
  • 사용자가 "마무리", "병합하고 정리", "워크트리 해체", 또는 "깨끗한 메인/기준 브랜치로 돌아가기"를 요청하는 경우

다음과 같은 경우 /closeout을 사용하지 마십시오:

  • PR이 아직 생성되지 않았거나 여전히 리뷰 중인 경우 — 먼저 /pre-merge를 실행하십시오
  • 지속 가능한 교훈을 포착하려는 경우 — 그것은 /compound이며, 기본적으로 /closeout이 병합하기 전에 이미 PR을 타고 있습니다(병합 후 /compound는 대체 방법입니다); /closeout은 교훈을 포착하지 않습니다
  • PRD와 슬라이스 이슈를 종료하거나 리서치 아티팩트의 머리말을 조정하려는 경우 — 그것은 SYSTEM-OVERVIEW.md Step 9의 정리 산문입니다; /closeout은 git-위생 절반을 수행하고 이슈 종료 및 리서치-아티팩트-대체 절반을 Step 9로 연기하며 중복하지 않습니다. 이 스킬이 만드는 유일한 종료는 sidecar 백엔드(Step 3)에서 병합된 슬라이스의 작업 항목입니다. 거기에는 Closes #N 줄이 병합 시 이를 수행하지 않기 때문입니다.
  • 변경 사항이 전혀 기능 브랜치에 없는 경우 (병합할 것이 없는 기준 브랜치에 있는 경우) — 마무리할 것이 없습니다

이것은 HITL-확인 및 비차단(non-blocking)입니다: 이미 선택적인 꼬리를 운영화합니다. 자동 병합하지 않습니다 — 병합은 외부 지향적이고 되돌리기 어려우므로 항상 먼저 확인합니다.

프로세스

이것을 do-confirm 체크리스트(Gawande)로 실행하십시오: 각 단계를 수행한 후 다음 단계 전에 그 결과를 확인하십시오. 파괴적인 단계(4–6)는 자체 보호됩니다 — 실행하기 전에 Step 2의 안전 전제 조건이 충족되는지 확인하십시오.

0. 호스트 프로비저닝 격리? 먼저 해체를 양도하십시오

병합/해체 시퀀스 전에 이 저장소의 격리가 호스트 소유(Conductor, Codespaces, devcontainers)인지 확인하십시오. 그런 경우, /closeout은 여전히 GitHub 네이티브 병합을 수행하지만 워크트리를 해체하거나 로컬 브랜치를 정리하지 않습니다 — 호스트가 해당 수명 주기의 절반을 소유하며, 이에 대항하는 것은 Meadows의 정책 저항(policy resistance)입니다 (동일한 재고를 밀어내는 두 행위자). 워크트리 재고를 호스트에 양도하십시오.

유출 감지는 /execute의 유입 검사보다 더 엄격하며 일반적인 "toplevel ≠ 기본 작업 트리" 휴리스틱만 사용해서는 안 됩니다: 파이프라인에서 만든 워크트리도 해당 테스트를 만족시키므로, 일반 신호는 "호스트가 해체를 소유함"과 "파이프라인이 이것을 만들었고 해체해야 함"을 구분할 수 없습니다. .claude/settings.json worktree.provisioning에서 해결하십시오 (없으면 기본값 "auto"), 그리고 /execute가 유입 시 하는 것과 정확히 동일하게 이 종료 지점에서 명시적 재정의를 존중하십시오:

  • host — 조건 없이 해체를 양도합니다.
  • pipeline — 파이프라인이 해체를 소유합니다. 호스트 환경 변수가 있더라도 아래 Steps 4–6 전체를 실행하십시오 — 명시적 pipeline 설정은 /execute가 워크트리를 자체적으로 프로비저닝했음을 의미하므로, /closeout이 이를 해체해야 합니다(호스트 이전 동작). 호스트 환경 변수는 명시적 pipeline 선택을 재정의하지 않습니다.
  • auto (기본값) — 호스트 환경 변수가 이 트리를 설명하는 경우에만 해체를 양도하십시오: 아래 블록이 host_owned=yes를 출력합니다. 일반적인 "toplevel ≠ 기본" 신호만으로 양도하지 말고, CONDUCTOR_WORKSPACE_PATH의 단순한 존재만으로도 양도하지 마십시오 — Conductor에서 시작된 셸은 이를 상속받아 다른 저장소의 작업 공간을 명명하는 것을 가질 수 있으며, 그것으로 양도하면 파이프라인에서 만든 워크트리를 해체할 사람 없이 남겨둡니다.
# 호스트 신호: 호스트 환경 변수가 이 셸이 아닌 이 트리를 설명합니까?
host_owned=no
top=$(cd "$(git rev-parse --show-toplevel)" && pwd -P)
if [ -n "${CONDUCTOR_WORKSPACE_PATH:-}" ]; then
  ws=$(cd "$CONDUCTOR_WORKSPACE_PATH" 2>/dev/null && pwd -P) || ws=
  if [ -n "$ws" ]; then
    case "$top/" in "$ws"/*) host_owned=yes ;; esac
  fi
fi
if [ -n "${CODESPACES:-}" ] || [ -n "${REMOTE_CONTAINERS:-}" ]; then host_owned=yes; fi
echo "host_owned=$host_owned"

해체가 양도되지 않은 경우 (worktree.provisioning: "auto"와 host_owned=no 포함), 파이프라인이 이를 소유합니다 — 아래 Steps 4–6을 작성된 대로 실행하십시오.

격리가 호스트 소유인 경우, /closeout은 축소된 시퀀스를 실행합니다:

  • Step 3 (병합)은 여전히 완전히 실행됩니다 — gh pr merge --delete-branch. Conductor 및 유사 호스트는 PR을 생성하지만 병합은 인간의 행동에 맡깁니다; 파이프라인이 GitHub 네이티브 병합을 수행하는 것이 추가하는 가치이며, 호스트는 결과를 반영합니다. 연기는 파일 시스템 해체로 범위가 제한되며, 절대 병합이 아닙니다.
  • Step 4의 재앵커, Step 5의 wt remove / git worktree remove / ExitWorktree { remove }, Step 6의 로컬 브랜치 정리를 건너뜁니다 — 호스트가 워크트리 및 브랜치 수명 주기를 소유합니다(Conductor는 작업 공간이 보관될 때 이를 회수합니다). 호스트 관리 트리에 대해 절대 --force를 사용하지 마십시오.
  • Step 8의 체크리스트: "워크트리 제거됨" 및 "브랜치 정리됨"은 "해당 없음 — 호스트 관리" 가 됩니다. 병합, 깨끗한 트리, PR 병합됨, 및 원격 브랜치 확인은 여전히 적용됩니다. 마지막 항목은 동일한 N/A로 분류하기 쉽지만 그렇지 않습니다: 호스트가 소유하는 것은 파일 시스템 워크트리와 로컬 브랜치인 반면, 원격 브랜치는 이 스킬이 위 한 글머리 기호에서 여전히 완전히 실행하는 gh pr merge --delete-branch에 의해 삭제됩니다. 호스트가 소유하는 절반을 양도하는 것은 호스트가 소유하지 않는 절반을 양도하는 것이 아닙니다.

1. 의도 확인 및 상태 수집

절대 자동 병합하지 마십시오. 무엇을 하려는지 진술하고 명시적인 승인을 받으십시오.

먼저 사실을 수집하십시오 (읽기 전용):

git branch --show-current                                   # 기능 브랜치
git rev-parse --show-toplevel                               # 현재 작업 트리 (워크트리일 수 있음)
git worktree list --porcelain | awk '/^worktree /{print $(2); exit}'   # 기본 체크아웃 (메인 작업 트리)
gh pr view --json number,title,url,state,mergeable,mergeStateStatus,reviewDecision 2>/dev/null

PR이 대상으로 하는 기준 브랜치를 결정하십시오 — 저장소에 선언된 기본값, 가정이 아닙니다:

SC="<이 스킬의 디렉토리>/references/sidecar.sh"
bash "$SC" base-ref --name   # 베어 브랜치 이름: prod, origin/prod 아님

이 사이트는 기준 브랜치 이름을 원하며, ref가 아닙니다 — Step 4에서 git switch <base-branch>에 제공되며, git switch는 원격 추적 ref를 완전히 거부합니다(fatal: a branch is expected, got remote branch 'origin/prod') — 곧 풀할 로컬 기준 브랜치를 원합니다. 따라서 base-ref --name을 요청하며, /pre-merge, /help, /walk-commits, /visual-recap, /compound, /execute가 취하는 ref 형식을 의도적으로 취하지 않습니다; 그들은 기준을 범위 끝점으로만 사용하기 때문에 ref가 필요하며, 이 스킬은 그렇게 하지 않습니다. 그들과 일치시키기 위해 "수정"하지 마십시오. base-ref --name이 0이 아닌 종료 코드로 종료되면 기준을 찾지 못한 것입니다: stderr를 중계하고 추측하지 말고 중단하십시오.

사용자에게 하나의 짧은 블록으로 알리십시오: 어떤 PR이 병합될지, 어떤 워크트리(있는 경우)와 브랜치가 해체될지, 그리고 어떤 기준 브랜치로 돌아가 풀할지. 확인을 기다리십시오. 이 브랜치에 PR이 없으면 중단하고 /pre-merge로 라우팅하십시오.

2. 파괴적 단계 전 안전 전제 조건 확인

해체는 파괴적입니다; 먼저 안전한지 확인하십시오 (자체 보호). 다음 모두 충족되어야 합니다:

  • 기능 브랜치가 완전히 푸시되었습니다 — git status에 푸시되지 않은 커밋이 없고, git log @{u}..이 비어 있습니다.
  • 이 브랜치에 대한 PR이 존재하고 병합 가능합니다 (mergeable이 MERGEABLE; mergeStateStatus가 BLOCKED/DIRTY가 아님).
  • 필수 검사가 통과되었고 리뷰가 승인되었습니다 (reviewDecision이 APPROVED, 또는 저장소가 리뷰를 요구하지 않고 사용자가 명시적으로 수락했습니다).

전제 조건 중 하나라도 실패하면 중단하고 어떤 것인지 보고하십시오 — 병합하거나 아무것도 제거하지 마십시오. 실패한 전제 조건은 강제로 진행하는 것이 아니라 리뷰로 돌아가라는 신호입니다. 실패한 것이 리뷰인 경우 — reviewDecision이 CHANGES_REQUESTED — 돌아가는 경로는 /respond-to-review입니다: 각 주석을 코드와 비교하고, 인간이 무엇을 수정하고 무엇을 반박할지 결정하며, 수정 사항은 /fix-findings와 /pre-merge 재실행을 거친 후 /closeout이 다시 실행됩니다.

리뷰 통화성 — 승인이 이 diff를 여전히 포함합니까? (비차단)

위의 세 체크박스는 리뷰가 발생했는지 묻습니다. 그 중 어느 것도 그것이 병합될 diff를 포함하는지 묻지 않습니다. 의도된 선형 흐름에서 이들은 동일한 질문이며, 이것이 바로 간격이 보이지 않는 이유입니다; 실제 HITL 세션에서 범위는 리뷰 통과 후 브랜치에 누적되며, /pre-merge가 읽은 diff를 /closeout이 병합하는 diff와 연결하는 것은 아무것도 없습니다. 이것은 피드백 루프가 없는 유입입니다(Meadows — 구조적 수정은 루프를 추가하는 것이지, 열심히 설득하는 것이 아닙니다), 그리고 그것은 "PR이 리뷰되었습니다"를 "PR의 초기 버전이 리뷰되었습니다"로 조용히 저하시킵니다, 저하가 가장 중요한 바로 그 순간에. 지속적 딜리버리는 규칙을 분명히 말합니다: 당신이 리뷰한 아티팩트는 당신이 배송하는 아티팩트여야 합니다.

/pre-merge Phase 4가 남긴 스탬프를 읽고 현재 HEAD와 비교하십시오. backend stamp가 말하는 곳에 있습니다, references/state-backend.md에 따라: PR 본문 또는 사이드카에.

git fetch --quiet
SC="<이 스킬의 디렉토리>/references/sidecar.sh"
case "$(bash "$SC" backend stamp)" in
  sidecar)
    REVIEWED_SHA=$(bash "$SC" stamp read "$(git branch --show-current)")
    HEAD_SHA=$(git rev-parse '@{u}' 2>/dev/null || git rev-parse HEAD) ;;
  github)
    REVIEWED_SHA=$(gh pr view <number> --json body -q .body \
      | sed -n 's/.*<!-- reviewed-at: \([0-9a-f]\{40\}\) -->.*/\1/p' | tail -1)
    HEAD_SHA=$(gh pr view <number> --json headRefOid -q .headRefOid) ;;
  *) echo "closeout aborted: sidecar.sh backend stamp failed; not guessing the github path" >&2; exit 1 ;;
esac

사이드카 백엔드에서 HEAD는 브랜치의 업스트림이며, 이는 포지가 병합할 것입니다: 위의 첫 번째 전제 조건은 이미 로컬 HEAD와 일치하도록 요구합니다. 브랜치에 업스트림이 없으면 HEAD로 대체됩니다. stamp read는 PR-본문 마커가 전달하는 것과 동일한 전체 40자 OID를 출력하거나 아무것도 출력하지 않으므로, 아래 모든 것은 두 백엔드에서 변경 없이 실행됩니다.

정확히 40개의 16진수 문자, 문자열 동등성으로 비교. 그 문장의 두 부분 모두 하중 지지입니다. 이 마커는 두 스킬에 걸친 계약이기 때문입니다. /pre-merge Phase 4는 전체 40자 OID를 내보내고, headRefOid는 항상 40자이므로, \{40\}만이 동등하게 비교할 수 있는 유일한 너비입니다 — 더 짧은 것을 받아들이면 잘못된 마커(손으로 편집된 본문, 잘못된 절반으로 대체된 템플릿 자리 표시자)가 성공적으로 구문 분석된 다음 영원히 $HEAD_SHA와 다르게 될 수 있습니다. [[ "$REVIEWED_SHA" == "$HEAD_SHA" ]]가 비교입니다: 두 개의 전체 OID에 대한 정확한 문자열 동등성, 접두사 테스트가 아닙니다. 정규식이 거부하는 마커는 빈 $REVIEWED_SHA를 생성하고 아래 "스탬프 없음" 분기로 이동하여 정상적으로 저하됩니다 — "SHA가 다름" 분기가 HEAD가 전혀 움직이지 않은 PR에 대해 거짓 경보를 울리는 것보다 훨씬 낫습니다. Skill Kit 저장소에서 scripts/test-review-currency-marker.sh는 이 라운드 트립을 두 스킬 모두에 대해 고정하여 작성자 형식과 이 파서가 조용히 분리될 수 없도록 합니다.

세 가지 결과, 그 중 하나만 중단합니다:

  • SHA가 일치 — 문자열 동등 전체 OID, 따라서 리뷰된 diff 는 병합 후보입니다. 조용히 통과하십시오. 서술하지 마십시오: 이것은 일반적인 경우이며, 매 실행마다 사건이 아닌 것을 발표하는 것은 게이트를 벽지로 만드는 것입니다.
  • 스탬프를 찾을 수 없음 ($REVIEWED_SHA가 비어 있음) — 수동으로 작성된 PR, 외부 기여, 스탬프 이전의 PR, 또는 위 정규식이 거부한 모양의 마커. 한 줄로 기록하십시오 ("이 PR에 /pre-merge 스탬프 없음 — 리뷰 통화성 알 수 없음")하고 계속하십시오. 스탬프가 없다고 해서 리뷰되지 않은 diff의 증거는 아니며, 스탬프되지 않은 모든 PR에서 중단하는 것은 경고 피로를 유발하여 게이트가 반사적으로 무효화되는 바로 그것입니다(Meadows, 정책 저항).
  • SHA가 다름 — 문자열 동등하지 않은 두 개의 잘 형성된 40자 OID, 이는 리뷰 후 커밋이 도착했음을 의미합니다. 중단하지 마십시오,

...

Individual skills in this repo

This repo contains 4 individual skills — each has its own dedicated page.

관련 스킬