본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 06. 04. 17:26

DAG TOML: 4개월간의 코드 리뷰 고통을 어떻게 기계 검증 가능한 계획 형식으로 전환했는가

요약

멀티 에이전트 개발 프로세스에서 발생하는 반복적인 코드 리뷰 문제를 해결하기 위해, 기계적 검증이 가능한 TOML DAG 형식의 구현 계획을 도입한 사례를 다룹니다. LLM 에이전트 간의 계획 불일치로 인한 리소스 낭비를 줄이고, 검증 테이블을 통해 리뷰 효율성을 높이는 방법을 설명합니다.

핵심 포인트

  • 멀티 에이전트 환경에서 발생하는 리뷰 반복 문제 분석
  • 기계적 검증기를 갖춘 TOML DAG 형태의 계획 도입
  • 리뷰 결과의 오류를 exit 1로 즉시 처리하여 효율성 증대
  • 검증 테이블을 통한 코드와 테스트의 매핑

이 기사의 모든 타임라인: 실패 데이터는 2025년 12월 – 2026년 3월의 것이며, 분석 단계는 2026년 4월 4일에 실행되었고, 첫 번째 DAG TOML은 2026년 4월 2일에 작성되었으며, 런타임은 2026년 4월–5월에 이어졌습니다.

문제점: LLM 에이전트들은 서로 맞지 않는 아름다운 계획을 작성한다

우리는 멀티 에이전트 (multi-agent) 개발 프로세스를 운영합니다. LLM 에이전트들 (Claude, Codex CLI, Gemini CLI)이 Rust 코드베이스 상에서 서로의 작업을 계획하고, 구현하며, 교차 리뷰 (cross-review)합니다. 모든 작업 결과물은 머지 (merge)되기 전에 최소 두 개의 서로 다른 모델에 의한 독립적인 리뷰를 거칩니다.

이 방식은 작동합니다. 하지만 2025년 말까지 변동성 (churn) 문제가 있었습니다. 작업이 **재리뷰 (re-review)**를 위해 계속 되돌아왔고, 되돌아올 때마다 여러 모델에 걸쳐 전체 리뷰 라운드가 소모되었습니다. 그래서 2026년 4월에 우리는 약간 특이한 시도를 했습니다. 우리의 리뷰 아카이브(약 2,400개의 리뷰 문서)를 데이터셋으로 취급하고 다음과 같이 질문했습니다: 왜 작업이 실제로 되돌아오는가?

이 기사는 해당 데이터셋에서 추출한 실제 사례 하나와, 그 과정에서 도출된 분류 체계(taxonomy), 그리고 해결책을 보여줍니다. 해결책은 바로 기계적 검증기 (mechanical validators)를 갖춘 TOML DAG 형태의 구현 계획입니다. 이를 통해 리뷰 결과의 한 부류가 일주일간의 반복 작업 대신 exit 1 (오류 종료)로 처리될 수 있게 되었습니다.

증거 A: 프로젝트 지속성 체인 (2025년 12월 5–6일)

해당 기능은 화려하지 않았습니다: persist_if_configured — 코드 인덱스 프로젝트의 인메모리 상태(레포지토리 인덱스, 파일 테이블, 심볼 인덱스)를 종료 시 디스크에 저장하고, 시작 시 이를 다시 로드하는 기능입니다. 이는 한 번의 리뷰로 끝나야 마땅한 종류의 작업이었습니다.

날짜가 모두 기록된 문서 기록은 다음과 같습니다:

  • 2025년 12월 5일 — 명세서(Spec) 작성 및 승인 완료. 전체 계획 패키지: 명세서, 설계, 구현 계획, 테스트 계획. 구체적 목표: 재시작 후 빠른 복구(warm restore), 5만 개 심볼 인덱스에 대해 750ms 미만의 지속(persist) 시간, 80% 이상의 모듈 커버리지.
  • 2025년 12월 5일 — 코드 한 줄을 작성하기 전, 세 가지 모델(Codex 3회, Gemini 2회, Claude 4회)을 통한 _9차례_의 구현 전 리뷰 반복(pre-implementation review iterations).
  • 2025년 12월 6일 — 구현 완료. 두 차례의 독립적인 구현 후 리뷰(post-implementation reviews). 두 리뷰 모두 '수정 요청(REQUEST CHANGES)'을 반환함.
  • 2025년 12월 6일 — 수정 반복, 두 번째 리뷰 라운드, 승인.

