isopod — 코드를 쓰는 에이전트가 아니라, 조직의 개발 프로세스를 굴리는 하네스
문서 작성 2026-08. 회사명은 밝히되 제품·인명은 익명화했다. 본문의 모든 수치는 실측이며 재현 명령은 §10 부록에 있다.
1. Executive Summary
isopod 는 자연어 스펙 문서를 의존 그래프를 가진 작업 카드로 변환하고, 그 카드가 소진될 때까지 다중 에이전트를 무인으로 굴려 검증된 커밋을 만드는 하네스다. B2B 3D 디지털트윈 SaaS 기업 Cupix의 대화형 AI 어시스턴트 서비스 개발에 약 2개월간 투입해 운영했다.
측정된 결과는 다음과 같다.
- 에이전트 파이프라인이 73장의 작업 카드를 실행해 70장을 검증 통과시켰다(95.9%). 사람이 직접 구현한 8장은 이 수치에서 제외했다.
- 그 과정에서 385회의 에이전트 패스가 돌았고, 실행 층에서 사람이 승인한 횟수는 0이다.
- 실측 비용은 $2,754, 에이전트 연산 시간은 144.4시간, 최대 동시 실행은 13 패스였다.
- 관리 대상 SSOT 는 카드 200장 / 의존 엣지 438개 규모였다.
"에이전트한테 개발해달라고 시키는 것"과 무엇이 다른가
코딩 에이전트는 한 건의 작업을 최적화한다. 조직은 지속되는 프로세스를 굴려야 한다. 실무에서 중요하게 보는 다섯 가지는 대부분의 에이전트 하네스가 아예 다루지 않는 축이고, 이것들을 하나의 의존 그래프 위에서 한꺼번에 처리하는 도구는 조사 범위(§5, 인접 도구 6종)에서 없었다.
| 실무가 요구하는 것 | 일반 코딩 에이전트 · SDD 툴 | isopod | 근거 |
|---|---|---|---|
| 스펙을 엄밀히 고정하고, 구현이 거기 부합함을 증명 | 스펙은 산문 프롬프트. 부합 여부는 "테스트가 초록"으로 갈음 | 스펙 스키마가 상태 머신을 필수로 요구(states·events·transitions, 불법 전이까지 열거) → 수용 기준마다 반증 예시 → 테스트가 전이를 겨냥 → 4계층 게이트를 전부 결정적 프로그램이 판정 | §3-4, §4-2 |
| 진행 상황을 언제든 보고 | 대화창 스크롤백이 유일한 상태 | 카드 200장이 버전 관리되는 SSOT, 7단계 상태 머신, 트래커 미러. "무엇이 어디까지"가 파일과 트래커 양쪽에 상시 존재 | §3-3, §4-3 |
| 다른 팀과의 협업 | 없음 (혼자 도는 전제) | 외부 팀 카드는 읽기 전용 미러 — 소유권이 데이터 모델의 1급 개념, 쓰기 범위를 필드 단위로 제한, 코드가 예외를 던져 강제. 역방향 기대는 consumer_contract 로 박제 |
§4-3 |
| 사람의 정책 결정을 기다리는 구간 | 없거나, 전부 승인 게이트라 파이프라인이 사람 속도로 내려감 | 게이트를 층으로 가름 — 요구사항 층에만 사람, 실행 층 0개. 미결 정책은 게이트 카드가 그래프 노드로 존재해, 그걸 의존하는 카드만 정확히 대기한다 | §3-2, §4-6, §7-2 |
| 도중에 바뀐 스펙의 결정론적 추적 | 프롬프트를 다시 씀 = 이전 상태 소실 | 상태별 편집 규칙 + 마이그레이션 카드(배포된 카드를 고치지 않고 이전 경로를 새로 만든다). 폐기 시 역방향 정리 / 신규 결정 시 정방향 주입 / 결정 대체 시 엣지 재검토 3축이 감사 함수 | §4-3, §7-4 |
| 이 전부가 한 시스템 안에서 | 인접 도구 6종 중 트래커 연동 내장 1종, 다중 팀 소유권 모델 0종 | 위 다섯이 하나의 의존 그래프 위에서 동작하고, 그 위를 에이전트가 소진까지 굴린다 | §5 |
이 표의 왼쪽 열이 이 문서의 논지다 — isopod 의 차별점은 "에이전트가 더 똑똑하다"가 아니라 에이전트에게 개발을 시킬 때 실무가 실제로 요구하는 주변 조건을 전부 1급으로 모델링했다는 데 있다.
이 하네스가 풀려고 한 문제의 성격
이 수치들보다 중요한 것은 어떤 조건에서 굴렀는가다. 실험실 조건이 아니었다.
- 트래커 기반으로 협업하는 3개 팀(내부 2 + 외부 1)의 작업이 한 그래프 안에 있었다. 외부 팀 카드는 그쪽 트래커가 진실이므로 읽기 전용 미러로 다뤄야 했고, 그 소유권 경계는 문서 규약이 아니라 코드가 예외를 던져 강제한다.
- 스펙이 계속 바뀌었다. 카드의 20.5%(41장)가 스코프 컷으로 폐기됐고, 결정(ADR) 17건이 누적되며 서로를 대체했다. 그래서 이 하네스의 핵심은 "카드를 만드는 법"이 아니라 "이미 배포된 카드의 전제가 바뀌었을 때 이력을 위조하지 않고 옮기는 법" 이다.
- 그 결과 설계의 중심 질문이 "어디까지가 정책 판단(LLM)이고 어디부터가 기계인가" 가 됐다. 이 경계를 어디에 긋고 어떻게 코드로 지켰는지가 §4 의 내용이다.
조사한 인접 도구 6종 중 트래커 연동이 내장된 것은 1종뿐이고, 다중 팀 소유권 모델을 가진 것은 없었다(§5). 이 축이 isopod 가 실무 조건에서 얻은 차이다.
먼저 밝히는 한계
done 은 머지 직후 스모크로 런타임 진실을 받지만 그 실패가 상태 전이를 막지는 않고(§9-1), 게이트 면제는 막지 않고 세는 설계이며(§9-2), 이식은 1회 실증됐지 경제성이 증명된 것은 아니고(§9-3), 하네스 제작 비용과 모델 대비 하네스 기여도는 측정하지 못했다(§9-4). 표본은 단일 프로젝트다.
또한 이 문서가 인용하는 95.9% 는 아래 §9-2 의 검증 결함들이 수정되기 전에 얻은 수치다. 수정과 면제 계측은 앞으로를 담보하지 과거 70장을 소급해 강화하지 않는다 — 이 구분을 문서 전체에서 유지한다.
2. 문제 정의 — 왜 만들었나
2-1. 스펙과 구현 사이의 간극
코딩 에이전트의 산출물이 "컴파일은 되는데 의도를 미묘하게 빗나가는" 문제는 2025년 이후 업계가 공통으로 지목한 결함이다. 이에 대한 산업 표준 대응이 spec-driven development(SDD) — 자연어 스펙을 임시 계획서가 아니라 구현을 지배하는 SSOT 아티팩트로 승격시키는 방식이다.
그런데 SDD 도구들이 풀지 못했다고 스스로 기록한 문제가 있다: 조직 규모에서 스펙을 관리하는 문제. 구체적으로 (a) 스펙과 코드가 갈라졌을 때 어느 쪽이 진실인지 판정하는 법, (b) 이미 배포된 스펙을 고칠 때의 이력 보존 규약이다.
isopod 가 겨냥한 지점이 정확히 여기다. 대상 프로젝트는 스펙 문서가 이미 존재했고, 외부 협업 팀(알고리즘 팀)이 별도 트래커로 자기 작업을 관리했으며, 결정 사항(ADR)이 누적되면서 배포된 카드의 전제가 바뀌는 일이 반복됐다.
2-2. 에이전트 자율성의 신뢰 문제
두 번째 동기는 어디까지 맡길 수 있는가다. 자율 실행을 늘리면 처리량이 오르지만, "에이전트가 완료했다고 말한 것"과 "실제로 동작하는 것" 사이의 간극이 그대로 리스크가 된다.
업계 실측이 이 문제의 규모를 보여준다 — 다중 에이전트 실패 사례 1,642건을 분류한 연구에서 가장 큰 두 실패 유형이 step repetition 15.7% 와 unaware of termination conditions 12.4% 였다. 합쳐서 관측된 실패의 1/4 이상이 "언제 멈출지 모르는 것"이다. 동시에 premature termination 6.20% 가 공존한다 — 즉 반복 상한 하나로는 양방향을 동시에 못 잡는다.
그래서 isopod 의 설계 질문은 "에이전트를 얼마나 똑똑하게 만들까"가 아니라 "무엇을 증거로 인정하고, 언제 멈추고, 사람을 어디에 둘 것인가" 였다.
2-3. 그리고 이 조건들은 동시에 온다
앞의 두 문제는 도구 하나로도 부분적으로 다뤄진다. 실무가 어려운 진짜 이유는 다섯 가지가 동시에 성립하기 때문이다. 대상 프로젝트에서 실제로 겹쳐 있던 조건이다.
- 스펙이 명시적으로 존재하고, 구현이 거기 부합했음을 증명해야 한다. 스펙 문서가 이미 있었고, "에이전트가 알아서 잘했다"가 아니라 어느 수용 기준이 어느 테스트로 덮였는지가 추적 가능해야 했다.
- 진행 상황을 언제든 답할 수 있어야 한다. 트래커가 협업의 기준면이므로 "지금 무엇이 어디까지 갔는가"는 대화 로그가 아니라 조회 가능한 상태여야 한다.
- 다른 팀의 작업이 우리 의존 그래프 안에 있다. 그런데 그 팀의 진실은 그쪽 트래커에 있다 — 노드로 두면 편집 가능해지고, 편집하는 순간 남의 팀 상태를 위조하는 것이 된다.
- 어떤 카드는 사람의 정책 결정 전에는 진행할 수 없다. 미결 결정 위에서 코드를 커밋하면 그 코드는 결정이 뒤집히는 순간 전부 낭비다. 동시에, 대기가 파이프라인 전체를 멈춰서도 안 된다.
- 스펙이 도중에 바뀐다. 카드의 20.5%(41장)가 스코프 컷으로 폐기됐고, 결정 17건이 누적되며 서로를 대체했다.
이 중 하나만 빠져도 설계는 훨씬 단순해진다. (3)이 없으면 소유권 모델이 통째로 불필요하고, (5)가 없으면 상태별 편집 규칙도 마이그레이션 카드도 필요 없으며, (4)가 없으면 게이트를 층으로 가를 이유가 없다. isopod 의 복잡도 대부분은 다섯이 동시에 성립하는 조건에서 나왔고, 그 조건은 개인 개발자용 도구가 만날 일이 없다(§5).
3. 시스템 아키텍처
3-1. 하네스와 프로젝트의 분리
하네스 코드와 프로젝트 데이터가 분리돼 있고, 코드가 실제로 쓰이는 대상 저장소는 또 다른 곳이다. 하네스는 대상 저장소를 가리킬 뿐 그 안에 살지 않는다.
flowchart LR
subgraph H["isopod 하네스 (이식 대상)"]
Core["core/<br/>런타임 드라이버 · 오케스트레이터"]
Mod["modules/<br/>SSOT 도구 · 어댑터"]
Sh["shared/<br/>공용 프리미티브"]
Skills["skills/<br/>슬래시 커맨드 표면"]
end
subgraph P["프로젝트 인스턴스 (프로젝트 소유)"]
Cfg["설정 파일<br/>경로·트래커·팀·임계값 SoT"]
SSOT["SSOT 카드 200장<br/>+ 의존 그래프"]
Spec["스펙 문서<br/>(사람 소유, 읽기 전용)"]
RT["런타임 상태<br/>(재생성 가능)"]
end
subgraph T["외부"]
Target["대상 저장소<br/>(쓰기 대상)"]
Tracker[("트래커<br/>Jira / noop")]
Ext["외부 팀 저장소<br/>(읽기 전용)"]
end
Cfg --> Mod
SSOT --> Mod
Mod -->|"roll:go"| Target
Mod -->|"미러"| Tracker
Ext -.읽기 전용.- Mod
Core -->|"runAgent()"| Backend["에이전트 백엔드 어댑터"]
Backend -->|spawn| CLI[("에이전트 CLI")]
3-2. 3중 중첩 루프
파이프라인은 선형이 아니다. 관측 → 결정 → 실행 → 검증 이 서로 다른 시간축에서 겹쳐 돌고, 바깥 루프의 "실행"은 안쪽 루프를 소진까지 굴리는 것이다.
flowchart TB
subgraph L2["L2 · 요구사항 루프 (일~주) — 사람 게이트 여기"]
direction LR
O2["관측<br/>스펙 변경·버그·스코프 컷"] --> D2["결정<br/>anchor · 카드 초안"]
D2 --> A2["실행<br/>카드 발행 → 프론티어"]
A2 --> V2["검증<br/>관측 게이트 (사람)"]
V2 --> O2
end
subgraph L1["L1 · 프론티어 루프 (시간~일) — 무인"]
direction LR
O1["관측<br/>실행 가능 집합 계산"] --> D1["결정<br/>실행 순서"]
D1 --> A1["실행<br/>sweep 사이클"]
A1 --> V1["검증<br/>머지 검증"]
V1 --> O1
end
subgraph L0["L0 · 스토리 루프 (분~시간) — 무인"]
direction LR
O0["관측<br/>현실 스냅샷"] --> D0["결정<br/>spec 에이전트"]
D0 --> A0["실행<br/>구현"]
A0 --> V0["검증<br/>4계층 게이트 · 리뷰"]
V0 --> O0
end
A2 -.위임.-> O1
A1 -.위임.-> O0
V1 -.소진 보고.-> V2
사람이 치는 명령은 8종이다: anchor → story:go → (검토) → story:jump → roll → jump → (PR 리뷰·머지) → land → gate-release. 이 중 L0/L1 구간에는 승인 게이트가 하나도 없다.
3-3. 카드 상태 머신
작업 카드는 파일 하나당 하나이고 버전 관리된다. 상태마다 편집 가능성 규칙이 다르다.
stateDiagram-v2
[*] --> drafted
drafted --> planned: 검토 통과
planned --> published: 트래커 발행
published --> done: 머지 검증 통과
published --> deleted: 스코프 컷
planned --> deleted: 스코프 컷
drafted --> deleted: 스코프 컷
done --> [*]
deleted --> [*]
note right of drafted
자유 편집
end note
note right of published
조건부 편집
(의도 강화만, 재발행 필수)
end note
note right of done
편집 금지 (규약)
배포 스냅샷
end note
배포된 카드의 목적지를 바꿔야 하면 원본을 고치지 않고 마이그레이션 카드를 새로 만들어 의존으로 연결한다. 이력을 덮어쓰지 않고 이전 경로를 별도로 남기는 방식이다.
3-4. 검증 게이트 스택
한 스토리는 5개 페르소나를 순차로 통과한다. 각 페르소나는 읽기 권한이 다르다.
flowchart LR
R["현실 스냅샷"] --> S["spec<br/>수용 기준 정의"]
S --> P["plan<br/>작업 분해"]
P --> T["test-author<br/>실패하는 테스트 선작성"]
T --> I["impl<br/>구현 (테스트 수정 불가)"]
I --> G{"4계층 게이트"}
G -->|통과| RV["review + security"]
G -->|실패| I
RV -->|통과| C["커밋 (N→1 압축)"]
RV -->|reject| I
C --> PR["PR (합성 브랜치 재검증 후)"]
PR --> M["사람 리뷰 · 머지"]
M --> LD["land: 머지 검증 → 상태 전이 → 트래커 동기화"]
4계층 게이트의 판정자는 전부 결정적 프로그램이다.
| 계층 | 무엇을 보나 | 판정자 |
|---|---|---|
| L1 정적 골격 | 선언만 하고 안 쓴 의존, 상수 반환 스텁 | TS 컴파일러 API |
| L2 변이 | 테스트가 실제로 결함을 잡는가 | AST 변이 + 테스트 러너 |
| L3 골든 I/O | 이식 시 입출력이 보존되는가 | JSON deep-diff |
| L4 런타임 수용 | 실제 시나리오가 도는가 | 실제 프로세스 spawn |
여기에 red-baseline(구현 전 테스트가 실패함을 증명)이 앞단에 붙는다. 그 근거는 에이전트의 진술이 아니라 러너 자신이 돌린 subprocess 의 exit code 다 — 테스트가 실패해야(exit ≠ 0) red-baseline 이 통과로 기록된다.
4. 기술 하이라이트
업계 패러다임 15종을 조사해 각각에 isopod 구현을 대응시켰다. 여기서는 판별력이 높은 7개를 다룬다. 각 절 끝의 "과장 금지선"은 조사에서 나온 반증이며, 지우지 않았다.
4-0. 설계의 중심 — LLM 이 허용되는 자리와 허용되지 않는 자리
이 하네스의 모든 설계 결정은 한 질문으로 수렴한다: 어디까지가 정책 판단이고, 어디부터가 기계가 해야 할 일인가.
LLM 은 비결정적이다. 같은 입력에 다른 표현을 내고, 마이너 버전 변경에 문장 형태가 바뀐다. 그래서 isopod 는 LLM 을 정책 판단에만 쓰고, 그 출력조차 자유 텍스트로 받지 않는다.
| 단계 | LLM 사용 | 출력 형태 |
|---|---|---|
| 앵커 추출 | 두 모델 독립 추출 | 구조화 + 교차검증 후 대칭차 0 요구 |
| 앵커 게이트 (단일관심사·묶기·폐기·종류별) | 없음 | 결정적 검사 |
| 카드 초안 (묶기·정합·분류) | 유일한 정책 판단 지점 | 판단 4종 전부 enum / bool / id |
| 카드 발행 · 정리 | 없음 (모델 호출 0회) | 순수 결정론 |
| 실행 가능 집합 계산 | 없음 | 그래프 연산 |
| 스펙 · 계획 · 테스트 · 구현 · 리뷰 | 있음 | 스키마 강제 + 검증 실패 시 재프롬프트 |
| 4계층 게이트 판정 | 없음 | TS 컴파일러 / AST 변이 / deep-diff / 프로세스 spawn |
| 상태 전이 (머지 검증) | 없음 | git 조상 판정 |
읽는 법은 간단하다 — 표의 "없음" 칸이 이 하네스가 한 일이다. 에이전트가 잘하는 것을 에이전트에게 맡기는 건 어렵지 않다. 어려운 쪽은 에이전트에게 맡기면 안 되는 것을 골라내 결정적 기계로 옮기고, 그 경계를 코드로 지키는 것이다.
4-1. 앵커 그래프의 결정론 — 두 모델 교차검증과 내용 기반 조인
문제. 자연어 스펙에서 작업 단위를 뽑는 일은 본질적으로 LLM 의 몫이다. 그런데 그 결과가 이후 모든 것의 토대가 된다 — 앵커를 잘못 뽑으면 카드도, 의존 그래프도, 테스트도 전부 어긋난다. 비결정적 단계가 파이프라인 최상류에 있다는 것이 이 설계의 근본 난점이다.
해법 — 서로 다른 두 모델의 합의를 요구한다. 설정에서 지정한 두 개의 서로 다른 모델이 같은 스펙을 독립적으로 읽고 앵커 그래프를 추출한다. 통과 조건은 느슨한 유사도가 아니라 앵커 대칭차 0 AND 엣지 대칭차 0 — 하나라도 어긋나면 실패다.
진짜 설계는 조인 키에 있다. 두 모델이 같은 내용을 뽑아도 서로 다른 ID 를 붙이면 단순 비교는 전부 불일치로 나온다. 그래서 조인 키를 모델이 지어낸 ID 가 아니라 원문 위의 문자 구간(span) 으로 잡았다. 각 앵커는 자기가 유래한 소스 문자열을 한 글자도 바꾸지 않고 인용해야 하고(패러프레이즈 금지), 그 인용문은 정체성이 아니라 포인터다 — 인용 위치의 줄 범위 안에서 정확히 일치하는 substring 을 찾아 문자 구간으로 환산하고, 조인은 그 구간끼리 한다.
구간으로 내린 이유는 문자열 비교가 못 푸는 것을 푼다는 데 있다. 두 추출기는 스펙을 독립적으로 분절하므로 같은 주장을 뜻해도 인용이 같은 글자에 떨어지는 일은 드물다 — 한쪽이 문장 전체를, 다른 쪽이 그 안의 명사구를 인용한다. 문자열 조인이면 이건 전부 불일치다. 구간 조인이면 겹치면 같은 주장이고, 겹침에는 임계값이 없다 — 한 글자를 공유하거나 아니거나다. 관계 판정은 Allen 의 13개 구간 관계를 쓰고, 교차(crossing)는 금지·중첩(nesting)은 허용한다(한 줄에 두 주장이 있으면 각자 자기 부분 구간만 차지하고, 명사구 앵커는 자기를 감싼 제약 앵커 안에 들어앉는다). 조인은 2단계다 — 같은 kind 끼리 먼저 잇고 남은 것을 kind 를 넘어 잇는다. 한 단계로 하면 작은 중첩 앵커가 자기를 감싼 앵커를 상대 런의 성분으로 끌고 들어가, 일치를 입도 분쟁으로 오보한다.
합의 이후는 전부 결정적이다. 솔리드니스 게이트를 통과하면 합의된 후보 스토어를 앵커 스토어로 옮기는 채택(adopt) 단계가 순수 코드로 돌고, 이어서 단일 관심사 / 묶기 / 폐기 / 종류별 게이트가 결정적 검사로 돈다. 그리고 이후 파이프라인에서 LLM 정책 판단은 정확히 한 곳(카드 초안)만 남고, 그 판단마저 네 개 전부 enum/bool/id 필드로 강제된다. 카드 발행과 정리 단계는 모델 호출이 0회다 — 되돌릴 수 없는 작업(트래커 발행)을 비결정적 단계에 두지 않기 위해서다.
채택이 코드가 된 것은 설계 원칙의 사후 적용이다. 원래 이 단계는 에이전트에게 "후보 스토어를 앵커 스토어로 복사하고 설정의 접두사 순번으로 번호를 다시 매겨라"라고 말로 시켰다. 그런데 설정에는 접두사가 다섯 개 있고 그중 kind: anchor 인 것은 하나뿐이다 — 즉 집합은 코드가 이미 알고 있는데 모델에게 그 원소를 재구성하라고 요구한 것이고, 이는 이 하네스가 §7-3 에서 스스로 금지한 형태다. 실제로 한 번은 앵커 종류별로 스토리 접두사를 골라 카드 ID 와 정면 충돌하는 스토어를 만들었고, 스키마·게이트·npm run check 가 전부 통과시켰다. 지금은 복사·번호 재부여가 코드이고(참조 재작성도 스키마에서 뽑은 13개 지점 전부를 돈다), 앵커 ID 가 앵커 접두사를 벗어나면 검증기가 거부한다. 앵커 파이프라인의 에이전트 호출은 이 변경으로 5회에서 4회가 됐다.
과장 금지선. 두 모델 합의는 같은 것을 함께 놓칠 수 있다(상관된 오류). 대칭차 0 은 "둘이 동의했다"를 증명하지 "스펙을 옳게 읽었다"를 증명하지 않는다. 또한 이 게이트는 앵커 추출 단계에만 있고, 이후 스펙·구현 페르소나는 단일 모델이다.
4-2. 상태 머신으로 정의되는 테스트 — TDD in action
문제. 에이전트에게 "테스트를 잘 써라"라고 지시하면 자기가 통과시킬 수 있는 테스트를 쓴다. 커버리지는 방어가 안 된다 — 업계 실측에 라인 커버리지 100%인데 변이 점수 4%인 스위트가 존재하고, 한 대형 기업은 라인 커버리지만 봤다면 유효한 테스트 277개를 잘못 버렸을 것이라고 보고했다.
해법 — 스펙을 산문이 아니라 상태 머신으로 강제한다. 스펙 문서의 스키마상 필수 필드가 states · events · transitions 다. 그리고 각 전이는 legal 플래그를 달아 불법 전이까지 명시적으로 열거하게 한다.
| 필드 | 강제 내용 |
|---|---|
transitions[] |
{from, event, to, legal} 필수 — 불법 전이도 1급 시민 |
acceptance_criteria_coverage[] |
기준마다 covered_by + falsifying_examples(그 기준을 반증할 예시) |
test_plan[] |
{id, targets, type, name} — 테스트가 무엇을 겨냥하는지 명시 |
실제 카드 하나의 예: 전이 25개 중 15개가 불법 전이였고, 수용 기준 6개에 대해 테스트 작성 에이전트가 구현 전에 가드 20개를 작성했다. 즉 테스트의 과반이 "이렇게 되면 안 된다"를 증명하는 네거티브 테스트다. 커버리지를 라인이 아니라 전이로 재는 구조라서, 해피 패스만 훑고 초록을 받는 경로가 애초에 없다.
여기에 세 겹의 강제가 겹친다.
- Red-baseline — 구현 전에 테스트가 실패함을 증명해야 한다. 그 근거는 에이전트의 진술이 아니라 러너가 직접 돌린 subprocess 의 exit code 다 (테스트가 실패해야 통과 기록).
- 테스트/구현 분리 — 구현 페르소나는 테스트 파일을 고칠 수 없다. 테스트를 고쳐서 초록을 만드는 경로를 사람이 아니라 역할 분리로 막았다.
- 상수 스텁 증명 불가 규칙 — 스펙 검증기가 해피 케이스와 엣지 케이스의 기대값이 같으면 거부한다. 상수를 반환하는 스텁으로는 둘을 동시에 만족할 수 없어야 하기 때문이다. 이건 구현 시점이 아니라 스펙 작성 시점의 게이트다.
과장 금지선. 테스트/구현 분리의 강제력은 파일 권한이 아니라 사후 git diff 검사다. 그 검사가 하드코딩된 경로 접두사를 보는 탓에 소스 디렉터리에 함께 있는 테스트 파일 수정이 빠져나가던 결함은 설정의 테스트 glob 참조로 고치고 경계 테스트로 박제했지만(§9-2), 검사 시점이 사후라는 성질 자체는 그대로다.
그리고 그 glob 수정은 자기 뒤에 숨어 있던 결함 두 개를 드러냈다 — 둘 다 그린필드 스펙을 실제로 굴려 보고서야 나왔다(§9-3). (i) 검사의 비교 기준이 roll 베이스였는데, test-author 가 쓴 테스트는 커밋되지 않은 패치로 구현 워크트리에 얹힌다. 그래서 구현이 건드린 적 없는 그 테스트들이 언제나 diff 에 들어왔고, TDD 흐름에서 이 게이트는 원리적으로 통과가 불가능했다 — 강제가 아니라 마비였다. (ii) git diff 는 untracked 파일을 나열하지 않는데 구현이 새로 만든 파일은 untracked 다. 즉 검사는 구현이 편집한 테스트는 잡아도 직접 작성한 테스트는 아예 못 봤다 — 둘 중 더 쉬운 우회로다. 지금은 (i) 비교 기준을 test-author 워크트리의 내용으로 바꾸고(경로가 아니라 내용 비교라, 건네받은 테스트를 고쳐 쓰면 여전히 잡힌다) (ii) untracked 를 합집합에 넣어 둘 다 닫혔다. 문서가 "역할 분리로 막았다"라고 쓴 시점에 그 강제는 실제로는 서 있지 않았다 — 이 항목이 실행으로 확인된 적이 없었기 때문이고, 이는 §7-1 이 지목하는 "경계를 통과하는 테스트 0개"의 정확한 재발이다. 또한 상태 머신을 누가 채우는가 하면 spec 페르소나(LLM)다 — 전이를 빠뜨리면 그 전이는 애초에 테스트 대상이 되지 않는다. 이 구조가 강제하는 것은 "빠짐없이 열거했는가"가 아니라 "열거한 것은 반드시 테스트로 이어진다" 이다.
4-3. 다팀·다트래커 환경의 소유권 모델 — 실무 조건이 만든 설계
문제. 외부 팀은 우리 SSOT 를 쓰지 않는다. 그들의 진실은 그들의 트래커에 있다. 그런데 의존 그래프를 계산하려면 그들의 작업이 우리 그래프에 노드로 존재해야 한다. 여기서 딜레마가 생긴다 — 노드로 두면 편집 가능해지고, 편집하는 순간 그건 남의 팀 상태를 우리가 위조하는 것이 된다. 협업이 트래커 기반으로 돌아가는 실무에서는 이 문제가 회피 불가능하다.
해법 — 소유권을 데이터 모델에 1급으로 넣는다.
- 팀 이름이 아니라 능력 플래그로 가른다. 코드가 "이 팀은 외부다"를 팀 이름 문자열로 판단하지 않고, 설정의 능력 플래그(트래커 관리 주체인가 / 저장소에 쓸 수 있는가 / 스키마 변종 / 렌더 대상인가)로 분기한다. 팀이 늘거나 이름이 바뀌어도 코드가 안 바뀐다.
- 쓰기 범위를 필드 단위로 제한한다. 외부 팀 카드에서 우리가 쓸 수 있는 것은 트래커 미러 필드 + 상태 + 마감 마일스톤 뿐이다. 제목·하위작업·의존·계약·앵커 등 본문 전체는 잠긴다. 신규 카드 생성도 막힌다.
- 이 경계는 문서 규약이 아니라 코드가 예외를 던져 지킨다. 편집 함수 안의 불변식 검사가 미러 범위 밖 패치를 거부한다 — 조사 과정에서 실제로 실행해 확인했다 (본문 편집 시도 → 예외, 미러 필드 → 통과).
- 역방향으로는 계약을 기술한다. 외부 팀에 요구하는 인터페이스는
consumer_contract필드로 카드에 박제하는데, 스냅샷 200장 중 이 필드를 가진 24장이 전부 외부 팀 카드다. 우리가 그들의 구현을 편집할 수는 없지만 무엇을 기대하는지는 버전 관리되는 형태로 남긴다.
스펙이 바뀌는 조건도 데이터 모델이 흡수한다. 이 프로젝트에서 카드의 20.5%가 폐기됐고 결정 17건이 누적되며 서로를 대체했다. 그래서 세 가지 규칙이 생겼다 — 카드 폐기 시 역방향 의존 정리, 신규 결정 발행 시 정방향 의존 주입, 결정 대체 시 의존 엣지 재검토. 앞의 둘은 감사 함수로 코드화됐고, 셋째는 §7-4 의 사고에서 나왔다. 배포된 카드의 목적지가 바뀌면 원본을 고치지 않고 마이그레이션 카드를 새로 만들어 의존으로 연결한다.
왜 이게 차별점인가. 조사한 인접 도구 6종 중 외부 트래커 연동이 내장된 것은 1종이고, 다중 팀 소유권 모델을 가진 것은 없다(§5). 이건 기술적 우위라기보다 문제 설정의 차이다 — 개인 개발자용 도구는 이 문제를 만날 일이 없다.
과장 금지선.
- 외부 팀 미러는 단방향이다. 우리가 그쪽 트래커를 조회해 반영하는 것이지 양방향 동기화가 아니다.
- 소유권 강제에 탈출구가 있다 — 환경 변수 하나로 통과시킬 수 있고, 흔적은 stderr 한 줄뿐이라 파일이나 커밋에 남지 않는다. 사후 감사는 경고 등급이라 CI 를 막지 않는다.
done카드 편집 금지는 코드 강제가 아니라 규약이다(§9-2). 강제되는 것은 done 으로 들어가는 자격(머지 검증)이지 들어간 뒤의 불변성이 아니다.
4-4. 검증 주도 루프 — 자기신고를 형태 수준에서 차단
업계 난점: 검증기 자체의 적합성이 미해결이다. 커버리지 100%인데 변이 점수 4%인 테스트 스위트가 실측으로 존재하고, 한 대형 기업 사례에서는 라인 커버리지만 봤다면 테스트 277개를 잘못 버렸을 것이라고 보고했다. 즉 "테스트가 초록"은 안전 신호가 아니다.
isopod 의 구현 — 예상보다 강했던 지점 6가지:
- 자기증명 거부. 증거 객체 검증기가
command: "self-review …"형태를 hard error 로 막는다. 단어 경계 앵커라 실제 도구명(self-review-runner)은 오탐하지 않는다. - 코드가 모델의 판정을 덮어쓴다. 리뷰어가
ok: true라고 주장해도 fail 등급 findings 가 있으면 코드가ok: false로 뒤집고, 모델이 주장하는 예외 통로를 인정하지 않는다. - 스키마 enum 이 코드에서 로드 시 주입된다. 상태·실패분류·리뷰어종류 enum 을 코드 SSOT 에서 스키마에 밀어 넣어 스키마-코드 drift 가 구조적으로 불가능하다.
- 상수 스텁 증명 불가 규칙. 스펙에서 happy 케이스와 edge 케이스의 기대값이 같으면 거부한다 — 상수를 반환하는 스텁으로는 둘을 동시에 만족할 수 없어야 하기 때문이다.
- 머지 검증이 판정 불가 시 보수적으로 거부하고, 상태 전이 직전에 한 번 더 재확인해 force-push 를 잡는다. 미검증이면 아무 변경도 하지 않고 종료한다.
- PR 직전 합성 브랜치 재검증. 스토리별 격리 작업트리 게이트가 원리적으로 못 보는 스토리 간 파손을 잡는 유일한 자리이며, 실패하면 PR 자체를 열지 않는다.
과장 금지선.
- 증거 완전성 미검사는 닫혔다 — 다만 시점이 중요하다. 결과 문서 검증기가 rigorous 플래그를 검증 함수에 전달하지 않아 L1~L4 증거가 0건인 결과도 검증 CLI 를 exit 0 으로 통과하던 결함이 실측으로 확인됐고, 플래그 전달 + CLI 를 프로세스로 spawn 해 증거 0건 문서가 exit 2 로 떨어지는지 확인하는 경계 테스트로 막았다(함수 단위 테스트는 이 클래스를 원리적으로 못 잡는다 — 함수를 직접 부르는 테스트는 이미 있었고 전부 초록이었다). 그러나 본문의 70장은 이 수정 이전에 검증된 것이다.
- 면제 표면은 여전히 넓다 — 다만 이제 세어진다. L3/L4 의 n/a 판정과 L1 경고 강등, L2 임계값 하향을 spec/plan 에이전트가 스스로 켤 수 있고, 면제 사유의 내용 검증은 사실상 없다(공백 여부만 확인). 달라진 것은 모든 면제가 단일 seam 을 통과해 원장에 기록되고 표면별 카운트가 리포트에 노출된다는 점이다(§4-8). 막지 않고 센 이유는 §7-2 에 있다 — 막으면 파이프라인이 사람 응답 시간에 묶인다.
- 따라서 "모든 스토리가 4계층 검증을 통과했다"는 서술은 여전히 거짓이다. 정확한 서술은 "각 스토리가 자기에게 적용된 게이트 집합을 통과했고, 앞으로는 그 집합에서 무엇이 빠졌는지가 데이터로 남는다" 이다.
4-5. 정지 조건 — 업계 최대 실패 클래스에 대한 대조
업계 난점: 앞서 인용한 대로 "언제 멈출지 모름"이 최대 실패 유형이고, 조기 종료와 공존하므로 단일 노브로 못 잡는다.
isopod 의 구현: 정지 규칙을 3층으로 나눴다 — 바깥 재시동 상한(5회)과 종료 경로 6개, 같은 범주로 반복 실패하면 종결시키는 정체 판정 2종, 그리고 재시도 캡 3층.
실측 결과: (스토리 × 페르소나) 쌍 352개 중 319개가 1패스에 종료, 33개가 2패스. 관측된 종료 사유 379건이 전부 정상 완료였다. 이 데이터셋에서 반복 루프로 인한 무한 진행은 관측되지 않았다.
과장 금지선: 표본이 한 프로젝트 385패스로, 인용한 업계 연구(1,642 trace / 7 프레임워크)와 규모·다양성이 다르다. 또한 "재시도 총량이 10회로 제한된다"는 서술은 틀리다 — 전송 오류·게이트 하니스 오류·동일 호출 재시도 3종의 예산은 캡을 차감하지 않는다. 정확히는 "유한하게 제한되지만 상한이 10은 아니다".
4-6. Human-in-the-loop 배치 — 원칙이 없는 자리에 원칙을 세움
업계 난점: 조사 결론은 "어느 층에 게이트를 두면 유효하고 어느 층은 노이즈인지에 대한 원칙이 없어 실무는 대개 전부 넣거나 전부 뺀다" 였다. 인접 도구 조사가 이를 재현한다 — 한 도구는 승인 지점이 다수(전부 넣기), 다른 도구는 차단성 승인 지점이 명시돼 있지 않다(전부 빼기).
isopod 의 답: 게이트를 층으로 가른다.
| 층 | 사람 게이트 | 이유 |
|---|---|---|
| L2 요구사항 | 있음 | 카드 발행은 되돌릴 수 없고, 관측 게이트는 통합 시나리오의 런타임 진실이 들어오는 유일한 통로 (스토리 단위 진실은 머지 직후 스모크가 자동으로 가져온다 — §7-1) |
| L1 프론티어 | 없음 | 소진까지 자동 재시동 |
| L0 스토리 실행 | 0개 | 실측 385패스 무인 |
과장 금지선: 실행 층 게이트를 뺀 대가로 세운 5개 하류 방어선 중 일부는 §4-4 에서 본 대로 에이전트가 면제할 수 있다. "게이트를 뺐지만 다른 게이트가 전부 잡는다"는 부분적으로만 참이다.
4-7. 구조화 출력 — 자유 텍스트를 분기에서 몰아냄
업계 난점: 문법적 보장이 의미적 정확성을 뜻하지 않는다(벤더 문서 자신이 명시). 게다가 형식 강제가 추론력을 깎는다는 연구가 있고, 후속 연구는 손실의 대부분이 디코더 제약이 아니라 "형식을 요구하는 지시문 자체" 에서 온다고 보고했다.
isopod 의 구현: 스키마 검증을 벤더의 플래그가 아니라 정규화된 결과 봉투 위의 자체 검증 + 재프롬프트로 뒀다. 위반 시 구체적 위반 내용을 프롬프트에 붙여 재실행하고 (기본 2회 재시도, 총 3회), 소진되면 값을 비운다. 백엔드 인터페이스 문서가 벤더 스키마 플래그를 "힌트일 뿐" 이라고 못박아, 어댑터가 있는 CLI 는 모두 같은 검증 경로를 탄다. 봉투 언랩도 정규식이 아니라 인용부호 인식 균형 괄호 파서를 쓴다.
과장 금지선: 원칙(§룰 24 "자유 텍스트 정규식 파싱 금지")은 완전히 지켜지지 않았다. 전송 오류 분류기가 서브에이전트 stdout 에 정규식을 걸고, 리뷰어 판정 정규화에도 ajv 없는 정규식이 하나 있다. 다만 전자는 완전한 이벤트 스트림이 있으면 산문 경로를 차단한다. 정확한 서술은 "구조화 신호가 있으면 산문을 보지 않고, 없을 때만 보수적 패턴으로 폴백한다".
4-8. 자가 개선 — 부분 완화의 실증
업계 난점: "규칙 파일이 늘수록 어느 규칙이 실제로 발화됐는지 관측 불가하고, 규칙 간 충돌·화석화를 감지할 자동 수단이 없다 — self-improving loop 는 잘못된 교훈도 똑같이 잘 박제한다."
isopod 의 부분 답: 룰 중 일부를 실행 가능한 감사 함수로 승격했다.
| 룰 | 승격된 감사 | 잡는 오류 |
|---|---|---|
| 카드 폐기 시 역방향 의존 정리 | dangling 의존 감사 | 살아있는 카드가 폐기된 카드를 의존 |
| 신규 결정 카드의 정방향 의존 | 고아 결정 감사 | 발행된 결정에 인바운드 의존 0 |
| 미검증 카드 적재 한계 | 미검증 게이트 감사 | 관측 게이트 뒤 카드 수 > 설정 임계 |
의존 그래프 감사는 총 22개이고, 각각 스키마 검증이 원리적으로 못 잡는 이유가 다르다 (교차 파일 유일성 / 참조 무결성 / 그래프 도달성 / 파일시스템 존재 / git HEAD 대비 diff / 설정 대조).
관측을 켜는 두 번째 축 — 면제 원장. 위 업계 난점의 앞부분("어느 규칙이 실제로 발화됐는지 관측 불가")에 대한 직접 답이다. 검증 게이트의 면제 지점 10개는 각각은 정당한 근거를 갖지만 총량을 아무도 세지 않았다 — 어느 방어선이 명목상 존재하고 어느 것이 실질 비활성인지 데이터가 0이었다. 해법은 면제 지점을 열거해 세는 것이 아니다(새 지점이 추가될 때마다 집계에서 조용히 빠진다 — §4-7 이 금지한 "닫힌 완결셋을 만들 수 없는" 구조와 같은 함정이다). 대신 면제를 만드는 단일 seam 을 뒀다. 모든 면제가 그 seam 을 지나 {roll, story, pass, surface, check, reason_kind} 형태로 원장에 append 되고, 분기용 필드는 전부 enum/id 이며 자유 텍스트는 사람 읽기용 한 칸뿐이다. 표면별 카운트가 리포트에 노출되고, seam 을 타지 않는 새 면제 지점은 정적 감사가 검출한다.
설계 원칙은 "막지 말고 세고 노출한다" 이다. 막으면 §7-2 사고(사람 응답 대기로 파이프라인 정지)의 직접 재발이고, 면제율이 지표가 되면 압력은 자동으로 생기면서 파이프라인은 멈추지 않는다. 다만 이 계측은 지금부터 쌓인다 — 본문의 70장은 원장 이전 데이터이므로 그 스토리들의 면제 분포는 소급해서 알 수 없다.
과장 금지선 3가지.
- 룰 29개 중 감사로 승격된 것은 소수이고, 20개는 여전히 산문이다.
- "감사가 N개 버그를 잡았다"고 쓸 수 없다. 200장 스냅샷에서 실제 발화한 감사는 1개뿐이다. 정직한 서술은 "과거 사고가 감사 함수로 코드화돼 있고, 그 사고의 물증이 데이터에 남아 있다".
- 22개를 균질하게 세면 안 된다 — error / warn 티어가 갈리고 warn 이 다수라, 기본 설정에서 CI 를 실제로 실패시키는 감사는 절반 이하다.
그리고 이 축에는 자기 반증이 있다. 조사 중 룰 문서와 코드가 어긋난 사례가 3건 나왔다 (룰이 서술한 상수가 프로덕션 코드에 없음, 페르소나 개수 오기, 설정 SoT 원칙을 어긴 경로 하드코딩). 자가 개선 루프가 자기 문서의 drift 를 막지 못한다는 직접 증거이며, 이는 위 업계 난점의 재확인이다.
같은 패턴이 한 번 더 나왔다. 하네스에는 §7-1 이 처방한 "경계 통과 결함"을 정확히 겨냥해 만든 스윕 도구가 있는데, 저장소 전체 grep 결과 그 도구를 가리키는 자리가 파일 자신과 주석 한 줄뿐이었다 — 만들어 놓고 아무도 부르지 않았다. 그리고 §9-2 의 실측 결함 4건 중 3건이 정확히 그 도구가 겨냥한 클래스였다. 도구를 만드는 것과 그것이 발화하게 만드는 것은 별개의 작업이라는 게 이 축의 결론이고, 교정으로 그 스윕을 하네스 자신에게 1회 적용했다. 자가 개선 루프의 실제 도달점은 "룰을 감사로 승격한다"가 아니라 "승격한 감사가 실제로 호출되는 자리에 배선돼 있는가" 까지다.
5. 설계 포지셔닝 — 인접 하네스와 무엇이 다르고 무엇이 같은가
인접 도구 6종(SDD 툴킷 2종, 에이전트 스킬 플러그인 1종, 자율 SWE 에이전트 1종, 오케스트레이션 프레임워크 1종, 상용 spec-driven IDE 1종)을 1차 자료로 조사했다. 조회 시점은 2026-08-06 이며, 이 분야는 주 단위로 움직여 아래 판정 중 일부는 시간이 지나면 틀린다. 우열 비교가 아니라 최적화 대상의 차이로 읽어야 한다.
| 축 | isopod | 인접 도구들 |
|---|---|---|
| 검증의 대상 | 작업 산출물을 실제로 실행해 판정 | 스펙 문서의 형식 판정 (1종은 exit code 로 프로그램적, 1종은 차단형 훅, 2종은 권고) |
| 작업 단위 | 의존 그래프를 가진 카드 집합 | feature / change / plan / 티켓 1건 |
| 1회 실행 스코프 | 프론티어 소진까지 자동 재시동 | 1건 처리 (다중 자동 소진은 6종 모두 확인 안 됨) |
| 상태 수명·이력 | 버전관리 SSOT + 7단계 상태 머신 + 상태별 편집 규칙 | 저장소 파일 (1종은 영속 모델에 기본값 없음) |
| 사람 승인 위치 | 요구사항 층에만 | 다수 배치 또는 명시 없음 |
| 외부 트래커 | 어댑터 2종 실증 + 외부 팀 읽기 전용 소유권 모델 | 1종만 내장, 나머지 없음/미확인 |
| 이식성 | 에이전트 CLI 2종 (빈 저장소 이식 리허설은 하네스 수정 0으로 통과) | 30+ / 40+ / 11 플랫폼 — 에이전트 CLI 축은 isopod 가 명백히 열세 |
| 진입 마찰 | 높음 (설정→스펙→anchor→카드→roll) | 대부분 1커맨드 — isopod 가 명백히 열세 |
| 자가 개선 | 사고 → 룰 → 일부 감사 코드 승격 | 스킬 축적 또는 없음 |
인접 도구가 더 나은 점 (명시)
한 도구는 30개 이상의 에이전트를 지원하고 회사가 유지하며 2주에 10회 릴리스한다. 다른 도구는 API 키도 MCP도 필요 없는 낮은 의존성과 exit code·JSON 형태를 계약 문서로 명문화한 설계를 갖는다 — isopod 에는 대응 문서가 없다. 스킬 플러그인 계열은 승인 지점이 촘촘해 처음 쓰는 사람이 통제감을 잃지 않는다. 자율 SWE 에이전트 1종은 트래커 연동이 내장이라 isopod 가 직접 만들어 유지하는 부분을 제품이 해결해 준다. 오케스트레이션 프레임워크는 영속 실행·노드별 재시도·중단/재개를 프레임워크 1급 개념으로 제공하는데, isopod 는 같은 것을 매번 손으로 짰다.
isopod 가 의도적으로 안 한 것
낮은 진입 마찰, IDE 통합, 폭넓은 CLI 지원, 스펙 문서 형식(EARS 등) 강제, 임의 그래프를 짜는 범용 오케스트레이션 층. 파이프라인은 고정돼 있다.
같은 것: spec-first 접근, 단계 분리, TDD 강제, 저장소 내 아티팩트 — 이건 isopod 의 발명이 아니라 이 분야의 공통 관행이다.
6. 정량 임팩트
6-1. 처리량 (실측)
| 지표 | 값 |
|---|---|
| roll 실행 | 14회 (약 2개월) |
| 에이전트 실행 고유 스토리 | 73 |
| 검증 통과 | 70 (95.9%) |
| 실패 종결 | 3 |
| 에이전트 패스 | 385 |
| 총 턴 | 31,116 |
| 최대 동시 실행 | 13 패스 (평균 1.81배) |
프론티어 소진의 직접 증거: roll 별 생성 카드 수가 101 → 100 → 99 → 80 → 80 → 80 → 78 → 74 → 70 으로 단조 감소했다.
95.9% 를 읽는 법. 이 통과율은 §4-4 의 면제 표면과 함께 읽어야 한다. 게이트는 실제로 실행되지만 L3/L4 의 n/a 판정을 에이전트가 스스로 켤 수 있으므로, 95.9% 는 "70장이 4계층 전부를 통과했다"가 아니라 "70장이 각자에게 적용된 게이트 집합을 통과했다"는 뜻이다. 스토리마다 실제로 적용된 게이트 조합은 균일하지 않다.
그리고 이 70장은 §9-2 의 검증 결함 수정과 면제 원장 도입 이전의 데이터다. 수정은 앞으로 들어올 스토리를 담보하지 이 70장을 소급해 강화하지 않으며, 수정 이후 표본은 아직 없다. 이 문서는 그 두 시점을 섞지 않는다.
동시성 13 을 읽는 법. 병렬 실행은 기본값이 아니다. 워커 수 기본값은 1이고, sweep 이 환경 변수로 켤 때만 병렬이 된다. 또한 병렬은 스토리 사이에서 일어나며, 한 스토리 안의 5개 페르소나는 순차 실행이다. 격리도 작업트리 수준이지 프로세스·체크아웃 수준이 아니다 — N 워커가 하나의 대상 체크아웃을 공유하고 커밋 창만 뮤텍스로 직렬화된다.
이 73/70 을 얻기까지 걸러낸 것. 단순 집계는
verified: true를 114건으로 센다. 여기에는 (a) 이미 커밋된 스토리의 재실행 no-op 44건, (b) 실패 후 재시도 스냅샷 중복, (c) 사람이 직접 구현한 8장이 섞여 있다. (c)는 결과 파일이 스스로 "러너 미사용"이라고 기록해 둔 것을 전수 조사해 제외했다. 이 구분은 §8 의 척추이기도 하다.
6-2. 비용과 시간 (실측)
| 항목 | 값 |
|---|---|
| 본 집계 (385 패스) | $2,754 |
| 재시도 스냅샷 (158 패스) | $1,257 |
| 총 지출 | $4,012 (재시도가 31.3%) |
| 검증 통과 스토리당 | $39 (재시도 포함 전액 기준 $57) |
| 에이전트 연산 시간 | 144.4시간 |
| 기계 가동 시간 | 84.8시간 (병렬 1.70~1.81배) |
페르소나별 비용: 구현 34% · 테스트 작성 24% · 스펙 18% · 계획 18% · 리뷰 6%. 검증 비용의 대부분이 사후 리뷰가 아니라 사전 테스트 작성에 들어가는 구조다.
컨텍스트 재사용: 캐시에서 읽은 토큰이 약 48.4억, 캐시에 쓴 토큰이 약 1.63억으로 재사용 비율 약 96.7% 였다. 매번 프롬프트를 재구성하지 않고 누적 컨텍스트를 재사용하며 굴렸다는 직접 증거다. (다만 이 캐시 효과는 CLI 가 제공하는 것이지 isopod 가 구현한 압축 알고리즘이 아니다.)
실패 분류: 재시도 63건 중 43건(68%)이 전송 계층 일시 오류였다. 로직 실패가 아니다. 두 방향으로 읽어야 한다 — 재시도 장치가 없었다면 이 68%가 사람 개입으로 전가됐을 것이고, 동시에 총 지출의 31%가 모델 품질이 아닌 운영 안정성 비용이었다는 뜻이다.
6-3. 인건비 환산 (추정 — 가정 전면 공개)
아래는 추정이다. 실측인 것은 에이전트 비용·시간·건수뿐이다.
| 가정 | 값 |
|---|---|
| A1 스토리 1장의 사람 구현 공수 | 보수 4h / 중간 8h / 낙관 16h |
| A2 완전부담 시간당 인건비 | ₩60,000 / ₩80,000 / ₩100,000 |
| A3 스토리당 사람이 여전히 하는 일 | 3h / 2h / 1h (스펙·검토·리뷰·머지·게이트) |
| A4 환율 | ₩1,350/USD |
에이전트 비용은 재시도 포함 스토리당 $57 ≈ ₩77,400.
| 시나리오 | 대체된 시간 | 인건비 환산 | 에이전트 비용 | 순효과 |
|---|---|---|---|---|
| 보수 | 1h | ₩60,000 | ₩77,400 | −₩17,400 (손해) |
| 중간 | 6h | ₩480,000 | ₩77,400 | +₩402,600 |
| 낙관 | 15h | ₩1,500,000 | ₩77,400 | +₩1,422,600 |
70장 기준 총 순효과 −₩122만 ~ +₩9,958만, 중간 시나리오 +₩2,818만.
보수 시나리오가 음수라는 점을 지우지 않는다. 스토리 단위가 작고 사람 잔여 작업이 크면 이 하네스는 경제적으로 손해다. 유리해지는 조건은 (a) 스토리 단위가 충분히 클 때, (b) 여러 장을 병렬로 굴릴 때, (c) 추적 가능성 자체에 값을 매길 때다.
정량화하지 않은 효과: 병렬성(최대 13패스 동시), 추적 가능성(카드 200장 · 의존 438 · 하위작업 1,018 · 앵커 참조 673이 버전 관리된 상태로 잔존), 야간 가동. 근거 없는 배수를 곱해 임팩트를 부풀리지 않기 위해 수치화하지 않는다.
7. 사고와 구조적 해결
4건을 실패 클래스가 겹치지 않게 골랐다. 전부 개인의 실수가 아니라 구조의 결함이었다.
7-1. "머지됐다"를 "동작한다"로 착각한 카드 21장 — 검증 공백
증상. 통합 데모를 처음 실제로 돌렸더니 주요 유스케이스가 전부 미통과했다. 그런데 그 경로 위의 카드 21장이 이미 done 이었다.
측정. 이력 검색 결과 결함은 회귀가 아니라 이전 릴리스에도 동일하게 존재했다 — 한 번도 동작한 적 없는 경로였다. 결정적 측정은 "경계를 통과하는 테스트가 몇 개인가" 였다: 파서의 출력을 내부 형태로 만드는 테스트 8개, wire 스키마를 쓰는 테스트 6개, 둘 다 쓰는 테스트 0개. 양쪽을 각자 테스트하면 영원히 초록이다.
오진하기 쉬운 지점. "최근 머지가 회귀를 냈다"(이력 검색으로 기각), "테스트가 부족했다" (양쪽 다 있었고 전부 초록이었다).
진짜 원인. published → done 을 승인하는 유일한 게이트가 머지 검증뿐이고, 머지와 done 사이에 머지된 코드를 실행해 보는 단계가 하나도 없다. 런타임 진실은 사람이 여는 관측 게이트로만 들어오는데 그 게이트가 파이프라인 맨 끝에 있었다. 따라서 "게이트 하나 뒤에 달린 카드 수 = 아무도 실행해 본 적 없는 토대 위에서 done 에 도달할 수 있는 카드 수"였다.
구조적 해결. (1) 관측 게이트 뒤의 카드 수를 세어 임계 초과 시 경고하는 감사를 추가하고 임계값을 코드가 아니라 설정 파일에 뒀다. (2) 얇은 수직 슬라이스 게이트를 앞에 배치해 23번째가 아니라 2번째 카드에서 드러나게 하는 설계 규칙을 세웠다. (3) 관측 게이트를 열 때 계약 스윕 신선도를 강제해, 대상 경로에 마지막 스윕 이후 변경이 있으면 게이트 개방을 거부한다. 우회 경로는 하나뿐이고 그 사실이 게이트 증거에 기록된다. (4) 머지 검증과 상태 전이 사이에 얇은 수직 슬라이스 스모크 1회를 넣어, 런타임 진실이 사람의 게이트 개방을 기다리지 않고 파이프라인 안으로 들어오게 했다 — 다만 실패해도 전이를 막지 않는다. 막으면 §7-2 사고의 직접 재발이므로, done 에 증거 없음 표식을 남기고 리포트에 노출하는 데까지가 설계다. 차단이 아니라 노출이라는 이 선택이 §9-1 의 잔여 한계를 만든다.
자동 검출되는가 — 그리고 사고의 물증. 된다. 그리고 스냅샷에 사고가 그대로 남아 있다: 200장 SSOT 에서 이 감사가 실제로 발화하며, 한 게이트가 카드 23장을 임계값 8 대비 초과 보유하고 있다.
이 사례의 핵심: 규칙 위반이 아니라 규칙대로 한 결과였다. 머지 검증은 전부 통과했다. 해결도 사람을 다그치는 대신 측정 가능한 임계값 + 자동 경고로 갔다.
7-2. 사람 응답을 기다리다 멈춘 파이프라인 — 게이트 배치
증상. 자리를 비운 사이 배터리가 방전됐고, 재개해 보니 러너가 아무 진전 없이 대기 중이었다. 드리프트 승인 6건과 사인오프 2건이 응답을 기다리고 있었다.
측정. roll 별 유휴 비율(경과 시간 중 이벤트가 하나도 없던 비율)이 91% · 85% · 85% · 84% · 73% 인 구간이 존재한다. 반대로 유휴 3%인 roll 도 있다. 처리량의 지배 변수는 모델 속도가 아니라 사람 응답 시간이었다.
오진하기 쉬운 지점. "배터리가 원인" — 방전 시점에 러너는 연산 중이 아니라 대기 상태였다. 전원이 아니라 대기가 배터리를 태웠다.
진짜 원인. 드리프트 신호가 병렬 파이프라인에서 구조적으로 흔하다. 같은 서비스 안 형제 스토리의 커밋만으로도 의존 경로가 "변경됨"으로 판정된다(스코프 계산이 의존 카드의 대상 경로를 합집합하기 때문). 즉 정상 동작이 매번 사람을 부르는 게이트를 만들어 놨던 것이다.
구조적 해결. 드리프트를 승인 게이트에서 spec 에이전트의 입력 주석으로 강등했다. 구조 오류(선언됐는데 없음, 의존 미충족)만 중단시키고, 나머지는 자동으로 "기존 export 를 보존하며 재작성" 경로로 보낸다. 기존 대화형 흐름은 환경 변수 옵트인으로만 남겼다. 동시에 잃는 신호를 5개 하류 방어선이 커버하도록 명시했다.
자동 검출되는가. 아니다. 이 사고는 설계 원칙("실행 층에 새 승인 게이트 추가 금지")으로 흡수됐고, 강제력은 코드 게이트가 아니라 코드 리뷰 규약이다.
교훈은 "HITL 을 없애라"가 아니라 "HITL 을 어느 층에 두는지가 처리량을 결정한다" 이다.
7-3. 자유 텍스트에 정규식을 얹다 — LLM 비결정성
증상. 특정 게이트를 통과시키는 후처리가 매 라운드 새로운 문장 표현에 미끄러졌다. 패턴을 넓히면 다음 라운드에 또 새 표현이 나왔다.
측정. 반증 검토에서 라운드마다 미탐 채널이 3~5개씩 새로 나왔다. whitelist 는 계속 커졌지만 끝내 절반만 커버했다.
오진하기 쉬운 지점. "패턴을 더 넓히면 된다" — 원리적으로 불가능하다. 모델이 다음에 낼 문장은 학습된 언어 전체의 부분집합이라 닫힌 완결셋을 만들 수 없다. 게다가 패턴 미매치는 안전한 폴백이 아니라 로직 skip = 조용한 false-green 이었다.
진짜 원인. 비결정적 자유 텍스트를 결정론적 후처리의 입력으로 삼은 것 자체.
구조적 해결. 분기를 스키마 강제 출력으로 옮기고(§4-7), 룰로 박제했다.
자동 검출되는가 — 그리고 정직한 반증. 검출 자동화는 없다. 그리고 조사 결과 이 룰은 완전히 지켜지지 않았다(§4-7 과장 금지선). 원칙을 세운 것과 원칙이 지켜지는 것은 별개이며, 이 격차를 문서에 남기는 것 자체가 이 사례의 결론이다.
7-4. 산문은 고쳤는데 그래프가 안 고쳐진 카드 — 검증기의 원리적 사각지대
증상. 이미 랜딩된 카드의 결함을 다음 실행에 태우려고 실행 가능 집합을 계산했더니 그 카드가 없었다.
측정. 발행 상태 71장 중 프론티어 진입 12장, 차단 59장. 그중 관측 게이트 의존만으로 막힌 카드가 3장이었고, 극단 사례는 의존이 게이트 하나뿐이라 사람이 열기 전까지 어떤 실행에도 진입 불가했다.
오진하기 쉬운 지점. "정합성 검사가 통과하니 그래프는 정상" — 이게 정확히 함정이다. 엣지가 가리키는 카드는 실존하고, 순환도 없고, dangling 도 고아도 아니다. 그래프는 완전히 유효하다. 다만 더 이상 참이 아닌 계획을 인코딩하고 있었다.
진짜 원인. 두 겹의 stale edge. (1) 한 결정이 다른 결정을 대체하면서 단계별 계획이 뒤집혔는데, 제목·요약·하위작업 산문은 새 계획으로 다시 썼지만 의존 배열은 그대로 뒀다. (2) 게이트의 적용 대상이 좁혀졌는데 좁혀진 밖의 카드에 게이트 의존이 남아 있었다.
여기에 구조적 비대칭이 겹친다 — 코드 의존은 같은 실행 안에서 풀리지만 관측 게이트 의존은 원리적으로 안 풀린다. 문 분류기가 관측 게이트를 건너뛰므로 실행 중 충족이 불가능하고, 사람이 여는 길밖에 없다.
구조적 해결. (1) "결정 대체 시 의존 엣지를 같은 커밋에서 함께 손본다"는 룰을 신설해, 기존의 폐기 시·신규 결정 시 규칙과 합쳐 카드 생성·삭제·교체 3축을 덮었다. (2) 검증 방법 자체를 바꿨다 — 산문 리뷰로는 안 보이므로 실행 가능 집합을 실제로 계산해 의도한 카드가 들어오는지 확인한다. 안 들어오면 차단 사유가 원인을 지목한다.
자동 검출되는가 — 부분적으로만. 폐기·신규 결정 축은 감사 함수가 있다. 그러나 대체 후 남은 stale edge 축은 여전히 자동 검출되지 않는다. "이 엣지가 여전히 참인가"는 의미론적 질문이라 스키마로도 그래프 정합성으로도 원리적으로 못 잡는다.
다만 방어선이 절차적 습관에서 도구로 옮겨졌다. 카드 발행 직후 의도한 카드가 실행 가능 집합에 실제로 들어오는지 자동 확인하고, 안 들어오면 차단 사유(missing_deps)를 리포트한다. 사람의 습관은 다음 사람에게 전달되지 않지만 도구는 전달된다. 자동화된 것은 "의도가 그래프에 반영됐는가" 이지 "그 의도가 옳은가" 가 아니라는 구분은 그대로 남는다.
7-5. 네 사례의 공통 구조
| # | 실패 클래스 | 오진하기 쉬운 곳 | 해결의 성격 | 자동 검출 |
|---|---|---|---|---|
| 1 | 검증 공백 | "테스트가 초록이니 괜찮다" | 측정 가능한 임계값 + 설정값 + 머지 직후 스모크 | O |
| 2 | 게이트 배치 | "배터리가 원인" | 게이트를 층으로 재배치 | X (설계 규약) |
| 3 | LLM 비결정성 | "패턴을 더 넓히면 된다" | 스키마 강제로 입력을 교체 | X (부분 미준수) |
| 4 | 검증기 사각지대 | "검사가 통과하니 정상" | 절차 신설 → 도구화 + 일부 감사 승격 | 부분 |
4건 중 2건은 여전히 자동 검출로 승격되지 못했다. 이 하네스의 자가 개선 루프가 실제로 도달한 지점이 거기까지다. 특히 3번은 원칙을 세운 뒤에도 코드가 그 원칙을 완전히 지키지 않는다는 것이 조사에서 확인됐다 — 사고를 룰로 박제하는 것과 룰이 실제로 지켜지는 것은 별개다(§4-8).
8. 사람이 한 일 vs 하네스가 한 일
에이전트 하네스 문서를 읽는 사람이 가장 먼저 의심하는 것은 "실제로는 사람이 다 한 것 아니냐"다. 답은 과대주장이 아니라 정확한 경계선이다.
| 구간 | 주체 |
|---|---|
| 자연어 스펙 작성 | 사람 (하네스는 읽기 전용) |
| 앵커 추출 · 카드 초안 · 묶기 | 하네스(LLM) + 사람 검토 |
| 트래커 발행 | 사람이 명령 (되돌릴 수 없음) |
| spec → plan → test → impl → review | 하네스 (사람 게이트 0) — 실측 385패스 |
| PR 리뷰 · 머지 | 사람 |
| 상태 전이 · 트래커 동기화 | 하네스 (사람이 명령, 머지 검증이 전제) |
| 머지 직후 스모크 | 하네스 (자동) — 스토리 단위 런타임 진실. 실패해도 차단하지 않고 표식만 남긴다 |
| 관측 게이트 개방 | 사람 — 통합 시나리오의 런타임 진실이 들어오는 유일한 지점 |
하네스가 하지 않은 것 (실측으로 제외)
- 8장의 카드는 사람이 직접 구현했다. 결과 파일에 "러너 미사용"이라고 기록돼 있고, 본 문서의 73/70 집계에서 제외했다.
- 1장은 상태 전이 기록만 사람이 합성했다. 코드 자체는 자동 실행으로 랜딩됐으므로 제외하지 않았고, 이 구분도 남긴다.
이 경계가 뜻하는 것
하네스가 자동화한 구간은 "검토된 카드 → 검증된 커밋" 이다. 요구사항을 정의하는 앞단과 운영 진실을 확인하는 뒷단은 사람이 소유한다. 이는 결함이 아니라 설계된 배치이며 (§4-6, §7-2), 그 근거는 사고에서 나왔다.
9. 한계와 로드맵
9-1. done 은 여전히 동작을 완전히 증명하지 않는다
가장 중요한 한계다. 머지 검증과 상태 전이 사이에 얇은 수직 슬라이스 스모크가 들어가 런타임 진실이 자동으로 파이프라인 안에 들어오게 됐지만, 그 스모크가 실패해도 published → done 전이는 막히지 않는다. 남는 것은 done 에 붙는 증거 없음 표식과 리포트 노출뿐이다.
이건 미구현이 아니라 의도된 선택이다. 전이를 막으면 실패한 스모크마다 사람의 판단을 기다려야 하고, 그건 §7-2 사고(사람 응답 대기로 파이프라인 정지, 유휴 91%)의 직접 재발이다. 따라서 현재 설계의 정확한 서술은 "런타임 진실이 자동으로 들어오고 자동으로 노출되지만, 자동으로 차단하지는 않는다" 이다.
잔여 축도 명시한다 — 스모크는 얇은 수직 슬라이스 1회이지 유스케이스 전체 커버리지가 아니고, 통합 시나리오의 진실은 여전히 사람이 관측 게이트를 열 때 들어온다. 완화책(미검증 카드 적재 임계 경고, 계약 스윕 강제, 머지 직후 스모크)이 겹겹이 있어도 done 을 "동작한다"와 등치시킬 수는 없다.
9-2. 검증 강제의 구멍은 닫혔다 — 시점과 잔여를 구분한다
포트폴리오 작성 중 코드 전수 조사에서 실측 결함 4건을 발견했다. 감추지 않고 실었고, 그대로 두지도 않았다 — 4건 전부 수정하고 각각을 잡는 경계 통과 테스트를 함께 넣었다. 진짜 산출물은 수정이 아니라 그 테스트다. 수정만 하면 같은 클래스가 다음 리팩터링에 재발한다.
| 결함 | 실측 내용 | 수정 + 그것을 잡는 테스트 |
|---|---|---|
| 증거 완전성 미검사 | 결과 문서 검증기가 rigorous 플래그를 검증 함수에 전달하지 않아 L1~L4 증거 0건 결과가 exit 0 통과 | 플래그 전달 + CLI 를 프로세스로 spawn 해 증거 0건 문서가 exit 2 로 떨어지는지 확인 |
| L2 변이 fail-open | 프로세스 실행 실패가 "변이를 잡았다"로 집계 — 바로 옆 타임아웃 분기는 정확히 그 오류를 막아 뒀다(인접 분기 비대칭) | 하네스 오류로 라우팅해 분모에서 제외 + 별도 카운터. 존재하지 않는 명령을 줬을 때 kill 率이 부풀지 않는지 확인 |
| 스텁 가드 dead code | 코드는 === true 로 판정, 스키마는 문자열로 선언 → 가드 도달 불가 |
코드를 스키마에 맞춤. 스키마 검증을 통과한 실제 row 가 가드를 발화시키는지 확인 |
| 테스트/소스 분리 우회 | 판정이 하드코딩 경로 접두사라 소스 디렉터리에 함께 있는 테스트 수정이 통과 | 설정의 테스트 glob 참조로 전환. colocate 테스트 수정이 위반으로 잡히는지 확인. 단 이 행의 닫힘은 불완전했다 — 아래 참조 |
4건 중 3건이 같은 형태였다 — 경계 양쪽이 각자 초록인데 경계를 통과하는 테스트가 없다. §7-1 사고가 처방한 것과 정확히 같은 클래스이고, 그 룰이 대상 프로젝트에 요구하는 규율을 하네스가 자기 자신에게는 적용하지 않고 있었다는 뜻이다(§4-8).
그리고 네 번째 행의 "닫혔다"는 이르렀다. 이후 그린필드 스펙을 실제로 파이프라인에 태워 보니(§9-3), glob 수정이 드러내기만 하고 닫지는 못한 결함 두 개가 그 뒤에 있었다 — 검사 기준이 roll 베이스라 TDD 흐름에서 원리적으로 통과 불가(강제가 아니라 마비)였고, git diff 가 untracked 를 안 세서 구현이 직접 작성한 테스트는 아예 안 보였다(편집만 잡고 작성은 놓침). 상세와 수정은 §4-2 과장 금지선에 있다. 여기서 배울 것은 결함 자체가 아니라 탐지 방법이다 — 이 세 결함은 전부 코드 전수 조사로 나왔지만, 그 조사가 닫았다고 판정한 것이 실제로 닫혔는지는 파이프라인을 끝까지 돌려야만 알 수 있었다. 정적 조사는 "이 코드가 틀렸다"까지 답하고, "이 수정이 실제로 작동한다"는 답하지 못한다.
남은 것 두 가지를 분명히 한다.
- 면제는 막지 않고 센다. L3/L4 의 n/a, L1 경고 강등, L2 임계 하향은 여전히 에이전트가 켤 수 있다. 바뀐 것은 모든 면제가 단일 seam 을 지나 원장에 남고 표면별 카운트가 노출된다는 점이다(§4-8). 막지 않은 이유는 §9-1 과 같다.
- 수정은 앞으로를 담보한다. 본문의 70장은 이 수정과 계측 이전에 검증된 것이므로, 그 스토리들의 검증 강도를 소급해 올려 말할 수 없다.
9-3. 이식은 실증됐지만 경제성은 아직이다
이식성은 이 하네스의 투자 회수 논거 전체가 걸린 축이라(§9-4) 리팩터링이 아니라 가설 검증으로 다뤘다.
달성된 것: 카드 스키마의 ID 접두사 정규식과 팀 enum 이 설정에서 로드 시점에 합성되도록 바뀌어, 갓 만든 프로젝트의 카드가 더 이상 스키마 위반을 내지 않는다(새 발명이 아니라 결과 스키마가 이미 쓰던 "enum 을 코드에서 스키마에 주입해 drift 를 구조적으로 불가능하게" 하는 패턴의 미적용 지점을 메운 것이다). 카드 ID 접두사의 단일 대문자 제약이 풀렸다. 두 번째 트래커 어댑터가 실제 왕복으로 검증됐다. 그리고 결정적 증거로 — 빈 저장소에서 초기화 → 앵커 → 카드 → 실행까지 카드 1장이 완주했고, 그 과정에서 하네스 코드 수정이 0이었다.
이후 표본을 카드 1장에서 체인 전체로 넓혔다. 두 번째 프로젝트(링크 단축 서비스)에서 타깃 저장소를 시드 커밋으로 되돌리고 파생 산출물을 전부 지운 뒤, 자연어 스펙 문서 한 장만 남기고 전 구간을 돌렸다 — 앵커 19개(수리 1라운드 만에 합의) → 카드 3장 + 게이트 1장(19개 앵커가 정확히 한 장씩에 배정) → 코드 스토리 3장 전부 검증 통과 → PR. 관측 게이트는 설계대로 사람을 기다리며 열리지 않았다. 하네스 게이트를 믿지 않고 실제로 서버를 띄워 HTTP 로 스펙 문장을 하나씩 확인했다 — 201 응답 형태, 6자 base62, 302 + Location, 멱등 생성, 코드 유일성, 미지 코드 404, 그리고 30일 경계(29일 302 / 정확히 30일 410 / 31일 410)까지 13개 주장 전부 성립.
그러나 "하네스 수정 0" 은 이 규모에서 더는 성립하지 않는다. 3장을 완주시키기까지 roll 을 세 번 돌렸고 하네스 결함 4건을 고쳐야 했다 — 앵커 채택이 접두사를 잘못 고르는 문제(§4-1), 진단 패널이 자기 출력 스키마를 못 보던 문제, 테스트/소스 분리 게이트가 TDD 에서 통과 불가였던 문제와 untracked 를 못 보던 문제(§4-2·§9-2), 그리고 그 수정이 CRLF 체크아웃에서 낸 회귀. 그러므로 정확한 주장은 이렇다 — 카드 1장 규모에서는 수정 0으로 이식되고, 자연어 스펙에서 PR 까지의 전 구간에서는 아직 아니다. 다만 네 결함 중 셋은 이식 자체의 결함이 아니라 원래 프로젝트에서도 잠재해 있던 결함이 새 레이아웃(콜로케이트 테스트·의존성 0)에서 처음 발화한 것이고, 넷 다 수정이 대상 프로젝트가 아니라 하네스에 남았다.
달성되지 않은 것: 이건 기계적 이식 가능성의 증명이지 경제성의 증명이 아니다. 두 번째 프로젝트는 실무 프로젝트가 아니라 연습용 예제이고, 카드 3장 규모다. 두 번째 실프로젝트에서 실제 절감이 났는지는 여전히 측정된 바 없다. 또한 에이전트 CLI 지원은 여전히 2종이며(인접 도구는 30+/40+/11 플랫폼), 이는 의도적으로 안 한 축이다 — N종 지원보다 어댑터가 실제로 교체 가능함을 2종으로 증명하는 편이 가치가 크다고 판단했다.
9-4. 답하지 못하는 두 가지 질문 (가장 날카로운 반론)
이 문서가 정직하게 답할 수 없는 질문 두 개를 먼저 적는다. 회피하면 나머지 주장도 신뢰를 잃기 때문이다.
(1) "하네스를 만드는 데 든 비용은 얼마인가?"
측정할 수 없다. 저장소 이력이 중간에 재초기화돼 커밋 수로 환산이 불가능하고, 개발 시간을 별도로 기록하지 않았다. 분명한 것은 14.2만 LOC 규모의 하네스를 만드는 비용이 단일 프로젝트에서 회수한 추정 절감액(중간 시나리오 약 ₩2,800만)을 넘었을 가능성이 높다는 것이다. 이 하네스의 투자 회수 논거는 단일 프로젝트 절감이 아니라 재사용(다른 프로젝트로의 이식)에 있다. 그 재사용은 §9-3 대로 기계적으로는 실증됐으나 경제적으로는 여전히 미검증이다 — 두 번째 프로젝트에서 자연어 스펙이 PR 까지 완주한다는 것과 두 번째 실프로젝트에서 절감이 난다는 것은 다른 명제이고, 그 완주조차 하네스 결함 4건 수정을 요구했다. 따라서 "이 하네스가 경제적으로 이득이었다"는 주장은 이 문서 범위에서 여전히 성립하지 않는다.
(2) "성과는 하네스 덕인가 모델 덕인가?"
분리할 수 없다. 대조 실험(ablation)을 하지 않았다 — 같은 카드를 하네스 없이 동일 모델에 맡겼을 때의 결과가 없다. 따라서 "95.9% 는 하네스의 성과"라고 말할 수 없다.
말할 수 있는 것은 좁다: 하네스가 존재하지 않았다면 성립할 수 없는 구간이 무엇인지다. 카드 사이의 의존 순서 계산, 프론티어가 빌 때까지의 자동 재시동, 결정적 프로그램이 판정하는 4계층 게이트, 머지 검증을 통과해야만 상태가 전이되는 규칙, 실패의 enum 분류와 그에 따른 재시도 예산 — 이들은 모델 능력과 무관하게 파이프라인이 제공한 것이다. 반대로 "코드 품질이 좋았다"거나 "설계 판단이 적절했다"는 부분은 모델 기여와 분리 불가하다.
9-5. 표본과 평가의 한계
roll 14회 / 스토리 73장 / 실무 프로젝트는 하나다(두 번째 프로젝트는 카드 3장짜리 연습용 예제라 표본을 늘려주지 않는다 — §9-3). 공개 벤치마크에 올린 적이 없어 외부 비교가 불가능하다. 정식 eval 하니스·골든 데이터셋·궤적 평가가 없다 — 리뷰 에이전트는 판정자이지 eval 체계가 아니다.
9-6. 로드맵 (우선순위 순)
이전 로드맵의 5개 항목(증거 완전성 검사 연결 · L2 변이 fail-open 수정 · 스키마의 프로젝트 결합 제거 · 관측 게이트 자동화 일부 · 두 번째 트래커 어댑터 실증)은 실행 완료됐다. 여기에 면제 원장과 stale edge 도구화가 더해졌다. 남은 것은 아래다.
- 면제 분포로 다음 축을 결정 — 원장 데이터가 쌓이면 어느 방어선이 실질 비활성인지 판정한다. 특정 게이트에 면제가 집중되면 검증 깊이 축, 전 표면에 고르게 낮으면 이식성 축, 특정 스토리 유형에 집중되면 스펙 작성 단계 축으로 재조준한다. 분기 규칙을 데이터가 오기 전에 미리 고정해 뒀다 — 데이터를 본 뒤에 규칙을 정하면 그건 사후 합리화다.
- 스모크 커버리지 확대 — 현재는 얇은 수직 슬라이스 1회(§9-1). 어디까지 넓힐지는 (1)의 데이터에 종속된다.
- 두 번째 실프로젝트 이식 — 경제성 논거가 여기 걸려 있다(§9-3·§9-4). 카드 1장 리허설이 아니라 실제 프로젝트 한 바퀴.
- stale edge 의 의미론 판정 — §7-4 의 원리적 잔여. "이 엣지가 여전히 참인가"를 자동 판정할 방법은 아직 없다.
- 회귀 표본 확보 — 하네스 자체 변경의 회귀를 잡을 골든 카드 세트가 없다. (1)의 계측이 쌓이면 그 데이터가 최소 표본의 씨앗이 된다.
10. 부록
10-1. 기술 스택
| 영역 | 구성 |
|---|---|
| 런타임 | Node.js (의존 최소, 표준 라이브러리 위주) |
| 데이터 | 카드 1장 = YAML 파일 1개, JSON Schema + ajv 검증 |
| 검증 | TS 컴파일러 API(정적) · AST 변이 + 테스트 러너 · JSON deep-diff · 프로세스 spawn |
| 격리 | git worktree (스토리별), 커밋 창 뮤텍스 직렬화, N→1 커밋 압축 |
| 관측 | 패스별 NDJSON 이벤트 tee + 백엔드별 정규화기 |
| 추상화 | 에이전트 백엔드 어댑터(2종) · 트래커 provider(2종) |
| 정적 분석 | ESLint + sonarjs + jscpd, 변경 빈도 가중 우선순위 |
코드 규모: 추적 파일 633개 / 14.2만 LOC. 소스 .js 254파일(6.80만 LOC), 테스트 284파일(6.39만 LOC), 테스트:소스 LOC 비 0.94.
10-2. 수치 재현
에이전트 실행 집계는 단일 스크립트로 재생성된다. 이 스크립트는 다음 3개 함정을 코드 레벨에서 처리한다.
| 함정 | 나이브 결과 | 처리 | 교정 결과 |
|---|---|---|---|
| 재실행 no-op | verified:true 114 |
skip 사유 유무 분리 + ID 중복 제거 | 고유 85 |
| 재시도 스냅샷 중복 | 543패스 / $4,012 | 경로로 본집계와 분리 | 385패스 / $2,754 |
| 병렬 구간 이중계산 | 144.4h 를 경과시간으로 오독 | 1분 버킷별 활성 패스 수 집계 | 가동 84.8h, 평균 1.81, 최대 13 |
여기에 사람 직접 구현 8장 제외(§6-1)를 더해 최종 73/70 을 얻는다.
병렬도 지표는 두 번 폐기하고 세 번째를 채택했다. (1) 연산시간 / 경과시간 은 유휴와 병렬도가 섞여 1 미만 값이 나왔다. (2) 패스 파일의 [첫 이벤트, 마지막 이벤트] 구간 합집합은 재시도가 같은 파일에 덧붙어 연산시간의 10배로 부풀었다. (3) 채택 — 이벤트를 1분 버킷으로 접고 버킷마다 이벤트를 낸 서로 다른 패스 수를 센다. 가정이 없고, 144.4/84.8 ≈ 1.70 과 독립적으로 정합한다.
10-3. 조사 방법
이 문서의 근거는 4개 채널에서 왔다: 하네스 코드(전수 조사 시점 582파일 · 현재 633), 운영 룰 문서(29개 룰), 실행 아티팩트(3,081파일 / roll 14회), SSOT 스냅샷(카드 200장). 이후 두 번째 프로젝트의 그린필드 완주(§9-3)가 다섯 번째 채널로 더해졌고, 그것이 정적 조사가 닫혔다고 판정한 것 중 하나를 다시 열었다(§9-2).
기술 주장은 코드 전수 조사로 검증했고, 조사 브리프에 "근거 없는 주장 금지, 파일::심볼 인용 필수, 반례를 적극적으로 찾을 것" 을 명시했다. 그 결과 이 문서의 초안 주장 중 최소 5건이 반증돼 수정됐다 — done 카드의 불변성이 코드 강제가 아니라 규약이라는 점, 증거 완전성 검사가 연결돼 있지 않다는 점, "5개 페르소나 병렬"이 실제로는 스토리 내 순차라는 점, 진단 패널에 다수결이 없다는 점, 설정 주도 이식성이 스키마 층에서 깨진다는 점이다.
반증된 주장을 지우고 남은 것만 싣는 대신, 반증 자체를 본문에 남겼다. 각 절의 "과장 금지선"이 그것이다.
그리고 반증 중 고칠 수 있는 것은 고쳤다. 위 5건 가운데 증거 완전성 미검사와 스키마 층 결합은 §9-2 · §9-3 대로 수정됐고, 나머지 3건은 사실 서술의 정정이라 본문 문장 자체가 교정됐다. 이 문서에서 조사 시점과 수정 시점을 섞지 않기 위해, 수정된 항목은 "무엇이 발견됐고 무엇으로 닫혔는가"를 함께 적었다 — 발견을 지우면 그 발견을 가능하게 한 방법론도 함께 지워지기 때문이다.