Community코딩 & 개발github.com

chrislacey89/pre-merge

구현 검증 완료 후 메인 브랜치 병합 전에 사용하는 주요 파이프라인 검토 단계로, PR 생성을 통해 작업 내역을 추적하고 아키텍처 검토를 수행합니다. QA 접수, 계획 수립 또는 구현 작업에는 사용하지 않습니다.

pre-merge란 무엇인가요?

이 스킬은 코드 변경 사항을 메인 브랜치에 병합하기 전에 아키텍처 원칙과 일치하는지 검토하는 GitHub PR 생성 및 리뷰 도구입니다. 작성자 모드(기본), 리뷰어 모드(`--pr`), 루프 모드(`--loop`)의 세 가지 모드로 동작하며, 각 모드는 검토 범위와 출력물이 다릅니다. 작성자 모드는 자신의 브랜치에서 PR을 생성하고 터미널에 검토 결과를 표시합니다. 리뷰어 모드는 기존 PR에 대해 초안 댓글을 생성합니다. 루프 모드는 검토 결과를 영구적으로 기록하여 세션 간에도 추적 가능하게 합니다. 이 스킬은 PRD 이슈 번호와 슬라이스 이슈에서 경계 맵을 추출하고, `review-checklist.md`에 정의된 아키텍처 검토 차원을 사용하여 전체 diff를 분석한 후 권고 사항을 제공합니다. 병합을 차단하거나 코드를 자동 수정하지 않으며, 발견된 문제는 소유자에게 할당되어 추적됩니다.

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

Installed? Explore more 코딩 & 개발 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

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

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

문서

Pre-merge (사전 병합)

PRD와 슬라이스 이슈를 연결하는 GitHub PR을 생성한 후, 프로젝트의 아키텍처 원칙에 대해 전체 diff를 검토합니다. 권고 사항을 생성하지만 병합을 차단하거나 코드를 자동 수정하거나 이슈를 생성하지 않습니다.

호출 위치

이 스킬은 구현이 검증된 후 메인 브랜치에 병합하기 전, 또는 다른 사람의 PR을 검토할 때 사용하는 주요 파이프라인 스킬입니다.

/pre-merge는 브랜치가 PR 생성, 아키텍처 검토, 최종 계획-코드 일치성 확인을 할 준비가 되었을 때 사용합니다. /pre-merge --pr <number>는 자신이 작성하지 않은 PR을 검토할 때 사용합니다.

구현 검증, QA 접수 또는 리팩터링 계획을 대체하는 용도로 사용하지 마십시오. 이 스킬은 작업이 이미 구축되어 검토할 준비가 되었다고 가정합니다.

모드

/pre-merge는 세 가지 모드 중 하나로 실행됩니다. 세 모드 모두 review-checklist.md에 정의된 아키텍처 검토 차원을 재사용하지만, 입력과 출력이 다릅니다.

  • 작성자 모드 (기본) - --pr 인수 없이 자신의 브랜치에서 호출됩니다. 스킬이 PR을 생성하고(2단계) 터미널에 권고 사항을 출력한 후(4단계) 제어권을 반환합니다. 자체 수정 커밋을 만들지 않으며 두 번째 검토를 열지 않습니다. 이 모드는 HITL 경로에서 /execute 6단계에 의해 자동 호출됩니다. 읽는 내용은 플래그가 아닌 브랜치 상태에 따라 결정됩니다. 첫 번째 검토는 전체 슬라이스를 읽습니다(슬라이스의 작업 항목이 시작 지점을 기록하지 않은 경우 전체 브랜치). 이미 검토 통화 스탬프가 있고 헤드가 이동한 PR에 대한 재실행은 스탬프 이후 델타(스탬프된 SHA 이후의 커밋)를 검토 대상으로 간주하며, 이전 검토가 다룬 내용을 다시 읽지 않습니다. 1단계 4번 하위 단계에서 이 결정을 내리고 출력합니다. 델타는 검토자가 읽을 수 있는 내용의 한계가 아닙니다. review-checklist.md는 검토자가 읽을 내용(델타 주변 트리 포함)을 정합니다.
  • 리뷰어 모드 - /pre-merge --pr <number>로, 자신이 작성하지 않은 PR에 대해 호출됩니다. 스킬은 PR 생성을 건너뛰고(PR이 이미 존재함) references/comment-craft.md(5P 게이트, Triple-R, 댓글 신호, MMG 교환)에 따라 검토 및 게시할 초안 댓글 텍스트를 생성합니다(4단계).
  • 루프 모드 - /pre-merge --loop로 호출되거나 /execute가 AFK 실행에서 핸드오프할 때 자동으로 진입합니다. 작성자 모드와 동일하게 1~4단계를 실행하지만, 1단계 4번 하위 단계의 범위 결정은 다릅니다. 루프 모드는 항상 전체 브랜치를 읽고 스탬프 이후 델타를 사용하지 않습니다. 그런 다음 5단계로 진행합니다. 모든 검토 결과를 PR 본문 또는 작업 항목의 내구성 있는 원장에 기록하고(references/state-backend.md § 검토 기록 위치 참조) 각 결과를 지지하거나 반박하는 증거를 첨부한 후 운영자에게 제어권을 반환합니다. 이 모드에서는 검토 결과가 터미널 출력이 아니라 소유자가 있는 내구성 있는 행이 됩니다. 루프 모드는 아무것도 수정하지 않으며 커밋을 만들지 않습니다. 운영자가 각 결과에 대해 수행할 작업을 결정합니다(5단계 § 루프가 수정하지 않는 이유 참조).

