직접 실행해보고 나서야 발견할 수 있었던 버그들
요약
Claude Code 스킬 세트인 ArDD를 개발하며 겪은 실제 실행 과정에서의 버그와 설계 오류를 다룹니다. 이론적인 설계와 달리 git 워크트리 생성 시 상태 불일치 및 설정 변경 문제가 발생함을 확인하고, 이를 검증하는 과정의 중요성을 강조합니다.
핵심 포인트
- 설계 단계의 이론과 실제 실행 결과는 다를 수 있음
- git 워크트리 생성 시 로컬 커밋이 반영되지 않는 상태 불일치 문제 발견
- 하네스 동작에 따라 git 설정이 예기치 않게 변경되는 버그 경험
- 가정하지 말고 반드시 실제 실행을 통해 검증해야 함
지난주에 저는 자기 통제력이 부족하여 이를 보완하기 위한 도구를 만든 경험에 대해 글을 썼습니다. 바로 ArDD인데, 이는 에이전트가 한 줄의 프롬프트(prompt)로부터 코드까지 곧바로 자유롭게 날뛰도록 내버려 두는 대신, 아티팩트(artifacts), 계획(plans), 작업(tasks)을 통해 프로젝트를 밀어붙이는 Claude Code 스킬 세트입니다. 저는 그 포스트를 마치 이것이 그저 코스튬을 입은 훌륭한 CLAUDE.md일 뿐인지 자문하며 마무리했습니다.
(이 글을 쓰는 시점 기준으로) ArDD를 실행하는 사람은 저뿐이기에, 다음에 이어질 내용은 개인적인 부분을 제외한 일종의 결정 기록(decision record)이라고 볼 수 있습니다. 하지만 이 과정이 저에게 가르쳐준 것은 제가 만든 단일 도구를 넘어 일반화될 수 있는 것이었으며, 그것이 일반화되는 방식이 바로 제가 이 글을 쓰는 이유입니다. 즉, 그것은 실제로 실행해보고 실제로 어떤 일이 일어났는지 살펴보기 전까지는 보이지 않았으며, 코드를 아무리 오래 읽어도 결코 찾아낼 수 없는 것이었습니다.
잘못된 위치에 존재했던 상태 (State)
ArDD의 핵심 루프는 계획(plan) → 작업(tasks) → 구현(implement)이며, 구현 단계에서는 자체적인 git 워크트리(worktree)에서 실행되는 하위 에이전트(subagent)에게 작업을 넘길 수 있으므로, 단일 스레드(single-threaded) 에이전트를 계속 지켜보고 있을 필요가 없습니다. 원래 설계는 인수인계(handoff)를 신중하게 처리하려고 노력했습니다. 위임하기 전에, 위임받은 워크트리가 브랜치(branch)를 생성할 때 안정적이고 이미 반영된(landed) 지점이 필요하다는 이론에 따라, 계획(plan)과 작업 목록(task list) 같은 다소 거친 상태(coarse state)를 메인 브랜치에 커밋(commit)했습니다.
그 이론은 틀렸으며, 저는 설계를 믿는 대신 실제 스모크 테스트(smoke test)를 실행해보고 나서야 이 사실을 알게 되었습니다. 저는 일회용 계획과 작업 파일을 커밋하고, 새로운 워크트리의 하위 에이전트에게 작업을 위임한 다음, 실제로 그 워크트리에 무엇이 포함되어 있는지 살펴보았습니다.
일치하지 않았습니다. 워크트리(worktree)는 origin/main에서 분기되어 있었으며, 제가 방금 로컬에서 생성한 두 개의 커밋 중 어느 것도 포함하고 있지 않았습니다. 하네스(harness)는 로컬 HEAD가 아닌 원격(remote) 추적 브랜치로부터 워크트리를 생성하며, 이는 스킬(skill)의 자체 설명만으로는 제가 제어할 수 없는 동작입니다. 설상가상으로, 이슈 트래커(issue tracker)에 따르면 하네스 버전에 따라 이 동작의 방향이 뒤바뀌기도 합니다. 따라서 "항상 최신 상태라고 가정하라"는 말은 "항상 동기화되어 있다고 가정하라"는 말만큼이나 틀린 말입니다. 유일하게 올바른 태도는 다음과 같습니다: 검증하라, 절대 가정하지 마라.
동일한 실행 과정에서 여전히 설명할 수 없는 현상이 나타났습니다. 워크트리를 생성하는 과정에서 저의 기본(primary) 체크아웃의 git 설정이 core.bare = true로 바뀌어 버렸는데, 이는 이를 인지하고 수동으로 되돌리기 전까지 해당 체크아웃에서의 일반적인 git 사용을 불가능하게 만듭니다. git worktree add 명령은 실행된 체크아웃의 설정을 건드릴 이유가 없으며, 저는 그 메커니즘을 끝내 찾아내지 못했습니다. 그래서 저는 이를 고친 척하지 않았습니다. 대신 코디네이터(coordinator)가 위임된 실행이 끝날 때마다 설정 변경 여부를 확인하고, 다시 발생하면 경고를 보내도록 했습니다. 설명되지 않는 하네스 버그에 대해 함정을 설치하는 것은 해결책(fix)이 아니며, 저 또한 그것을 해결책이라 부르지 않을 것입니다. 하지만 침묵하는 미지의 문제보다는 시끄럽게 알려진 미지의 문제가 낫습니다.
분기(divergence) 문제를 해결하기 위한 방법은 worktree-align.sh라는 작은 스크립트를 사용하는 것입니다. 위임된 하위 에이전트(subagent)는 필수적인 첫 번째 작업으로 이 스크립트를 실행합니다: 로컬 기본 브랜치를 새로운 워크트리 브랜치로 패스트 포워드(fast-forward)하고, 만약 깨끗한 패스트 포워드가 아니라면 진행을 거부합니다. 엉망이 된 분기를 화해시키려는 시도는 하지 않습니다. 그저 통과하거나 실행을 중단하는 결정론적인(deterministic) 확인 절차를 거칠 뿐입니다.
하지만 두 가지 해결책보다 더 오래 살아남은 변화는 그들이 강제한 재설계(redesign)였습니다. 기존의 흐름은 작업을 정당화할 근거가 존재하기도 전에 코디네이팅 상태(coordinating state)를 커밋했기 때문에, 서로 일치하지 않을 수 있는 두 개의 복사본을 남겼습니다. 이제 상태(state)는 생성된 브랜치를 따라 이동하며, 병합(merge) 시점에 코드와 함께 도착합니다. 이는 두 요소가 반드시 함께 도착하도록 보장되는 유일한 순간입니다. 작업 파일(tasks file)의 체크박스, ready → in-progress → completed 상태, 기능 레지스터(feature register)의 tasked → implemented 전환 모두 메인(main) 브랜치에서 미리 대기하는 것이 아니라, 작업 브랜치(work branch) 상에 존재합니다.
그 결과로 얻는 이점은 이제 실패한 실행(dead run)이 아무런 작업도 수행하지 않은 상태(no-op)가 된다는 점입니다. 위임된 워크트리(worktree)가 작업 도중 중단되더라도, 메인(main) 브랜치는 그 미완성 상태를 결코 보지 못했으므로 조정(reconcile)할 것이 아무것도 남지 않습니다. 메인 브랜치는 이전과 동일한 상태를 유지하며, 이는 여전히 유효합니다. 병합되었거나, 혹은 되지 않았거나 둘 중 하나입니다. 이 속성이 바로 핵심이며, 병렬 에이전트(parallel-agent) 도구를 구축하려는 누구에게라도 제가 권하고 싶은 단 한 가지는 이것입니다: 작업을 수행하기 전에 조정 상태(coordinating state)를 구체화(materialize)하지 마십시오. 중단된 에이전트가 정리해야 할 엉망진창인 상태를 남기지 않도록 설계하십시오.
기저에 깔린 태도
첫 번째 포스트에서 언급한 프레임워크에 대한 비판은 여전히 유효합니다. ArDD는 여러분을 대신해 아키텍처적 판단을 내려주지 않으며, 앞으로도 그럴 것입니다. 달라진 점은 더 좁은 범위에 있습니다. git 브랜치나 워크트리(worktree) 생명주기(lifecycle)와 같이 공유되고 변경 가능한 상태(shared, mutable state)를 다루는 부분들은 이제 제가 주니어 엔지니어에게 요구하는 방식대로 구축되었습니다: 모호한 것이 있다면 거부하고, 절대 추측하지 마십시오. 그리고 근본 원인을 해결할 수 없는 경우에는, 최소한 실패가 조용히 지나가지 않고 명확하게 드러나도록(loud) 트리와이어(tripwire)를 설치하십시오.
이러한 태도가 실제 핵심적인 교훈이며, 이는 지배적인 실패 모드(failure mode)가 '조용히 이루어지는 확신에 찬 잘못된 추측'인 에이전트 도구 분야에 특화된 것입니다. 이 중 그 어떤 것도 코드를 들여다보는 것만으로는 파악할 수 없었습니다. 제가 시킨 대로 하는 것이 아니라, 실제로 실행해보고 그것이 무엇을 하는지 관찰하는 과정이 필요했습니다. 그리고 결국, 이것이야말로 프레임워크가 저를 위해 해줄 수 없는 유일한 부분이기도 합니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기