그 모든 계획을 거친 후, 12월 6일에 두 명의 리뷰어는 무엇을 발견했을까요?

심각도발견 사항
높음 (HIGH)복구 경로(restore path)가 모든 파일의 리포지토리 ID(repo ID)를 NONE으로 덮어씀 — 지속된(persisted) ID가 무시되어, 다시 로드된 상태가 해당 리포지토리들과 분리됨. 기능의 핵심 목적이 조용히 작동하지 않음. 코드 내의 TODO가 이를 인지하고 있었음.
...

두 리뷰어 모두 — 서로 독립적이며 서로 다른 모델 제품군임에도 — 동일한 최상위 발견 사항으로 의견이 모였습니다. 12월 6일의 두 번째 라운드는 각 발견 사항을 특정 코드 및 지정된 테스트와 매핑하는 검증 테이블(verification table)을 통해 모든 문제를 해결했으며, 당일 승인되었습니다.

여기서 불편한 사실이 있습니다: 계획은 철저했고 계획 리뷰도 철저했음에도 불구하고, 구현물은 핵심 기능이 작동하지 않고 경로 탐색(path-traversal) 취약점이 있는 상태로 배포되었습니다. 산문(prose)으로 작성된 계획은 구현을 구속하지 못합니다. 산문에 대한 리뷰는 다시 실행할 수 없습니다.

아카이브 조사 (2026년 4월 4일)

우리는 2025년 12월부터 2026년 3월까지에 걸친 7개의 전체 "반복 체인(iteration chains)"(초기 요청 → 차단 리뷰 → 재리뷰 → 승인)과 대조군으로서 9개의 깨끗한 단판 승인(one-pass approvals) 사례를 분석했습니다:

  • 2025년 12월 — 프로젝트 지속성 (위 내용 참조); 4개의 언어 플러그인 전반에 걸친 플러그인 다듬기 ("production-ready"라고 주장했으나 테스트 매트릭스 결과는 달랐음); 테스트가 존재하지만 실패할 수 없었던 후속 사례 — 기능이 부재함에도 불구하고 비엄격한 단언(non-strict assertions)이 통과됨
  • 2025년 12월~2026년 1월 — **13번의 반복(iterations)**이 소요된 개인정보 민감형 계획 팩; 대부분 단일한 정형 스키마(canonical schema)가 존재하지 않고 문서마다 정의가 어긋났기 때문임
  • 2026년 2월 10일 — MUST/SHOULD 충돌로 인해 차단된 정책 표준, 그리고 작업 지침이 보안 제어(security controls)를 무시할 수 있게 만든 우선순위 모델
  • 2026년 2월 — 자체 상태 문서에는 여전히 테스트 실패가 기술되어 있음에도 "완전한 지원"을 주장한 C++ 언어 기능
  • 2026년 3월 10일 — 누락된 아티팩트 패밀리(artifact family)로 인해 6라운드와 7라운드를 허비한 계획 팩, 그리고 명시된 정렬 규칙 없이 "정렬이 결정론적(deterministic)임"이라고 주장한 사례

모든 재리뷰(re-review) 원인은 다음 6가지 범주 중 하나에 해당했습니다:

  1. 아티팩트 완전성 누락 (Missing artifact completeness) — 필요한 문서가 누락되었으며

첫 번째 DAG TOML은 2026년 4월 2일에 작성되었으며, 추출된 템플릿 저장소(repo)와 검증기(validators)는 아카이브 분석과 같은 날인 4월 4일에 뒤따라 나왔습니다. 이 형식은 모든 계획(plan)의 주장을 다음 세 가지 중 하나로 만듭니다: 필수 필드 (required field), 재계산 가능한 단언 (recomputable assertion), 또는 게이트 상태 전이 (gated state transition).