루프 모드의 종료 조건은 모든 검토 결과에 소유자가 할당된 경우이며, 검토 결과가 없으면 종료되지 않습니다. "검토 결과 없음"은 금지된 종료 신호입니다. 이는 4단계의 기존 최소 검토 결과 보호 장치와 일치합니다. 이 보호 장치는 사소하지 않은 diff에 대한 거의 빈 검토가 코드가 완벽해서가 아니라 검토가 조기에 중단되었음을 의미하기 때문에 작성되었습니다. 깨끗한 검토에서 종료되는 루프는 깨끗한 검토만을 안정적으로 생성하며 다른 것은 생성하지 않습니다(Meadows, 잘못된 목표 추구; Leveson: 낮은 사고 건수에 보상하지 말고 보고 자체에 보상하라).

사용자의 작업 브랜치가 아닌 브랜치에서 실행 중이고 --pr이 제공되지 않은 경우, 추측하지 말고 사용자가 특정 PR 번호에 대해 리뷰어 모드를 의미하는지 한 번 질문하십시오. 자동 감지는 키 입력을 절약하지만 모드를 잘못 분류하면 지역 권고 사항이었을 내용이 초안 댓글이 되거나 그 반대가 될 수 있습니다.

사용 시기

  • 작성자 모드: QA 통과 후 기능 브랜치를 메인에 병합하기 전; Ralph가 AFK 실행을 완료하고 동작을 확인한 후; 또는 전체 파이프라인 실행 없이도 병합 전에 검토하려는 모든 브랜치.
  • 리뷰어 모드: 팀원이나 외부 기여자가 PR을 열었을 때 review-checklist.md의 전체 아키텍처 검토를 diff에 적용하고 건설적인 댓글 텍스트를 생성하려는 경우.
  • 루프 모드: 검토 결과가 생성된 세션을 넘어서 유지되어야 할 때 — /execute의 AFK 핸드오프, 또는 여러 번에 걸쳐 검토할 브랜치로 매번 다시 도출하고 싶지 않은 경우. 지불해야 하는 비용이 검토 결과 추적을 놓치는 것이지 수정하는 것이 아닐 때 사용하십시오.

실행 흐름

1단계: 컨텍스트 수집

