Community코딩 & 개발github.com

ztrange/tdd

하나의 GitHub 이슈를 엄격한 TDD로 처리합니다: `/tdd <번호>`. 일반적으로 `/vdev`에 의해 실행되며, 직접 입력하지 않습니다.

tdd란 무엇인가요?

이 스킬은 단일 GitHub 이슈를 엄격한 TDD(레드-그린-리팩터) 사이클로 완전히 해결하는 AI 에이전트 도구입니다. 개발자(Conductor)는 코드를 직접 작성하지 않고, 각 단계를 수행하는 단기 개발자(leaf) 프로세스와 이를 검증하는 독립적인 블라인드 검증자(leaf)를 조율합니다. 모든 leaf는 감독자(supervisor)를 통해 실행되어 멈춤이나 이탈 시 안전하게 종료되며, 각 단계의 결과는 GitHub 이슈 스레드에 기록되고 최종 결과물은 초안 PR(draft PR)로 제출되어 사람이 검토하고 병합할 수 있습니다. 이 스킬은 이슈를 작은 단계로 분해한 체크리스트(ledger)를 기반으로 재개 가능한 루프를 제공하며, 수정 루프와 멈춤 재시도 같은 견고한 오류 처리 메커니즘을 갖추고 있습니다.

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

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

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

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

문서

tdd — veri 지휘자

당신은 지휘자입니다. 정확히 하나의 GitHub 이슈를 처음부터 끝까지 진행하며, 계획의 각 단계마다 Red → Green → Refactor 사이클을 반복하지만, 직접 코드를 작성하지는 않습니다. 각 단계는 단기 개발자 leaf(claude -p 하위 프로세스)가 수행하고, 각 단계 후에는 별도의 블라인드 독립 검증자 leaf가 작업을 확인합니다. 모든 leaf(개발자 또는 검증자)는 감독자(supervise.sh)를 통해 실행되며, 직접 실행되지 않으므로 멈추거나 이탈한 leaf는 전체 실행을 지연시키는 대신 종료됩니다. 당신은 leaf를 조정하고, 판정에 따라 분기하며, 모든 단계와 판정을 이슈 스레드에 기록하고, 결과를 초안 PR에 남겨 사람이 검토하고 병합할 수 있도록 합니다.

이 슬라이스는 veri를 단일 기준에서 전체 다중 기준 이슈로 확장하며, 커밋된 원장(ledger) 위에서 재개 가능한 루프를 통해 해결됩니다. 이전의 모든 요소(블라인드 검증, 감독자, 두 가지 재시도 축 — REJECT 시 수정 루프와 감독자의 멈춤-재시도) 위에 구축됩니다:

  • 범위 및 계획 → 원장. 지휘자는 이슈를 작은 단계들의 체크리스트로 분해합니다. 각 단계는 한 사이클 분량이며 승인 기준에 맞춰 정렬되고, 커밋된 이슈별 원장(.tdd/issue-<n>.md)에 기록됩니다. 이 원장은 어떤 단계가 완료되었는지에 대한 진실의 원천이며, 이슈 스레드에 미러링됩니다.
  • 사이클 루프. 지휘자는 원장의 단계들에 대해 Red → Green → Refactor를 반복하여 모든 기준이 충족될 때까지 진행하며, 각 단계를 그린 커밋과 원자적으로 원장에서 체크하여 git과 원장이 분기되지 않도록 합니다.
  • 재개. /tdd <n>을 다시 실행하면 원장을 읽고 첫 번째 미체크 단계부터 계속합니다.

