
AI 코딩 에이전트의 '완료'를 SQLite·JSONL·Evidence로 검증 가능하게 만들기
요약
AI 코딩 에이전트의 작업 완료를 검증하기 위해 SQLite, JSONL, HTML을 활용한 상태 머신 기반의 CLI 도구 'Project Loop Harness(pcl)'를 소개합니다. 에이전트의 주관적인 '완료' 선언 대신, 해시 고정된 증거(Evidence)와 상태 전이를 통해 작업의 재현성과 신뢰성을 확보하는 방법을 다룹니다.
핵심 포인트
- SQLite를 활용해 Goal, Task 등 현재 상태를 관리하는 System of Record 구축
- JSONL을 통한 추가형 감사 투영(Append-only Audit Projection)으로 변경 이력 추적
- SHA-256 해시를 이용해 테스트 결과 등 증거(Evidence)를 고정하여 검증 가능성 확보
- 상태 머신 기반의 워크플로우를 통해 에이전트 작업의 연속성과 신뢰성 강화
AI 코딩 에이전트는 코드를 변경하는 단계까지는 빨라졌습니다.
하지만 실제로 여러 세션, 여러 에이전트로 개발을 계속하다 보면 다른 문제가 남습니다.
- 무엇을 기준으로 '완료'라고 했는가
- 테스트나 리뷰의 증거는 어디에 있는가
- 다음 조작을 에이전트가 진행해도 되는가
- 어디서부터 인간의 판단이 필요한가
- 세션이 바뀌어도 동일한 상태에서 재개할 수 있는가
저는 이 운용을 로컬에서 관리하는 CLI,
Project Loop Harness (pcl)
를 개발하고 있습니다. 2026년 7월 14일에 v0.5.0을 공개했습니다.
이 기사에서는 단순한 출시 소개가 아니라, 다음 3가지를 구현하고 dogfooding(직접 사용)한 결과를 정리합니다.
- SQLite, JSONL, HTML을 어떻게 역할 분담했는가
- 대화상의 '완료'를 해시 고정 Evidence와 상태 전이로 어떻게 바꾸었는가
- 공개 PyPI 패키지만을 사용하는 재현 데모로 어디까지 검증할 수 있었는가
대시보드보다 먼저, 상태 머신(State Machine)을 만든다
Project Loop Harness의 기본 루프는 다음과 같은 형태입니다.
Goal -> Harness -> Workflow -> Agent Jobs -> Evidence -> Verification
-> State -> Dashboard -> Stop / Retry / Escalate
여기서 중심에 있는 것은 대시보드가 아니라, 가드(Guard)가 있는 상태 머신입니다.
구현에서는 다음 3가지를 의도적으로 분리했습니다.
SQLite: 현재 상태의 system of record
Goal, Task, Feature, Test, Evidence 등의 현재 상태는 SQLite에 저장합니다.
에이전트가 SQL을 직접 작성하는 것은 금지하며, 상태 변경은 pcl 명령 또는 내부 서비스 함수를 통합니다.
이를 통해 예를 들어 Goal을 닫는 처리에서 "증거가 있는가", "완료 조건을 충족하는가"를 동일한 경로로 검사할 수 있습니다.
JSONL: 추가형 감사 투영 (Append-only Audit Projection)
상태 변경마다 이벤트를 남깁니다. SQLite가 현재 상태라면, JSONL은 추가형 감사 투영입니다.
현재 상태 조회를 이벤트 재생(Event Playback)에만 의존하지 않고, 그럼에도 "언제 무엇이 바뀌었는지"를 나중에 추적할 수 있도록 했습니다.
HTML: 인간을 위한 생성 뷰 (Generated View)
HTML 대시보드는 SQLite의 상태로부터 생성합니다. 인간에게는 보기 편한 반면, 에이전트는 HTML을 상태로서 읽지 않습니다. 기계용으로는 CLI의 JSON 출력이나 dashboard-data.json을 사용합니다.
이 경계를 정한 이유는, 외관을 직접 편집하면 표시 내용과 실제 상태가 쉽게 어긋나기 때문입니다.
'완료'를 Evidence ID로 바꾼다
대화만 한다면, 에이전트는 "테스트를 통과했습니다", "완료했습니다"라고 말할 수 있습니다.
하지만 다음 세션에서 보면, 그 주장을 재확인할 수 없는 경우가 있습니다.
pcl에서는 테스트 출력이나 성과물을 Evidence로 등록하고, 필요하다면 파일을 복사하여 SHA-256을 고정합니다.
pcl evidence add \
--file artifacts/acceptance.txt \
--summary "수락 명령이 PASS한 출력" \
...
Task를 완료할 때는 해당 Evidence ID를 이유로 남깁니다.
나아가 pcl finish --emit-packet을 통해, 설정된 체크, strict validation, 리포지토리 상태를 completion packet으로 묶습니다.
pcl finish --emit-packet --goal G-0001 --json
이번 데모에서는 packet의 결과가 COMPLETED_VERIFIED가 된 후, Goal을 해당 packet Evidence에 연결하여 닫습니다.
인간의 승인이 없는데 story approve나 verification approved를 기록하지는 않습니다.
기계적으로 확인할 수 있는 사실과, 인간만이 결정할 수 있는 판단을 분리하기 위해서입니다.
공개 PyPI 버전만으로 재현했다
리포지토리에는 v0.5.0을 고정 설치하여 일련의 완료 루프를 재현하는 데모 스크립트를 두었습니다.
git clone https://github.com/mocchalera/project-loop-harness.git
cd project-loop-harness/examples/v0.5.0-adoption-demo
./run-demo.sh --keep
스크립트는 새로운 임시 디렉토리와 venv를 생성하며, checkout 내의 Python 코드가 아닌
PyPI에서 project-loop-harness==0.5.0
을 설치합니다.
그 후, 다음을 실행합니다.
init --dry-run
-> init / doctor
-> 자연어 intent로부터 Goal과 Task를 생성
...
2026년 7월 14일 macOS · Python 3.13 환경에서 통합된 스크립트를 실행한 결과는 다음과 같습니다.
시간은 해당 환경에서의 참고치이며, 벤치마크가 아닙니다.
Ran 1 test
OK
DEMO_OK=1
...
strict validation은 에러 0 · 경고 0이었습니다. 임시 디렉토리는 소유 마커(ownership marker)와 경로 prefix를 모두 확인한 후 삭제하며, 실패 시에는 진단용으로 유지합니다.