작성자 모드:

  1. PRD 이슈 번호를 묻습니다. 이 변경 사항이 전체 파이프라인을 거치지 않은 경우 "없음"을 수락합니다. AFK /execute 핸드오프에서 진입한 루프 모드에서는 묻지 마십시오. — 핸드오프에서 이슈 번호를 가져오고(/execute 6단계에서 전달) 번호가 없으면 "없음"으로 처리합니다. 응답할 사람이 없으므로 여기서 조건 없는 질문은 첫 단계에서 실행을 중단시킵니다. 사이드카 백엔드에서는 답변이 W-<n> 작업 항목 ID입니다(2단계). "없음"이면 2단계에서 PR을 기록할 대상이 없으며, 검토 노트와 루프 모드 원장도 저장할 위치가 없습니다. references/state-backend.md § 검토 기록 위치에서 어떻게 되는지 설명합니다.

  2. PRD가 주어진 경우:

    gh issue view <number>
    gh issue list --search "in:body #<prd-number>" --state all --json number,title,state,body --limit 100
    

    각 슬라이스 이슈 본문에서 경계 맵(생성/소비 섹션)을 파싱합니다.

    사이드카 백엔드에서 (bash "$SC" backend slices가 sidecar라고 응답함, references/state-backend.md 참조; 아래 블록이 이를 묻고 거부 시 gh issue로 폴백하지 않고 중단), PRD와 그 슬라이스는 작업 항목이며 1단계에서 묻는 번호는 W-<n> ID입니다. 이 브랜치가 구현하는 슬라이스이며, /execute가 전달하거나 그 PRD입니다. gh issue 명령은 실행되지 않습니다. 계보와 경계 맵은 작업 항목에서 가져옵니다.

    SC="<이 스킬 디렉토리>/references/sidecar.sh"
    W="W-<n>"
    case "$(bash "$SC" backend slices)" in
      sidecar) ;;
      github) echo "백엔드가 github입니다: 위의 이슈 블록으로 계보를 읽으십시오"; exit 0 ;;
      *) echo "계보 중단: sidecar.sh backend slices 실패; github 경로를 추측하지 않음" >&2; exit 1 ;;
    esac
    J="$(bash "$SC" work view "$W" --json)" || { echo "계보 중단: $W를 읽을 수 없음" >&2; exit 1; }
    case "$(jq -r '.kind // empty' <<<"$J")" in
      slice) PRD="$(jq -r '.parent // empty' <<<"$J")" ;;
      prd)   PRD="$W" ;;
      *) echo "계보 중단: $W에 kind가 없음" >&2; exit 1 ;;
    esac
    SLICES="$W"
    if [ -n "$PRD" ]; then
      bash "$SC" work view "$PRD" --json | jq -r '"# \(.id) (prd, \(.state))\n\(.body)"'
      SLICES="$(bash "$SC" work list --kind slice --parent "$PRD" --state all)" \
        || { echo "계보 중단: $PRD의 슬라이스를 나열할 수 없음" >&2; exit 1; }
    fi
    while IFS= read -r S; do
      [ -n "$S" ] || continue
      bash "$SC" work view "$S" --json | jq -r '"# \(.id) (slice, \(.state))\n\(.body)"'
    done <<<"$SLICES"
    

    이슈 본문과 동일한 방식으로 해당 본문에서 경계 맵을 파싱합니다. $W를 유지하십시오. 2단계에서 PR을 기록합니다.

  3. 기준 브랜치를 감지하고 diff를 평가합니다:

    SC="<이 스킬 디렉토리>/references/sidecar.sh"
    # $BASE_REF는 범위 끝점이고; $BASE_BRANCH는 `--base`가 사용하는 이름입니다.
    BASE_REF="$(bash "$SC" base-ref)" || { echo "base-ref 실패: 기준을 추측하지 않음" >&2; exit 1; }
    BASE_BRANCH="$(bash "$SC" base-ref --name)" || { echo "base-ref --name 실패: 기준을 추측하지 않음" >&2; exit 1; }
    git diff "$BASE_REF...HEAD" --stat
    git log --oneline "$BASE_REF..HEAD"
    

    base-ref는 사이드카에 선언된 base_ref가 있으면 출력하고, 없으면 origin/HEAD 기본값을 출력합니다. 0이 아닌 종료 코드에서는 ref를 출력하지 않습니다. 기준을 추측하지 말고 stderr를 중계하고 중단합니다. 스택된 PR 슬라이스의 경우 여기서 설정된 변수는 이후 블록에 도달하지 않습니다. 형제 슬라이스의 브랜치 이름(PR이 대상으로 하는 업스트림)을 해당 브랜치를 사용하는 각 블록(백엔드의 2단계 블록과 4단계 스탬프 블록)의 BASE_BRANCH= 자리 표시자에 넣습니다. 기준과 diff가 없으면 검토할 내용이 없다고 사용자에게 알리고 중단합니다.

    잔여: $BASE_REF는 마지막 git fetch 이후의 상태만큼만 최신입니다. origin/<base>가 확인되지 않으면 base-ref는 로컬 브랜치를 기준으로 측정하고 stderr에 이를 알립니다. 개수가 잘못된 것 같으면 git fetch 후 다시 실행하십시오.

  4. 범위 결정 — 전체 슬라이스 또는 스탬프 이후 델타. 이 결정은 작성자 모드에만 해당됩니다. 루프 모드는 이를 건너뛰고 무조건 SCOPE_FROM="$BASE_REF"로 설정하여 3단계의 루프 모드 항목에 따라 호출할 때마다 전체 브랜치를 읽습니다. 첫 번째 검토는 전체 슬라이스를 읽습니다. sidecar.sh range는 왼쪽 끝을 제공합니다. 이는 2단계에서 명명된 작업 항목에 이 브랜치에 여전히 기록된 시작점이 있는 경우 해당 시작점이고, 그렇지 않으면 $BASE_REF입니다. 따라서 github 백엔드에서 슬라이스는 전체 브랜치입니다. 이 스킬이 이미 스탬프를 찍고 헤드가 이동한 PR에 대한 재실행 — /fix-findings 라운드 또는 의도적인 잠금 삭제 후 수동 수정 — 은 스탬프 이후 델타를 대상으로 간주합니다. 스탬프는 references/state-backend.md에 따라 backend stamp가 말하는 위치(PR 본문 또는 사이드카)에 있습니다. /closeout 2단계와 동일한 표현식으로 PR 본문을 읽습니다. scripts/test-review-currency-marker.sh는 두 바이트가 동일함을 확인하고, scripts/test-delta-review-scope.sh는 두 백엔드에서 명명된 모든 브랜치 상태에 대해 이 블록을 fixture에 대해 실행합니다.

    SC="<이 스킬 디렉토리>/references/sidecar.sh"
    case "$(bash "$SC" backend stamp)" in
      sidecar)
        REVIEWED_SHA=$(bash "$SC" stamp read "$(git branch --show-current)") ;;
      github)
        REVIEWED_SHA=$(gh pr list --head "$(git branch --show-current)" --json body -q '.[0].body // ""' \
          | sed -n 's/.*<!-- reviewed-at: \([0-9a-f]\{40\}\) -->.*/\1/p' | tail -1) ;;
      *) echo "범위 중단: sidecar.sh backend stamp 실패; github 경로를 추측하지 않음" >&2; exit 1 ;;
    esac
    W="<2단계에서 명명된 W-<n>, 또는 github 백엔드에서는 비어 있음>"
    BASE_REF="$(bash "$SC" base-ref)" || { echo "범위 중단: sidecar.sh base-ref 실패" >&2; exit 1; }
    RANGE="$(bash "$SC" range ${W:+"$W"})" || { echo "범위 중단: sidecar.sh range 실패" >&2; exit 1; }
    SLICE_FROM="${RANGE%%..*}"
    SCOPE_FROM="$SLICE_FROM"
    if [ -n "$REVIEWED_SHA" ] \
      && git merge-base --is-ancestor "$REVIEWED_SHA" HEAD 2>/dev/null \
      && [ "$REVIEWED_SHA" != "$(git rev-parse HEAD)" ] \
      && [ -z "$(git log --merges --format=%H "$REVIEWED_SHA..HEAD")" ]; then
      SCOPE_FROM="$REVIEWED_SHA"
    elif [ -n "$REVIEWED_SHA" ] \
      && ! git merge-base --is-ancestor "$REVIEWED_SHA" HEAD 2>/dev/null \
      && [ "$(git rev-parse -q --verify "$REVIEWED_SHA^{tree}")" = "$(git rev-parse 'HEAD^{tree}')" ]; then
      SCOPE_FROM="HEAD"
    fi
    if [ "$SCOPE_FROM" = HEAD ]; then
      echo "검토 범위: 없음, 기록만 변경됨 — HEAD는 $REVIEWED_SHA에서 검토된 트리를 전달함"
    elif [ "$SCOPE_FROM" = "$BASE_REF" ]; then
      echo "검토 범위: 전체 브랜치, $BASE_REF부터"
    elif [ "$SCOPE_FROM" = "$SLICE_FROM" ]; then
      echo "검토 범위: 전체 슬라이스, $SLICE_FROM부터 ($W의 기록된 시작점)"
    else
      echo "검토 범위: 스탬프 이후 델타, $SCOPE_FROM부터"
    fi
    git diff "$SCOPE_FROM...HEAD" --stat
    git log --oneline "$SCOPE_FROM..HEAD"
    

    네 가지 상태는 의도적으로 전체 슬라이스로 폴백됩니다. 스탬프 없음(첫 번째 검토 또는 수동 PR); HEAD의 조상이 아니고 HEAD가 더 이상 해당 트리를 전달하지 않는 스탬프(브랜치가 다시 작성되고 내용도 함께 변경되었거나 검토된 커밋이 사라짐 — /closeout은 이를 더 강력한 분기 신호라고 부르며 더 작은 검토가 아님); HEAD와 동일한 스탬프(범위 지정할 새 항목 없음); 스탬프 이후 병합 커밋(스탬프 이후 diff는 이 브랜치에서 아무도 작성하지 않은 기준 브랜치 자체 변경 사항을 포함할 수 있음). range가 기록된 시작점을 찾지 못하면 전체 슬라이스는 전체 브랜치이며, 폴드 또는 리베이스로 인해 시작점이 사라진 경우 stderr에 알리고 $BASE_REF를 응답하며 시작점을 다시 고정하지 않습니다. 결정이 출력되므로 터미널 출력과 PR이 읽은 내용에 대해 동일한 내용을 말하게 됩니다.

    검토된 트리를 유지한 재작성은 분기가 아닙니다. 스탬프된 커밋이 HEAD의 조상이 아니지만 로컬에 여전히 존재하고 HEAD와 동일한 트리를 전달하는 경우, 기록은 변경되었지만 내용은 변경되지 않았습니다. 4단계의 핸드오프가 제공하는 폴드(git rebase -i --autosquash)가 정확히 이를 생성합니다. 블록은 review scope: none을 출력하고 SCOPE_FROM="HEAD"로 설정합니다. 3단계와 4단계의 스탬프(HEAD에 다시 스탬프) 및 핸드오프 메뉴를 제외한 모든 항목을 건너뜁니다. 스탬프는 검토된 콘텐츠를 인증하며 트리 해시는 해당 콘텐츠를 정확히 나타냅니다. 스탬프된 커밋을 로컬에서 사용할 수 없으면 트리를 비교할 수 없으며 재작성은 위의 전체 브랜치 폴백을 사용합니다.

    델타 재실행 시 델타가 대상이며 트리는 여전히 읽을 수 있습니다. 각 3단계 검토자에게 스탬프 이후 커밋이 해결했다고 주장하는 검토 결과(이 세션의 이전 출력, 루프 모드가 작성한 원장(references/state-backend.md § 검토 기록 위치 참조), 또는 커밋 메시지 자체)와 /fix-findings 중단자 평결(있는 경우) 및 bash "$SC" profile 출력(3단계 계약이 모든 검토자에게 전달하는 내용)을 그대로 전달합니다. 커밋당 첫 번째 질문은 수정이 검토 결과가 명명한 모든 사이트에서 해당 검토 결과를 해결하는지 여부이며, 이는 수정 완전성 차원입니다. 두 번째 질문은 수정이 동일한 사이트에 새로운 문제를 도입하지 않는지 여부이며, 이는 회귀 차원입니다. review-checklist.md의 해당 차원은 이러한 질문을 안내합니다. 각 검토자는 자신의 결과를 출력합니다. 루프 모드 원장이 있는 경우 결과가 원장에 추가됩니다. 그렇지 않으면 터미널에 추가 출력으로 표시됩니다.