계획은 다음과 같은 단위(units)의 집합입니다:

[units.U02]
name = "extract-initial-chain-set"
layer = 1
...

acceptance, constraints, failure_modes, critical_decisions단위별로 필수(required) 항목입니다. 카테고리 2 — 명시되지 않은 계약(unstated contracts) — 은 리뷰어가 부재를 통해 인지해야 하는 대상이 아니라, 누락된 필수 필드가 됩니다.

그 다음, 계획은 자체적인 파생 속성(derived properties)을 선언해야 합니다:

[computed]
entry_points = ["U01"]
leaf_nodes = ["U05"]
...

그리고 여기에 모든 비결이 있습니다: 약 500줄 규모의 Python 검증기(validator)가 단위(units) 테이블로부터 해당 주장들을 모두 재계산하고 이를 비교(diff)합니다.

  • blocksdepends_on의 정확한 역(inverse)이어야 합니다. 의존성(dependency)의 한쪽을 수정하고 다른 쪽을 잊어버리면, 정확한 불일치 메시지와 함께 검증이 실패합니다.
  • 순환(cycles)이 감지되며 실제 순환 경로로 출력됩니다.
  • 모든 ART: 아티팩트(artifact)는 정확히 하나의 생성자(producer)를 가져야 합니다. 1월에 13번의 반복(iteration)을 초래했던 "정식 정의의 소유권(who owns the canonical definition)" 표류 문제는 이제 한 줄짜리 에러가 됩니다.
  • 모든 consumes는 존재하는 produces와 일치해야 합니다. 숨겨진 의존성은 구멍(holes)으로서 드러납니다.
  • 의존하는 대상(depender)은 반드시 그 의존 대상(dependencies)보다 엄격하게 높은 계층(layer)에 위치해야 합니다. 과장된 병렬성(parallelism)은 실패 처리됩니다.
  • 선언된 임계 경로(critical path)는 실제 엣지(edge)들의 체인이어야 하며, 진입점(entry)에서 시작하여 종착점(leaf)에서 끝나야 하고, 동시에 실제 최장 가중치 경로(toposort DP를 통해 재계산됨)와 일치해야 합니다. 일정에 대한 환상은 실패합니다.
  • 파일을 공유하는 단위(units)들은 충돌 그룹(conflict groups)으로 선언되어야 합니다. 동일한 파일을 편집하려는 두 개의 병렬 에이전트는 계획 단계에서 포착됩니다.
  • files_modify 경로는 저장소(repo)에 존재해야 합니다. 상상 속의 코드베이스를 대상으로 작성된 계획은 실패합니다.
  • 플레이스홀더(<fill-in-later>)는 거부됩니다.

잘못된 계획(plan)에 대한 주장은 더 이상 리뷰어의 주관적인 판단 영역이 아닙니다. 그것은 단 한 줄의 diff(차이)를 가진 실패한 단언(assertion)이 됩니다.

리뷰에서 무엇이 바뀌었는가 (2026년 4월 4일, 첫 실전 사용)

이 형식이 도입된 지 이틀 만에, 첫 번째 DAG 리뷰를 거친 계획인 플러그인 비용 계층화(plugin cost-tiering) 기능이 통과되었습니다. 리뷰어의 검토 범위는 TOML 파일 그 자체였으며, 결과는 승인(APPROVED), 1회 통과, 차단 이슈 없음(zero blocking issues) 이었습니다. 네 개의 리뷰어 코멘트 모두 구조적 결함이 아닌, 실제 도메인 리스크(기존 manifest 폴드백 세맨틱, ID 안정성)에 관한 것이었습니다.