실제 공개 PyPI 버전 데모에서 생성한 화면. 상세 정보는 접혀 있으며, 사람이 가장 먼저 확인해야 할 5개 항목을 상단에 표시하고 있습니다.
기존 프로젝트 도입은 inspect-first
단순히 테스트만 해보고 싶다면 pipx를 사용할 수 있습니다.
pipx install project-loop-harness
cd /path/to/your-project
pcl init --dry-run --json
비어 있지 않은 프로젝트의 경우, 먼저 dry-run을 수행하여 생성 · 업데이트 · skip 대상을 확인합니다.
내용이 의도한 대로라면 초기화(initialize)를 진행하고, 실제 프로젝트의 명령에 pcl.yaml을 맞춥니다.
pcl init
pcl doctor
pcl validate --strict
...
일반적인 초기화 과정에서는 기존의 AGENTS.md, CLAUDE.md, .gitignore를 유지하며, Project Loop용 마커가 포함된 블록을 추가합니다. 기존의 pcl.yaml도 기본적으로는 교체하지 않습니다. --force는 별도의 명시적인 리뷰 경계(review boundary)입니다.
dogfood를 통해 알게 된 오해하기 쉬운 점
초기화만으로는 '완료'를 검증할 수 없다
새로 생성된 pcl.yaml의 체크는 대상 프로젝트에 맞춰 설정해야 합니다.
테스트 명령이 비어 있다면, pcl finish는 충분한 완료 증거(completion evidence)를 만들 수 없습니다.
이는 자동 추측으로 위험한 명령을 실행하지 않도록 설계된 것이지만, 처음 접하는 사용자 입장에서는 "init을 했는데 왜 finish가 안 되지?"라고 느끼기 쉬운 부분입니다. v0.5.0에서는 doctor와 finish의 진단 기능을 구체화했지만, 도입 경험 측면에서는 아직 개선의 여지가 있습니다.
doctor --strict와 validate --strict는 역할이 다르다
설정이 조정되지 않은 신규 프로젝트에서는 doctor --strict가 프로젝트 이름이나 빈 값 체크를 문제로 취급합니다. 반면, 라이프사이클 상태에 모순이 없다면 validate --strict는 통과합니다.
환경 · 설정의 건전성(healthiness)과 상태 전이의 정합성(consistency)을 모두 동일한 "strict"라는 용어로 부르고 있기 때문에, 이 부분 역시 별도의 설명 없이는 혼동하기 쉽다는 것을 알게 되었습니다.
HTML은 편리하지만, 정답(source of truth)은 아니다
사람은 대시보드를 보고 싶어 하고, 에이전트에게도 동일한 HTML을 읽게 하고 싶어 합니다.
하지만 생성물을 상태(state)로 취급하면, 업데이트 누락이나 표시 편의성이 기계적 판단에 섞여 들어갈 수 있습니다.
따라서 HTML은 마지막까지 human-only view로 유지하며, 에이전트는 JSON 출력과 Evidence를 사용합니다.
v0.5.0의 Council은 실험적인 opt-in
v0.5.0에는 모호하거나 리스크가 높은 작업을 다각도에서 검토하는 Council Profile도 포함되어 있습니다.
단, 이는 기본 경로(default path)가 아닙니다.
- 명확한 작업은 Direct가 기본값
- Council 출력은 조언(advice) Evidence이며, 승인이 아님
- Core는 모델 provider를 호출하지 않음
- 실제 네트워크 · 유료 provider 실행은 Core 외부에서 수행
- 실행을 위해서는 별도의 hash-bound된 인간의 승인이 필요함
현재의 판단은 '기본 채택'이 아니라 '실험을 계속한다'입니다.
기본 루프 (Basic loop) 도입 가치를 검증하기 전에, Council의 기능을 늘리지 않는 방침을 세웠습니다.
이번에 알고 싶은 것
v0.5.0을 공개했지만, 널리 사용되고 있다고 주장할 수는 없습니다.
우선 3명의 초면 사용자(first-time users)를 대상으로 다음 사항을 관찰할 계획입니다.
- 30초 안에 어떤 도구인지 설명할 수 있는가
- dry-run으로부터 기존 파일에 미치는 영향을 판단할 수 있는가
- 처음에 가치를 느끼는 출력은 무엇인가
- agent-safe와 human gate의 경계가 기대한 대로인가
- SQLite, JSONL, HTML의 역할을 설명할 수 있는가
만약 작은 scratch repository에서 시도해 볼 수 있는 분이 있다면, 처음에 막혔던 명령어와
기대했던 결과를 알려주시면 도움이 됩니다. 기밀 정보, 인증 정보,
프로젝트의 원천 데이터(raw data)는 Issue에 첨부하지 마세요.
Discussion

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