Individual skills in this repo

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

chrislacey89/closeout

Primary pipeline tail step after /pre-merge, when a reviewed PR is ready to land and you want the workspace back to a clean base. Use to merge, switch off the worktree before removing it, prune the merged branch, pull base, and verify a clean end state. Triggers on 'close out', 'merge and clean up', 'get back to a clean main/base', 'tear down the worktree'. Not for capturing lessons (that's /compound), or closing the PRD and any other open slices and research-artifact hygiene (that's the cleanup prose in SYSTEM-OVERVIEW Step 9).

chrislacey89/compound

Primary pipeline knowledge-capture step that closes the compounding loop. Capture a durable lesson in docs/solutions/ onto the open PR before /closeout merges (the default), or post-merge when the lesson only surfaces during/after merge. Use so future /research and /write-a-prd runs improve. Not for trivial edits with no reusable project-level learning, and not for the merge/teardown itself (that's /closeout).

chrislacey89/excavate

User-invoked side-route for onboarding to a legacy repo with no docs/solutions/, research archive, or written decisions. Mines the last 12 months of git history and, when gh is available, PR bodies and review threads, into ranked hotspots, cited recovered decisions, and a who-to-ask map, then offers a PR of docs/solutions/ entries. Not for repos that already compound, not for judging people, not model-invoked.

chrislacey89/execute

Primary pipeline execution step after /prd-to-issues or for clearly scoped implementation work. Use to build, verify, and commit a concrete slice, delegating to /tdd for backend work and behavior-heavy frontend logic when red-green-refactor will reduce risk. Not for shaping or pre-merge review.

관련 스킬