이것이 바로 의도한 대로 작동하는 메커니즘입니다. 리뷰어들이 여러 차례의 리뷰 라운드를 소모하며 질문하던 구조적인 문제들 — 무언가 누락되었는가? 의존성이 타당한가? 실제로 무엇이 병렬로 실행될 수 있는가? 타임라인 주장이 유효한가? — 에 대한 답을 검증기(validator)가 미리 제공합니다. 리뷰어의 주의력은 오로지 난도가 높은 세맨틱(semantic) 발견 사항에만 집중되며, 이것이야말로 인간과 프런티어 모델(frontier models)이 리뷰 라운드를 할애해야 할 유일한 영역입니다.

그리고 12월의 버그 클래스는? 게이트(Gates)와 증거 매트릭스(evidence matrices)

DAG 검증기만으로는 12월에 발생한 경로 탐색(path traversal) 취약점을 잡아낼 수 없었을 것입니다. 그것은 카테고리 6, 즉 경계가 명시되었으나 코드에서 강제되지 않은(boundary stated but not enforced in code) 사례입니다. 이를 겨냥한 두 가지 보조 형식이 있습니다:

  • 계약 선언(Contract declarations): 파일 시스템, 순서, 호환성 또는 폴드백(fallback)에 영향을 미치는 모든 계획은 계약을 명시적으로 선언해야 합니다(경로 루트 제한, 탐색 처리, 원자성(atomicity)). 그리고 각 계약은 무엇이 이를 검증하는지 명시합니다.
  • 증거 매트릭스(Evidence matrices): "결함 해결됨(finding resolved)" 또는 "기능 완료(feature complete)"라는 주장은 주장 ID(claim ID)를 증거 경로(evidence path), 선언된 범위(declared scope), 그리고 알려진 제외 사항(known exclusions)과 결합해야 합니다. 그리고 검증기는 해당 증거 파일이 존재하는지 확인합니다. 실패할 가능성이 있는 증거를 명시하지 않고서는 기계적으로 "해결됨"이라고 말할 수 없습니다. (실패할 수 없었던 2025년 12월의 테스트를 기억하시나요? 이것이 바로 그 실패 모드를 제거하는 방식입니다.)

12월 체인의 두 번째 리뷰 — 통과되었던 그 리뷰 — 는 이미 비공식적인 증거 매트릭스(evidence matrix)였습니다. 즉, 이전의 모든 발견 사항이 특정 코드 라인 및 지정된 테스트와 매핑되어 있었습니다. 이 형식(format)은 해당 테이블을 필수적으로 만들고, 기계가 검증할 수 있게 하며, 리뷰가 요청된 후 2차 단계에서 생성되는 대신 리뷰 요청 _전_에 반드시 작성되도록 만듭니다.

그 이후의 행보 (2026년 4월–5월)

정적 검증(Static validation)은 누군가가 실행할 때만 문제를 포착할 수 있습니다. 2026년 4월–5월에 동일한 네 가지 불변량(invariants)이 데이터베이스 기반의 런타임(runtime)으로 이동했습니다. 에이전트(agents)가 TOML을 한 번 임포트(import)하면, 모든 상태는 DB에 저장됩니다 —

  • 모든 의존성(dependency)이 done 상태일 때만 유닛(unit)이 에이전트에게 제공됩니다.
  • 상태 변경은 문자열 편집이 아닌, 이력이 남는 가드된 전이(guarded transitions)로 이루어집니다.
  • 역방향 엣지(inverse-edge), 단일 생산자(single-producer), 소비-생산자 관계(consumes-has-producer), 그리고 레이어 순서(layer-ordering) 불변량이 **변이 시점(mutation time)**에 강제됩니다.
  • 준비 게이트(readiness gates)는 쿼리(query)입니다:

여기에 설명된 템플릿 형식, 검증기(validators) 및 런타임(runtime)은 내부 플랫폼의 일부입니다. 스키마 아이디어(필수 계약 필드(required contract fields), 재계산된 [computed] 섹션, 단일 생산자 아티팩트(single-producer artifacts), 증거 행렬(evidence matrices))는 에이전트 주도(agent-driven) 또는 인간 프로세스에 맞춰 쉽게 적용할 수 있습니다.

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0