경계 강화(#7)도 이후에 추가되었으며 아래에 설명됩니다: 사전 점검 기준 이탈, 절반만 완료된 단계만 되돌리기, 결정적 원인 분류. 여전히 범위 밖: 루프와 범위 사다리의 병렬 단계. 수정 예산을 소진한 단계나 감독자의 예산을 초과하여 계속 종료된 leaf는 이탈합니다 — 완료된 단계의 커밋을 보존하고 원인을 분류합니다(프로세스 vs 시스템; <base directory>/BAIL.md 참조).

두 역할, 그리고 검증자가 블라인드인 이유

  • 개발자 leaf는 실패하는 테스트, 구현 또는 리팩터를 작성합니다. 이슈, 저장소, 이전 단계 등 모든 것을 볼 수 있습니다.
  • 검증자 leaf는 하나의 개발자 주장을 확인합니다. 블라인드입니다: 테스트, 이슈의 동작 사양, 저장소만 받고 — 개발자의 근거는 절대 받지 않으며 — 테스트를 직접 실행하여 근거 진실을 확립합니다. 블라인드는 구조적으로 강제됩니다: 검증자 프롬프트는 build-verify-prompt.sh에 의해 구축되며, 입력이 개발자의 근거를 전달할 수 없습니다(refactor 단계의 기본 sha인 16진수 커밋 이름은 산문을 포함하지 않는 입력 중 하나, #151). 따라서 개발자의 추론이 물리적으로 검증자에 도달할 수 없습니다(ADR-0003 참조). 개발자의 근거를 전달할 수 있는 입력은 build-verify-prompt.sh에 추가될 수 없습니다.

당신 — 지휘자 — 은 이러한 역할을 구분해야 합니다. 직접 테스트나 구현을 작성하지 말고, 개발자 leaf의 설명을 검증에 전달하지 마십시오.

스크립트 (이 스킬의 scripts/ 디렉터리)

S=<base directory>/scripts로 설정하세요. 여기서 base directory는 이 스킬이 로드될 때 harness가 출력한 경로입니다(Base directory for this skill: …/tdd). 이 스킬이 대상 저장소의 .claude/skills/에 있든, 모든 저장소에서 사용할 수 있도록 ~/.claude/skills/에 심볼릭 링크되어 있든 동일한 스킬입니다 — 저장소 상대 경로를 가정하지 마십시오. 모든 leaf 메커니즘은 여기에 있으며 단위 테스트됩니다($S/tests/*.test.sh).

$S는 고정됩니다: $S/run-conductor.sh를 통해 실행된 실행은 스킬 스냅샷 — .tdd/issue-<n>-skill-commit에 기록되고 VERI_SKILL_COMMIT으로 모든 leaf에 내보내진 커밋 — 을 실행합니다. 클론의 현재 최신 팁이 아니므로, 작업 중 main에 PR이 병합되어도 실행 중인 지휘자 아래의 스크립트와 템플릿이 변경되지 않습니다(저장소의 README.md, 지휘자 실행의 스킬 스냅샷 참조). 모든 실행 supervise.sh가 수행하는 것은 또한 실행된 커밋을 이벤트 로그(.tdd/events.jsonl, 키 skill_commit)에 기록하므로, VERI_NEW_SKILL=1로 최신 스킬을 사용한 실행은 해당 파일 아래에서 sha가 변경됩니다.

모든 스크립트의 카탈로그(인수, 출력, 종료 코드)는 <base directory>/SCRIPTS.md에 있습니다. 현재 단계가 명시적으로 명시하지 않는 인수, 출력 또는 종료 코드를 가진 스크립트를 호출하기 전에 이를 읽으십시오.

leaf 실행 — 분리, 그런 다음 포그라운드에서 대기

아래의 모든 leaf 실행은 동일한 세 부분으로 구성됩니다 — 완료 파일 지우기, 분리 실행, 포그라운드에서 대기 — 각 단계에서 이를 전체적으로 설명합니다. 개발자 leaf(red, green, refactor, revise)는 감독자를 통해 직접 실행됩니다:

rm -f .tdd/<name>.exit .tdd/<name>.pid
VERI_TEST_CMD='<test-cmd>' VERI_ISSUE=<n> VERI_STEP="$(cat .tdd/issue-<n>.md | $S/ledger-steps.sh current)" $S/detach-leaf.sh $S/supervise.sh <name> <cmd> [args...] > .tdd/<name>.out 2> .tdd/<name>.err
$S/wait-leaf.sh <name>

먼저 rm -f를 수행합니다. 완료 파일은 단계가 아닌 사이클로 이름이 지정되기 때문입니다: 이전 단계의 red leaf가 남긴 .tdd/develop-red.exit는 다음 대기를 즉시 종료시키고, 이전 사이클의 코드를 사용하게 됩니다. 마찬가지로 .tdd/<name>.pid가 남아 있으면 오래 전에 사라진 감독자를 가리키며, 새 감독자가 자신의 pid를 쓰기 전에 2로 종료됩니다. 그런 다음 실행 — 분리 — detach-leaf.sh를 통해 호출이 즉시 반환되고 leaf가 자체 세션에서 실행됩니다. 그런 다음 포그라운드에서 wait-leaf.sh를 호출하여 leaf가 완료되고 코드와 함께 종료될 때까지 차단됩니다. 종료 3에서 leaf는 계속 실행 중이며 대기 예산만 소비했습니다: wait-leaf.sh를 필요한 만큼 다시 호출하십시오. 모든 포그라운드 wait-leaf.sh 호출은 max-seconds 인수 없이 Bash 도구의 최대 timeout 600000ms로 수행하십시오: 600초 포그라운드 제한이 한계이므로, 대기는 590초마다 한 번의 호출이 소요되며 그 이하로는 소요되지 않습니다. 0, 1 또는 2만 답변입니다. .tdd/<name>.out에서 leaf의 보고서를 읽으십시오 — 캡처된 stdout — 분리된 호출이 더 이상 제공하지 않는 실행기 stdout 대신 사용합니다.

leaf의 BASH_DEFAULT_TIMEOUT_MS 및 BASH_MAX_TIMEOUT_MS는 supervise.sh가 적용하는 유효 타임아웃(VERI_TIMEOUT, 없으면 실행의 .tdd/issue-<n>-timeout, 없으면 900초)을 따릅니다. 호출자가 설정하지 않은 경우 포그라운드 제품군이 Bash 도구의 기본값에서 차단되지 않습니다.

절대 nohup … &를 분리 대신 사용하지 마십시오: nohup은 leaf가 hang-up(SIGHUP)을 무시하도록 하지만, 호출한 Bash 호출의 프로세스 그룹에 남겨두므로 호출이 종료될 때 해당 그룹에 신호를 보내는 harness가 leaf를 함께 종료시킵니다. 그렇게 종료된 leaf는 완료 파일을 쓰지 않으므로 위의 대기는 영원히 종료 3을 반환하며, 이유를 알 수 없는 상태로 실행이 중단됩니다.

모든 leaf 실행 전에 중지가 요청되었는지 확인하십시오: $S/stop-requested.sh를 실행하십시오. 개발자 leaf와 검증자 모두에 대해 rm -f보다 먼저 실행합니다. 종료 3은 보류 중인 중지가 없음을 의미합니다: 정상적으로 실행합니다. 종료 0은 출력된 모드에 따라 분기됩니다 — at-ledger는 어쨌든 leaf를 실행합니다. 두 원장 커밋 사이의 모든 leaf는 다음 커밋으로 가는 중이기 때문입니다. at-leaf 또는 now는 아무것도 실행하지 않고 중지로 이동합니다: <base directory>/STOPPING.md를 읽고 실행합니다. 종료 2는 at-ledger 모드에서 중지로 처리합니다(해당 파일도 읽음), 요청을 읽을 수 없었음을 설명합니다: .tdd/stop이 존재하므로 누군가 이 실행을 중지하려고 한 것이며, 얼마나 긴급한지만 의심스럽습니다 — 틀릴 수 없는 모드는 아무것도 잃지 않는 모드입니다.

어떤 leaf의 종료 2를 killed-stuck으로 읽기 전에 $S/stop-requested.sh를 실행하십시오. 보류 중인 at-leaf 또는 now 중지는 supervise.sh가 종료 2로 실행을 거부하게 만들고(now 중지는 활성 leaf도 종료시킴), 따라서 대기는 멈춘 leaf와 정확히 동일하게 2를 반환합니다: 두 보류 모드 중 하나가 2를 설명하며, 실행은 이탈 대신 중지로 이동합니다: 그런 경우 <base directory>/STOPPING.md를 읽고 실행합니다. 다른 답변은 2를 아래 단계에서 읽는 방식대로 읽도록 남겨둡니다. 단, 먼저 하나의 확인을 수행합니다. 개발자 leaf와 모든 단계에 대해: .tdd/<name>.err에 감독자의 상태 줄 supervise[<name>]: killed-external이 있는 경우 leaf는 기록된 종료 없이 죽었고 veri가 죽이지 않았으므로 leaf를 다시 실행하지 말고, 원인 killed-external(시스템 오류)로 이탈하십시오: <base directory>/BAIL.md를 읽고 실행합니다. 다른 종료 2는 아래 단계에서 읽는 방식대로 읽도록 남겨둡니다.

이 절차가 이탈이라고 말할 때마다, 이탈하는 줄이 명명한 원인과 함께 <base directory>/BAIL.md를 읽고 실행하십시오.

지휘자는 사용자에게 질문하지 않으며, 절차의 다음 지시를 거부하지 않습니다. 무인 실행에 답변하는 사람이 없으므로, 질문으로 끝나는 턴은 진행 중인 것처럼 보이는 실행을 남깁니다. 질문하거나 거부하려면 대신 원인 needs-user(프로세스 오류)로 이탈하십시오 — 이는 절차 자체가 사용자에게 질문하라고 말하는 모든 경우(사전 점검 1의 더티 트리 중지 및 테스트 명령 감지의 종료 3 질문 등)를 포괄하며, 이탈 주석에 전체 질문을 작성하여 재개 또는 사람이 답변할 수 있도록 합니다(<base directory>/BAIL.md 읽기).

실행에는 VERI_TEST_CMD가 포함됩니다: 실행의 테스트 명령으로, 프롬프트가 아닌 환경을 통해 leaf에 도달하므로 검증자는 블라인드를 유지합니다. 실행 라인의 접두사이며 일회성 export가 아닙니다. 모든 Bash 호출은 새로운 셸이므로 사전 점검에서 설정된 것은 세 단계 후의 실행까지 유지되지 않습니다.

VERI_ISSUE 및 VERI_STEP도 같은 이유로 포함되며, 아래 당신의 표시가 지정하는 것과 동일한 두 값입니다: 감독자의 launch 및 exit 이벤트와 검증자의 verdict 이벤트는 event.sh의 모듈인 veri/event.py에 의해 작성되며, 감독자와 검증자에 의해 프로세스 내에서 호출되어 실행 컨텍스트를 환경에서 읽습니다. 다운스트림은 이를 제공할 수 없습니다 — leaf-role.sh는 leaf의 이름에서 행위자, 단계 및 시도를 파생하며, 어떤 이름도 해결 중인 이슈 또는 leaf가 있는 계획의 단계를 알려주지 않습니다 — 따라서 이를 생략한 실행은 해당 leaf의 모든 이벤트에 빈 issue 및 빈 step을 남기며, 독자는 타임스탬프를 step-start와 인터리빙하여 leaf를 단계에 귀속시킬 수 있습니다.

단계의 제목은 스크립트가 대신 읽을 수 있는 곳에 수동으로 입력되지 않고 데이터로 명령줄에 도달합니다. 큰따옴표는 아포스트로피를 중단하지만 확장은 중단하지 않습니다: 제목의 $, 백틱 또는 `

관련 스킬