AI를 시스템의 한 구성요소로 설계하기: 내가 하네스 엔지니어링하는 방식

AI에게 더 많은 실행을 맡기면서도, 무엇을 믿을 수 있는지는 사람이 추적할 수 있게 만든 개발 방식

최근 진행중인 산책관련 프로젝트에서 설계한 100개 가량의 테스트가 모두 통과했다. 프로덕션 빌드가 끝났고, Android 에뮬레이터에서 앱을 강제로 종료했다가 다시 실행하는 시나리오도 성공했다. 진행 중이던 산책 상태를 복원했고, 이어서 특정 지점에서 산책을 마쳤을 때 완료 기록은 정확히 한 건 남았다.

하지만 내가 사용하던 작업 흐름은 여기서 일부러 멈췄다. 테스트 결과와 화면을 중간보고서에 정리한 뒤, 다음 단계로 넘어가지 않고 사람의 판단을 기다렸다.

이유는 단순했다.

“구현이 작동한다”와 “이 결과를 받아들여도 된다”는 다른 판단이다.

자동 검사는 정의된 조건에서 코드가 작동했다는 사실을 알려준다. 하지만 그 조건이 애초에 맞았는지, 에뮬레이터에서 얻은 결과를 실물 기기나 운영 환경까지 확대해 말해도 되는지, 남은 위험을 이번 변경에 포함할지 다음 작업으로 넘길지는 테스트가 결정하지 않는다.

실제로 이 작업에서도 사람의 검토 뒤 변경을 여러 PR로 나누고, 확인된 결과의 범위를 Android 에뮬레이터 시나리오로 한정했다. 실물 기기 검증과 실제 서버 전송은 확인하지 않은 범위로 남겼다.

AI가 결과를 만들었다고 시스템의 판단이 끝난 것은 아니다.

Pixel 7 에뮬레이터에서 force-stop 전, 재실행 후 발견, 이어가기, 완료 기록 확인까지 네 단계로 남긴 산책 세션 복원 검증 화면

나는 AI를 단순히 개발 도구라고 생각하지 않는다. 이 글에서 AI는 주로 코드베이스를 탐색하고, 도구를 사용하며, 구현과 수정을 수행하는 AI coding agent를 뜻한다. 그 안의 LLM은 판단과 생성을 담당하는 강력하지만 확률적인 구성요소다. 내가 설계하려 한 것은 이 agent가 어떤 정보를 받고 어디까지 행동하며, 무엇으로 검증되고 언제 사람에게 판단을 돌려주는지에 관한 주변 시스템이다. 이 글에서는 그 일을 Harness Engineering이라고 부른다.

모델에게 더 많은 일을 맡길수록 모델 밖에서는 오히려 더 분명한 구조가 필요했다. 무엇을 보게 할지, 어디까지 자율적으로 행동하게 할지, 결과 중 무엇을 믿을지 정해야 했다. 내가 사용하는 방식은 결국 세 질문으로 압축된다.

  • Context — AI에게 무엇을 보여줄 것인가?
  • 책임 경계 — AI에게 어디까지 맡길 것인가?
  • 검증 — AI가 만든 결과 중 무엇을 믿을 것인가?

이 글에서 말하는 것

  • AI coding agent에게 실제 개발 작업을 맡기며 운영해 본 workflow
  • Context를 선택하고, 자율 실행 범위를 정하고, 결과를 검증하는 방식
  • plan, baseline, 테스트, 중간보고와 사람의 판단을 한 작업 흐름으로 연결한 경험
  • 작업 중의 실패를 다음 규칙·검증·작업 방식에 다시 반영하는 과정

이 범위는 실제로 만든 것과 아직 만들지 못한 것을 구분하기 위한 기준이다. 경험으로 서술하는 주장은 plan, commit, report, test output 가운데 하나와 연결하고, 나머지는 일반적인 생각이나 앞으로 확인할 과제로 구분한다.

AI coding agent를 확률적 구성요소로 두고 Context·책임·검증 세 질문이 둘러싸는 구조 다이어그램
 

1. 모델의 능력과 시스템의 신뢰성은 같은 말이 아니다

처음 AI coding agent를 사용할 때는 프롬프트를 잘 쓰는 것이 가장 중요한 문제처럼 보였다. 목표를 자세히 쓰고, 제약을 나열하고, 완료 조건을 붙이면 결과가 좋아졌다. 작은 변경에서는 이것만으로 충분한 경우도 많았다.

문제는 저장소와 작업의 수명이 길어지면서 나타났다. 프론트엔드만 보더라도 웹뷰, React Native 셸, 공유 패키지, 생성된 API 계약이 연결되어 있었다. 서버가 소유하는 계산 로직을 프론트에 복제하면 안 됐고, 태그는 하나의 정본만 따라야 했으며, 생성된 계약 파일은 직접 수정해서는 안 됐다. 위치 권한, 지도 SDK 등은 서로 다른 실패 경계를 가졌다. 한 번의 프롬프트가 정확해도 작업 중 오래된 문서를 읽거나 현재 역할 밖의 파일을 수정하면 결과는 쉽게 틀어졌다.

같은 목표를 주어도 agent가 선택하는 탐색과 구현 경로는 달라질 수 있었다. 어떤 세션에서는 잘 찾았던 규칙을 다른 세션에서는 놓쳤고, 테스트가 통과하면 그 결과의 의미를 자신 있게 확대하기도 했다. 이것을 모델의 결함이라고만 보면 대응은 더 좋은 모델을 기다리거나 프롬프트를 더 길게 쓰는 데 그친다.

나는 문제를 다르게 보기 시작했다.

확률적으로 동작하는 구성요소를 사용하는 시스템이라면, 그 변동성을 전제로 나머지 구조를 설계해야 한다.

데이터베이스나 외부 API도 실패할 수 있지만 우리는 그것을 시스템 전체와 동일시하지 않는다. timeout, retry 같은 경계를 둔다. AI도 마찬가지다. 모델이 더 유능해지면 더 넓은 탐색과 구현을 맡길 수 있다. 그러나 어떤 사실을 읽었는지, 어떤 권한으로 행동했는지, 무엇을 검증했는지 알 수 없다면 결과를 신뢰할 근거는 부족하다.

이 관점에서 Prompt Engineering, Context Engineering, Harness Engineering은 서로 경쟁하는 표현이 아니다. 관심 범위가 넓어지는 과정에 가깝다.

  • Prompt Engineering은 이번 요청을 어떻게 표현할지를 다룬다.
  • Context Engineering은 이번 판단에 어떤 정보가 들어가야 하는지를 다룬다.
  • Harness Engineering은 무엇을 읽고, 어떤 도구로 어디까지 실행하며, 무엇으로 검증하고, 언제 사람에게 판단을 돌려줄지를 반복 가능한 작업 흐름으로 만든다.

이 글을 쓰면서 접하게된 인상적인 글이 있었다. 해당 글에선 plan, 구현, 검증, PR로 이어지는 팀 하네스와 메인 agent의 context를 아끼는 역할 분리를 접했다. 특히 작업의 기준을 사람이 소유한다는 관점이 인상 깊었다.

내 작업에 적용하면서 두 가지를 더 분명히 했다. 구현 전에는 현재 상태를 baseline으로 고정하고, 구현 후에는 검증 결과가 무엇을 뜻하는지 사람이 다시 확인했다. 성공 수치만 남기지 않고 실행 명령, 원본 보고서, 환경, 실패와 한계도 함께 보관하려 했다.

여기서 baseline, regression test, lint, build, E2E 같은 기법을 Harness Engineering이 새로 발명한 것은 아니다. 모두 소프트웨어 엔지니어링에서 오래 사용한 결정론적 장치다. 내가 한 일은 이 장치들을 확률적인 AI Agent에게 자율 실행을 맡기기 위한 주변 시스템으로 조직한 것이다. AI가 탐색하고 구현한 뒤 검사가 실패하면 다시 수정으로 돌아가고, 검사가 통과해도 결과의 의미와 외부 반영은 사람이 판단하게 연결했다.

따라서 계속 반복되는 언급이지만 이 글의 핵심은 아래 세 질문에 답하는 실제 작업 방식이다.

질문내가 설계하는 것놓치면 생기는 문제
AI에게 무엇을 보여줄 것인가?정본, pointer, 필요한 시점의 검색, 작업 상태오래되거나 무관한 정보로 판단
어디까지 맡길 것인가?실행 권한, 작업 전·후 사람 판단, 외부 반영 경계빠르지만 잘못된 방향으로 자율 실행
무엇을 믿을 것인가?baseline, 여러 검증층, 재현 조건, 한계테스트 통과를 제품 보장으로 확대

이후 등장하는 문서 구조와 실제 사례는 이 세 질문을 어떻게 운영했는지 보여주는 evidence다. 특정 프로젝트 기능이나 제품별 도구가 글의 주인공은 아니다.


2. Context — AI에게 모든 것을 보여주지 않는다

나는 장기 작업에서 정보를 많이 제공하는 것보다, 정보를 역할과 수명에 따라 나누고 현재 판단에 필요한 진입점을 제공하는 것을 먼저 설계한다. 제품과 아키텍처의 장기 결정, 특정 영역에서만 유효한 규칙, 이번 작업의 진행 상태와 측정 결과는 서로 다른 정보다. 모두 중요하다는 이유로 같은 파일에 넣으면 AI는 많이 읽지만 무엇이 지금의 판단 기준인지는 오히려 놓치기 쉽다.

내가 겪은 context 문제는 단순히 token이 부족한 문제가 아니었다.

판단에 필요한 신호와 지금은 필요하지 않은 정보가 같은 공간에서 경쟁하는 문제였다.

그래서 Context를 설계할 때는 세 가지를 우선한다. 사실은 한 곳에서 소유하고, 필요한 순간에 원문을 찾게 하며, 긴 작업의 상태는 대화 밖의 파일에도 남긴다. pointer, 영역별 지침, 필요 시점 검색, 별도 agent, context 압축은 이 원칙을 구현하는 수단이다.

사실은 한 곳에서 소유한다

실제 저장소에는 서비스 범위, 태그의 정본, 프론트와 백엔드의 책임, OpenAPI 생성 경계, 지도 데이터 정책, 개인정보 보존 기간처럼 서로 연결된 결정이 많다. AI가 놓치지 않게 하려고 전부 root 지침에 적으면 처음에는 안전해 보인다. 나도 그렇게 시작했다.

하지만 규칙을 계속 추가하면 문제가 뒤집힌다. 관련 없는 지침이 현재 작업을 가리고, 모든 문장이 중요하다고 쓰여 있으니 실제 우선순위가 사라진다. 같은 규칙을 AGENTS.md, CLAUDE.md, Skill에 복사하면 한쪽만 수정되는 순간 drift가 생긴다. 오래된 작업 메모가 팀 규칙처럼 보이거나, 반대로 중요한 제품 결정이 임시 요약 속에서 사라질 수도 있다.

그래서 문서의 양보다 소유권을 먼저 정했다.

Root map
  ├─ 제품·아키텍처·정책의 정본 문서
  ├─ 현재 코드와 가까운 영역별 지침
  └─ 이번 작업의 plan / report / evidence

root map에는 문서 우선순위, 금지된 변경, 책임 소유권처럼 어느 작업에서도 잃으면 안 되는 경계와 상세 문서의 위치를 둔다. 제품의 상세 규칙은 각 정본 문서가 소유한다. 특정 디렉터리에서 필요한 테스트와 완료 조건은 코드에 가까운 영역별 지침에 둔다. 이번 작업의 승인 범위와 진행 상태는 plan과 report가, 측정 결과는 evidence가 소유한다.

각 정보는 수명도 다르다. 제품 결정은 여러 도구와 사람이 장기간 공유한다. 현재 task의 상태는 자주 바뀌지만 세션이 끊겨도 이어져야 한다. 특정 review에서 발견한 패턴은 다음 review에 도움이 될 수 있지만 곧바로 팀의 정본이 되어서는 안 된다. 이들을 한 파일에 섞지 않는 것이 context 크기보다 더 중요한 이유다.

OpenAI의 AGENTS.md 공식 문서가 설명하는 계층적 지침 탐색은 root의 넓은 map과 가까운 영역의 구체적인 지침을 나누는 한 수단이다. 중요한 것은 파일 이름 자체가 아니라 정보의 소유권과 적용 범위를 구분하는 설계다.

복사하는 대신 가리킨다

제품별 설정 파일이 같은 사실을 다시 소유하지 않게 했다. 현재 CLAUDE.md는 프로젝트 규칙을 복사하는 대신 정본을 가리킨다.

@AGENTS.md

<!-- 이 저장소의 에이전트 지침 정본은 AGENTS.md 하나다. -->

Claude는 이 pointer를 통해 공통 지침을 읽고, Codex는 AGENTS.md를 직접 읽는다. pipeline을 호출하는 Skill에도 실행 조건과 필요한 파일의 위치만 두며, plan과 report, 검증 기록은 일반 Markdown과 명령으로 남긴다. 도구를 바꿀 때 프로젝트의 사실까지 다시 번역하지 않기 위해서다.

도구가 사실을 소유하지 않는다. 도구는 정본을 읽고 실행하는 연결 계층이다.

완전히 vendor-neutral하다는 뜻은 아니다. 일부 Skill과 Hook, review 작업 메모는 여전히 .claude/에 의존해 다른 도구에서는 별도 연결이 필요하다. 현재 지키는 원칙은 핵심 사실의 소유권을 도구 밖에 두고 제품별 연결을 얇게 만드는 것까지다.

필요한 순간에 필요한 원문까지 읽게 한다

모든 상세 문서를 처음부터 넣는 대신 root map과 현재 작업 계획에서 시작해, 판단 직전에 필요한 정본까지 내려간다. 태그를 바꾸면 태그 정본을, 생성 계약을 건드리면 OpenAPI와 설계해둔 monorepo 전략을 읽는 식이다. 파일과 import 관계로 범위를 좁히고, Skill도 선택된 뒤에 필요한 본문과 reference를 연다.

핵심은 적게 읽는 데 있지 않다. 관련 없는 정보는 미루되 결정하기 전에 판단을 바꿀 수 있는 원문에 도달해야 한다. 정본을 너무 늦게 읽으면 이미 잘못된 가정 위에서 구현을 시작하기 때문이다.

MCP와 tool도 현재 작업에 필요한 표면만 열고 긴 결과는 요약과 원문 위치를 분리한다. 다만 외부 도구를 실행해도 되는지는 Context가 아니라 다음 절에서 다룰 책임의 문제다.

대량 탐색은 분리하되 근거까지 돌려받는다

기획 문서 탐색이나 긴 test log 분류처럼 독립적인 일은 별도 agent context로 분리한다. 메인 agent는 전체 계획을 유지하고, 각 역할은 원문을 읽은 뒤 구조화된 결과를 돌려준다.

이 글에서 AI reviewer는 사람 code reviewer가 아니다. 구현 agent와 분리해 git diff의 정본 위반과 명백한 버그를 검사하는 webview-reviewer AI subagent다. 발견 사항을 제안할 뿐 결과의 의미와 공동 코드 반영은 결정하지 않는다.

분리의 목적은 병렬화보다 탐색 로그가 구현 판단과 경쟁하지 않게 하는 데 있다. 그렇다고 “확인했다”는 결론만 받아서는 안 된다. 읽은 정본, 확인하지 못한 범위, 결론을 뒤집을 불확실성과 raw report 위치를 함께 돌려받는다. 내가 정의한 격리는 전달 형태를 바꾸는 일이다.

긴 작업의 기억을 대화에만 맡기지 않는다

긴 작업은 승인 범위, 현재 단계, 결과와 다음 행동을 진행 파일에 남긴다. 구현 뒤 intermediate report에는 계획 대비 편차, 미완성, 사람이 결정할 항목을 구조화한다. 세션이 압축되거나 다른 날로 넘어가도 대화만으로 상태를 복원하지 않기 위해서다.

context 압축은 무손실 보관이 아니다. 결정뿐 아니라 이유, 실패한 시도, 아직 답하지 못한 질문을 파일에 남긴다. Claude 쪽에는 압축 뒤 핵심 경계를 다시 알려 주는 reminder Hook도 있지만 정본과 어긋날 수 있다. 장기적으로는 고정 문장을 복제하기보다 정본과 현재 작업 상태를 다시 불러오는 편이 낫다.

이 원칙을 설명하는 root 문서부터 이미 너무 크다

글을 쓰면서 확인했지만 Context의 한계는 존재한다. 작은 root map을 지향하지만 현재 AGENTS.md는 442줄, 약 4만 bytes다. OpenAI 공식 문서가 설명하는 Codex의 기본 combined project instruction 한도 32 KiB보다 root 파일 하나가 더 크다.

중요한 결정을 놓칠까 봐 계속 root에 추가한 결과, “작은 map과 가까운 영역별 지침”이라는 원칙을 스스로 위반했다. 따라서 root context를 작게 유지해 효과를 봤다고 주장할 수 없다. 실제로 적용한 것은 정본 우선순위와 pointer, 제품별 연결 분리이며, root 크기는 해결 중인 부채다.

후속 과제는 root에 모든 작업에 필요한 불변식과 정본 색인만 남기고 frontend, backend, data pipeline처럼 가까운 디렉터리로 세부 규칙을 옮기는 것이다. 분리 전후에는 파일 크기만 볼 것이 아니라 잘못 읽힌 규칙, 누락된 지침, 최초 탐색 비용을 같은 작업으로 비교해야 한다. 아직 측정하지 않은 개선 효과를 측정하고 더 좋은 구조를 설계하는것이 다음 목표이지 않을까한다.

AGENTS.md root map이 정본 문서·영역별 지침·작업 상태로 나뉘어 현재 판단에 필요한 원문만 연결되는 context routing 다이어그램
 

3. 책임 경계 — AI에게 모든 결정을 맡기지 않는다

permission prompt가 많다고 사람이 시스템을 통제하는 것은 아니다

AI에게 자율성을 주기 시작하면 가장 먼저 보이는 사람 개입은 permission prompt다. 저장소 밖에 파일을 쓰거나 네트워크에 접근하거나 외부 서비스의 상태를 바꾸려 할 때 도구가 멈춰 묻는다. 이 경계는 중요하다. sandbox는 기술적으로 접근 가능한 범위를 제한하고, approval이나 permission 규칙은 어떤 행동 전에 사람에게 물을지를 정한다.

하지만 명령을 실행해도 되는지 묻는 것과, 그 명령으로 풀려는 문제가 맞는지 묻는 것은 다른 일이다. pnpm test 실행을 허용했다고 성공 조건에 동의한 것은 아니다. 파일 수정을 승인했다고 측정 결과를 제품 전체의 성과로 말해도 된다는 뜻도 아니다.

오히려 모든 tool call에서 사람을 멈춰 세우면 attention이 값싼 확인에 소모된다. 정작 제품 의도, 범위, 결과 해석처럼 AI가 대신 책임질 수 없는 판단은 피곤한 상태에서 형식적으로 통과시킬 수 있다.

나는 사람의 개입을 횟수가 아니라 판단 가치가 높은 위치에 배치하려 한다. 사람이 집중할 질문은 네 종류다.

시점사람이 답할 질문자동화가 대신하기 어려운 책임
행동 직전이 명령이나 외부 호출을 실행해도 되는가?보안, 비용, 외부 상태 변경
작업 전우리가 풀 문제가 맞는가? 무엇을 성공으로 볼 것인가?제품 의도, 범위, 비범위
작업 후이 검증 결과로 어디까지 말할 수 있는가?결과 해석, trade-off, 한계
외부 반영 전공동 코드나 운영 환경에 반영할 것인가?협업과 release 책임

이 pipeline에서는 편의상 작업 전 판단을 G0, 작업 후 판단을 G1이라고 불렀다. Harness Engineering의 표준 용어가 아니라 두 판단 시점을 구분하려고 개인적으로 축소해서 붙인 이름이다.

작업 전에는 문제·범위·성공 기준을 확인한다

AI는 관련 문서를 탐색하고 plan과 대안, 검증 시나리오를 제안할 수 있다. 그러나 이번에 어떤 사용자 문제를 풀지, 무엇을 의도적으로 하지 않을지, 어떤 evidence가 있어야 완료라고 볼지의 최종 책임은 사람에게 둔다.

이 단계가 중요한 이유는 잘못된 구현을 막는 데만 있지 않다. 구현할 필요가 없는 문제를 발견하는 데도 있다.

Android 위치 권한을 셸에서 직접 요청하는 ensure 로직을 추가하려던 작업이 있었다. 이전 검증에서 권한 dialog를 보지 못했기 때문에 새 코드가 필요하다고 생각했다. 하지만 구현 전에 현재 상태부터 다시 확인했다. 앱을 재실행하고 첫 위치 요청을 발생시키자 기존 geolocation library가 시스템 dialog를 정상적으로 열었다. 사용 중 허용을 선택한 뒤 위치 기능도 동작했다.

원인은 이전 검증에서 pm grant로 권한을 미리 부여한 것이었다. 해결할 제품 결함이 아니라 검증 환경의 가정이 잘못된 것이었다. 결과는 코드 0줄이었다.

AI에게 곧바로 구현을 맡겼다면 이미 동작하는 경로 위에 중복 권한 로직을 만들 수 있었다. 이 경험 뒤로 작업 전 질문은 “무엇을 구현할까?”보다 한 단계 앞에서 시작한다.

정말 해결할 문제가 존재하는가? 현재 상태에서 같은 조건으로 재현되는가?

작업 후에는 통과 여부가 아니라 의미를 확인한다

작업이 끝난 뒤 AI와 자동화는 테스트 결과, 번들 변화, 실패 로그, 계획에서 벗어난 부분을 모을 수 있다. 그러나 단일 측정을 성능 개선이라고 부를지, emulator 결과를 production 보장으로 확대할지, 남은 결함을 현재 변경에 포함할지는 자동 검사의 참·거짓으로 결정되지 않는다.

그래서 중요한 작업은 검증 결과를 중간보고서로 구조화한 뒤 사람에게 돌려준다. 보고서에는 성공한 검사뿐 아니라 계획 대비 편차, 실패한 시도, 구현하지 않은 범위, 다음 결정을 함께 둔다. 사람이 승인하거나 특정 액션을 요구하기전엔 반영으로 넘어가지 않는다.

이 판단이 필요한 이유는 테스트가 부족해서만은 아니다. 모든 테스트가 의도대로 통과해도 그 테스트가 다루지 않은 환경은 남는다. “정의된 Android emulator 시나리오에서 성공했다”와 “모든 사용자 환경에서 안전하다” 사이의 거리는 테스트 개수로 자동 환산되지 않는다.

작업의 위험에 따라 절차의 무게를 조절한다

모든 오타 수정에 긴 plan과 별도 baseline, 중간보고서를 요구하면 workflow 자체가 개발을 방해한다. 반대로 공개 계약, 데이터·보안 경계, 중요한 상태 전이를 작은 수정처럼 다루면 사람이 판단해야 할 위험이 숨는다.

그래서 위험도와 변경 범위에 따라 검증 강도를 조절한다.

  • 문서 오타나 국소적인 스타일 수정은 범위를 짧게 확인하고 관련 test와 typecheck로 닫는다.
  • 공개 타입이나 여러 패키지의 계약을 바꾸면 plan과 전체 회귀를 포함한다.
  • 상태 유실, 데이터·보안 경계처럼 실패 비용이 큰 작업은 변경 전 baseline, 여러 검증층, 중간보고와 사람의 결과 해석까지 둔다.
  • 가볍게 시작했더라도 작업 중 공개 계약이나 보호 경계를 건드려야 한다는 사실이 드러나면 그대로 진행하지 않고 절차의 강도를 높인다.

위험이 커지면 사람의 판단과 evidence의 강도도 함께 높아지는가다.

내가 중요한 작업에서 사용하는 가장 단순한 흐름은 다음과 같다.

사람: 문제·범위·성공 기준 확인

변경 전 상태 재현

AI Agent: 탐색·구현·테스트 작성·수정

결정론적 검증과 원본 결과 수집

중간보고

사람: 결과의 의미와 주장 범위 확인

commit·명령·환경·before/after·실패·한계를 검증 기록으로 고정

PR / Release 판단

각 단계는 형식적인 체크박스보다 입력과 출력이 있는 경계에 가깝다. 변경 전 실패가 재현되지 않으면 구현으로 가지 않고 문제 정의나 환경을 다시 본다. 검증 근거가 부족하면 코드를 무작정 다시 쓰기보다 빠진 근거가 baseline인지 test인지 실제 환경 확인인지 찾는다. 사람의 해석이 끝난 뒤에는 새로운 측정을 처음 시작하는 것이 아니라, 이미 수집한 원본과 승인된 해석을 다시 찾을 수 있게 묶는다.

사람의 기준 확인, baseline, AI Agent 구현, 검증, 중간보고, 사람의 해석, 검증 기록, PR·Release까지 이어지는 8단계 workflow 다이어그램

기계적으로 막을 수 있는 위험은 매번 사람에게 묻지 않는다

행동 자체가 위험하고 명령 앞에서 비교적 명확히 판별할 수 있다면 자동 가드레일을 둔다. 현재 Claude 쪽 연결에는 frontend/admin, 생성 계약, _legacy 같은 보호 경로의 변형 명령과 .env 내용 읽기를 막는 PreToolUse Hook이 있다. 흔한 실수를 실행 전에 줄이기 위한 장치다.

하지만 이 Hook을 보안 sandbox라고 부르지 않는다. shell command 문자열을 검사할 뿐이고, 파싱에 실패하면 통과하며, 모든 우회 표현을 이해하지도 못한다. 문자열 가드레일이 있다는 이유로 최소 권한, secret 관리, sandbox, code review가 불필요해지는 것은 아니다.

여기서도 책임을 구분해야 한다. Hook은 “이 행동을 허용할 것인가?”를 다룬다. 무엇을 구현해야 하는지, 검사 결과가 충분한지, 결과를 어떻게 해석할지는 판단하지 않는다. 기계가 싸게 막을 수 있는 반복 위험을 앞에서 줄여 사람의 attention을 더 가치 있는 질문에 쓰려는 것이다.

사람 판단도 비용이므로 측정하고 조절해야 한다

사람이 중요한 기준을 소유한다는 말이 모든 작업에 더 많은 승인을 추가하자는 뜻은 아니다. 판단 단계가 많아지면 대기 시간이 늘고, 반복되는 승인은 형식적인 클릭으로 변할 수 있다. 작업의 위험을 잘못 분류하면 중요한 변경을 너무 가볍게 다루거나 작은 변경을 과도하게 무겁게 만들 수도 있다.

내가 지금 분명히 말할 수 있는 원칙은 하나다.

AI가 실행을 더 많이 담당할수록 사람은 실행이 아니라 기준을 소유해야 한다.

사람이 모든 구현 줄과 명령을 대신 선택하면 AI의 자율 실행에서 얻을 이점이 줄어든다. 반대로 문제, 성공 조건, 결과의 의미, 외부 반영까지 AI에게 넘기면 모델의 자신감이 시스템의 신뢰성처럼 보이기 쉽다. 내가 나누려는 경계는 그 사이에 있다.

작업 전·실행·작업 후·외부 반영 단계에서 사람, AI Agent, 자동 검사의 책임을 나눈 swimlane 다이어그램
 

4. 검증 — AI가 만든 결과를 그대로 믿지 않는다

검증의 목적은 신뢰할 범위를 정하는 일이다

내가 검증을 설계할 때 먼저 묻는 것은 “테스트가 통과했는가?”가 아니다. 이 변경이 어떤 실패에서 출발했고, 어떤 조건에서 작동했으며, 어느 경계는 아직 확인하지 못했는지를 함께 설명할 수 있는가를 묻는다.

AI가 만든 변경에 테스트를 많이 붙였다는 사실만으로는 부족하다. 기존 동작과 새 경계를 각각 어떻게 확인했는지, 사용자의 흐름을 실제로 재현했는지, 어느 commit과 환경에서 실행했는지를 연결해야 한다. 그래서 검증을 한 줄짜리 test 명령이 아니라 서로 다른 실패를 담당하는 여러 층으로 운영한다.

위에서도 말했지만 모든 작업에서 아래 과정을 모두 실행하는것은 아니다. 작업의 목적에 따라 필요한 과정을 실행할 뿐이다.

Baseline — 출발점을 고정한다

변경 전 실패가 실제로 존재하는지, 무엇을 같은 조건으로 비교할지를 확인한다. baseline이 없으면 after의 성공 화면이 같은 문제를 고친 것인지 판단하기 어렵다. 반대로 문제가 재현되지 않으면 곧바로 구현하지 않고 환경이나 문제 정의를 다시 봐야 한다.

앞 절의 Android 권한 사례는 baseline이 항상 before → after 개선을 만드는 것이 아님을 보여준다. 기존 상태를 다시 재현했더니 애초에 해결하려던 실패가 존재하지 않았고, 따라서 after를 만들 이유 자체가 사라졌다. AI 활용의 성과는 생성한 코드 양뿐 아니라 불필요한 구현과 과장된 결론을 얼마나 일찍 멈췄는가로도 봐야 한다.

Regression — 이미 되던 것을 지킨다

새 기능의 테스트만 통과해도 기존 계약과 사용자 흐름이 깨질 수 있다. 관련 unit test뿐 아니라 변경이 걸치는 패키지의 기존 test, typecheck, 생성 계약 drift를 함께 본다. 회귀 검증은 새 기능이 완전하다는 증거가 아니라, 이전에 고정한 동작이 이번 변경 뒤에도 유지된다는 증거다.

Edge — 정상 흐름 밖의 불변식을 확인한다

Edge test를 작성할 때는 구현의 모든 줄을 따라가기보다 “두 번 호출해도 한 번만 반영되는가?”, “손상된 입력이 다음 실행까지 남는가?”, “외부 의존성이 없을 때 무한히 재시도하는가?”처럼 결과의 불변식으로 질문한다. 구현 방법이 달라져도 지켜야 할 성질을 test에 남기기 위해서다.

E2E — 사용자가 겪는 대표 흐름을 끝까지 통과시킨다

unit과 edge test가 모두 통과해도 화면 전환, 앱 재실행, native bridge처럼 여러 경계가 연결되는 지점에서 사용자 흐름은 끊길 수 있다. 대표 시나리오를 실제 실행 환경에서 처음부터 끝까지 통과시키고 화면과 결과를 남긴다. 실물 기기, 다른 OS, 느린 네트워크, production 사용자 전체로 확대하려면 별도 검증이 필요하다.

lint, typecheck, build — 다른 층이 놓친 구조적 결함과 비용을 본다

lint는 소스 패턴으로 판별할 수 있는 결함을, typecheck는 타입 계약을, build는 실제 산출물 생성과 크기 변화를 확인한다. lint가 렌더 중 ref 변경을 잡을 수는 있지만, 그 기능을 지금 만들어야 하는지는 판단하지 못한다. build가 통과해도 사용자의 종단 흐름이 맞다는 뜻은 아니다.

앞서 말했듯 이 검증 기법들이 새로운 것은 아니다. Harness에서 중요한 것은 결정론적 장치들을 AI의 실행과 사람의 해석 사이에 연결하고, 실패가 나오면 AI의 수정으로 돌아가며, 통과한 결과도 사람이 의미를 확인하게 만드는 workflow다.

baseline, regression, edge, golden, static·build 검증층이 주로 잡는 것과 통과해도 남는 것을 정리한 표

숫자가 아니라 재현 조건을 함께 남긴다

검증 결과는 사람의 해석을 거쳐 다시 찾을 수 있는 기록으로 남긴다. 초록색 통과 여부나 최종 숫자만 저장하지 않는다. 내가 기록하려는 최소 구조는 다음과 같다.

baseline_commit: <commit>
after_commits: [...]
commands: [...]
environment: [...]
raw_reports: [...]
before: {...}
after: {...}
failures: [...]
known_limits: [...]

이 기록은 검증에서 이미 얻은 원본, 중간보고서가 구조화한 결과, 사람이 승인한 해석을 commit·명령·환경·실패·한계와 연결해 재현 가능한 package로 고정하는 일이다.

evidence는 데이터가 말할 수 있는 범위를 제한하는 일이다.

테스트 수의 증가는 자동 검증 범위가 넓어졌다는 뜻이지 같은 비율의 품질 향상이 아니다. 한 emulator 시나리오의 성공은 production 무손실 보장이 아니며, 한 번의 bundle 측정도 장기적인 성능 추세가 아니다. 데이터에는 항상 비교 조건과 말하지 못하는 범위를 붙여야 한다.

실제 한 사이클: 강제 종료로 사라지던 산책 세션을 복원하기

이제 Context, 책임 경계, 검증이 실제 작업 하나에서 어떻게 합쳐졌는지 보자. 이 사례는 내 Harness 전체를 대표하는 유일한 작업이 아니라, 한 번의 중요한 작업을 시작부터 외부 반영 경계까지 추적할 수 있는 가장 완전한 local evidence다.

Context는 한 번 통과하고 끝나는 별도 단계가 아니다. root map과 관련 정본은 코드와 정책의 소유권을 알려 줬고, 승인된 plan은 이번 작업의 범위와 성공 조건을 보존했다. intermediate report와 raw evidence는 긴 작업의 상태를 대화 밖에 남겼다.

Context — root map·관련 정본·task plan·report

문제·범위·성공 기준을 사람이 확인

변경 전 실패 재현

AI Agent의 탐색·구현·테스트 작성·수정

Regression·Edge·Golden·lint·build

중간보고

결과의 의미와 주장 범위를 사람이 확인

검증 기록 확정

PR / Release 판단

1. 문제와 성공 조건, 하지 않을 일을 먼저 정했다

해결할 문제는 “앱이 죽어도 잘 되게 하자”처럼 넓게 두지 않았다. 진행 중 산책 세션을 체크포인트로 남기고, 재실행 뒤 이어가기 또는 버리기 경로를 제공하며, 재개 후 완료 기록이 중복되지 않아야 했다. 서버 endpoint가 아직 없는 이벤트는 조용히 사라지지 않고 pending 상태로 보존해야 했다.

동시에 하지 않을 것도 정했다. 첫 사이클에서는 Playwright 기반 비행기 모드 E2E, ErrorBoundary, 실제 HTTP 전송, 실물 기기 검증을 다음 작업으로 넘겼다. 구현 중 연관된 문제가 보이더라도 승인 없이 범위를 넓히지 않게 했다.

사람은 문제와 성공 기준, 비범위를 소유했다. AI Agent는 이 경계 안에서 어떤 파일을 탐색하고 어떤 구현 순서를 선택할지 자율적으로 결정했다.

2. 변경 전 실패를 같은 환경에서 고정했다

예시 프로젝트에서 변경 전 evidence는 commit 9e1a9e5를 기준으로 수집했다. 자유 산책을 시작해 화면에 13초·587m가 표시된 상태에서 앱을 종료하고 다시 실행했다. 앱은 홈으로 돌아왔고 진행 중 세션의 흔적과 복원 UI가 없었다. 완료 기록은 0건이었다. 세션이 React 메모리에만 있어 process와 함께 사라진 것이었다. 이벤트는 localStorage에 쌓였지만 전달을 보장하는 구조는 없었다.

같은 실패를 먼저 고정하지 않았다면 이후 “복원 기능을 구현했다”는 주장을 확인하기 어렵다. 다른 조건에서 성공 화면만 만든 것인지 비교할 기준이 없기 때문이다.

3. AI는 승인된 불변식 안에서 구현하고 수정했다

AI Agent는 기존 산책 session 구조를 탐색하고 체크포인트, 복원 UX, durable outbox를 구현했다. 핵심은 state를 저장했다는 사실보다 뒤에서 검증할 수 있는 불변식을 만든 것이다.

checkpoint에는 만료 조건과 track 크기 제한을 두고, session id로 재개 뒤 완료 기록의 중복을 막았다. 저장 시점과 복원 진입 경로도 명시해 만료·손상 데이터·재진입을 test할 수 있게 했다. 사용자는 유효한 session을 자동 복원하거나 이어가기와 버리기 중 하나를 선택할 수 있었다.

사람이 매 파일의 구현 방식을 지정하지는 않았다. AI는 탐색, 구현, test 작성, lint와 review가 찾은 문제의 수정 순서를 스스로 선택했다. 자율성은 지시가 없는 상태가 아니라 검증할 불변식과 멈출 경계가 분명한 상태에서 생겼다.

4. 검증층은 서로 다른 질문에 답했다

검증은 하나의 큰 통과 표시가 아니었다. 각 검증층은 서로 다른 실패를 확인했고, 어느 하나도 다른 검증을 대신하지 못했다.

검증층이 작업에서 확인한 것결과가 말하지 않는 것
Regressionwebview 테스트 83개 → 112개, 기존 native bridge 테스트 22개 통과테스트 수만큼 품질이 향상됐다는 의미는 아니다
Edgecheckpoint 만료·손상·저장 실패·재진입과 outbox 중복 제거·pending 보존실제 서버 전송의 성공을 보장하지 않는다
E2EPixel 7 AVD에서 강제 종료 → 391m 상태 복원 → 재개 → 587m 완료 기록 1건실물 기기·iOS·production의 무손실을 보장하지 않는다
lint / buildlint가 실제 결함 2건을 발견했고 production build가 통과했다제품 의도와 사용자 흐름 전체가 옳다는 의미는 아니다

구현 agent와 분리한 AI reviewer는 자동 검사가 놓친 ?resume=1 진입 경로의 비대칭도 발견했다. 별도 review는 lint와 test를 보완했지만, 그 제안 역시 사람의 판단과 재검증을 거쳐야 했다.

같은 방식으로 측정한 bundle 변화는 다음과 같았다.

지표BeforeAfterDelta
js_raw1,381,202 B1,389,236 B+8,034 B (+0.58%)
js_gzip305,009 B307,959 B+2,950 B (+0.97%)
webview test83112+29

이 수치는 성능 개선이나 장기적인 품질 추세가 아니다. 기능을 추가한 뒤 동일한 방식으로 측정했을 때 bundle이 얼마나 변했고, 어떤 동작이 회귀 테스트에 포함됐는지를 기록한 값이다.

Evidence는 숫자를 많이 모으는 일이 아니라, 숫자가 말할 수 있는 범위를 제한하는 일이다.

5. 통과한 결과의 의미는 사람이 제한했다

자동 검증이 모두 통과해도 작업을 바로 완료하지 않았다. 중간보고에는 성공한 테스트뿐 아니라 실제 HTTP 전송, 실물 기기, iOS, production처럼 아직 확인하지 않은 범위도 함께 적었다.

사람의 검토에서는 유실 0·중복 0이라는 주장을 Pixel 7 AVD의 정의된 복원 시나리오로 제한했다. outbox와 checkpoint·연결 작업은 분리했고, 실제 서버 계약과 추가 환경 검증은 후속 작업으로 남겼다. 코드는 작동했지만, 그 결과로 무엇까지 말할 수 있는지는 별도의 판단이었다.

검증 결과는 원본을 다시 찾을 수 있도록 다음 정보와 연결해 남겼다.

baseline_commit: 9e1a9e5
after_commits:
  - 6166d9a
  - eddab0c
commands: [...]
environment: Pixel 7 AVD, Android 16, Vite dev, Metro
raw_reports: [...]
before: {...}
after: {...}
failures: [...]
known_limits: [...]

첫 기록부터 이 구조가 완전했던 것은 아니다. baseline과 구현 commit이 여러 문서와 Git 이력에 나뉘어 있어 추적이 불편했고, 이후에는 before와 after commit, 실행 환경, 실패와 한계를 한곳에 함께 적도록 보완했다.

이 사례의 핵심은 checkpoint나 outbox 자체가 아니다. 검증 기법을 새로 발명했다는 것도 아니다.

사람이 문제와 주장 기준을 소유한 상태에서 AI가 자율적으로 실행하고, 기존의 결정론적 검사들이 evidence를 공급하며, 결과의 의미와 외부 반영 책임을 다시 사람에게 돌려주는 전체 흐름이 핵심이다.

사람의 기준부터 baseline, AI Agent 구현, 자동 검증, 중간보고, 사람의 해석, 검증 기록까지 한 작업을 끝까지 추적한 타임라인 다이어그램
baseline commit과 검증된 commit, 테스트 수, gzip 번들 크기, 검증 환경, 숫자가 말하지 않는 범위를 함께 기록한 evidence 카드
 

5. 실패를 하네스의 다음 입력으로 만든다

“다음부터 조심해”를 다음 프롬프트에만 남기지 않는다

한 번의 작업이 끝나면 AI와 나 모두 무언가를 배운다. 문제는 그 배움이 다음 세션에 자동으로 이어지지 않는다는 것이다. 모델은 새 context에서 시작하고, 사람의 기억도 정확하지 않다. 그렇다고 발견한 모든 실수를 AGENTS.md에 추가하면 root context는 끝없이 커진다.

그래서 실패를 발견하면 바로 영구 규칙으로 만들지 않고 세 가지를 본다.

  1. 같은 문제가 다시 생길 가능성이 높은가?
  2. 다시 생겼을 때 피해가 큰가?
  3. 기계적으로 판별할 수 있는가?

이 질문에 따라 실패를 남길 위치를 달리한다.

실패의 성격다음에 남기는 위치이유
여러 작업에 적용되는 제품·아키텍처 결정정본 또는 가까운 영역별 지침사람이 이유와 우선순위를 읽어야 함
특정 review에서 다시 확인할 패턴다음 review의 project-scoped 작업 메모아직 팀 전체 규칙으로 일반화하기 이르기 때문
실행 전에 명확히 판별 가능한 위험 행동permission 또는 Hook피해가 생기기 전에 막을 가치가 큼
소스 패턴으로 반복 검출 가능한 결함lint사람의 반복 확인보다 일관됨
입력과 결과로 재현 가능한 동작regression·edge test다음 변경에서 자동으로 다시 검증 가능
제품 코드 밖의 실행 실패workflow와 실행 규율같은 잘못된 도구 사용을 반복하지 않게 함

이 분류는 “AI가 기억한다”는 기대와 다르다. 특정 review의 작업 메모는 팀의 공식 정본도, 사람의 기억도 아니다. 구현 agent와 분리한 AI reviewer가 다음 diff에서 다시 확인할 임시 패턴이다. 틀리거나 오래될 수 있으므로 감사하고 수정할 수 있어야 한다. 반복되며 명확한 입출력으로 고정할 수 있게 되면 메모에서 test로 옮기는 편이 낫다.

반대로 모든 발견을 test로 만들 수도 없다. “이 변경이 제품 의도와 맞는가?”처럼 맥락과 책임이 필요한 질문은 assertion 하나로 환원하기 어렵다. 그런 판단까지 억지로 자동화하면 검사가 통과했다는 사실이 의사결정이 옳다는 착각을 만들 수 있다.

실제 실패는 성격에 맞는 층으로 돌아갔다

Harness는 실패를 모두 같은 지침에 쌓는 방식으로 개선되지 않았다. 원인에 따라 코드 검사, review, 행동 경계등 처럼 서로 다른 위치에 남겼다.

  • 반복 가능한 React 결함 → lint

    render 중 ref.current를 변경한 코드와 effect 안의 동기 setState는 정적 분석으로 다시 판별할 수 있었다. 주의 문구를 늘리는 대신 코드 수정과 기존 lint 규칙으로 닫았다.

  • 진입 경로의 비대칭 → review 항목

    ?resume=1이 한 경로에 남은 문제는 다음 review의 확인 항목으로 남겼다. 같은 유형이 반복되면 사람이나 AI reviewer의 발견에 기대지 않고 URL 진입과 정리를 검증하는 test로 올릴 계획이다.

  • 잘못된 stacked PR base → 외부 반영 확인

    코드 검증이 통과해도 PR의 base와 선행 merge 상태는 잘못될 수 있었다. 이 문제는 제품 test가 아니라 PR·Release 직전 diff와 base를 다시 확인하는 절차로 돌려보냈다.

  • 보호 경로와 .env 접근 → 허용과 가드레일

    반복 위험을 실행 전에 줄이도록 permission deny와 PreToolUse Hook에 반영했다. 다만 문자열 기반 Hook은 실수 방지 장치일 뿐, 우회 불가능한 security boundary로 간주하지 않는다.

핵심은 모든 교훈을 root 지침에 추가하지 않는 것이다. 반복해서 재현할 수 있는 결함은 lint와 test로, 위험한 행동은 permission으로, 작업 방식의 실패는 실행 규율로 보낸다. 이후에도 빠져나가는 결함은 더 강한 검사로 올리고, 오탐이 많은 Hook이나 오래된 작업 메모는 완화하거나 삭제한다.

feedback은 네 번째 축이 아니라 세 축을 고치는 순환이다

실패를 다음 작업에 반영하는 과정은 Context, 책임, 검증 옆의 새로운 네 번째 원칙이 아니다. 세 축을 실제 실패에 맞게 갱신하는 feedback system이다.

실패·편차·사람의 반려

원본 결과와 중간보고

원인 분류
  ├─ 잘못된 판단 재료  → 정본·pointer·영역별 지침 수정
  ├─ 잘못된 자율 범위  → permission·stop condition 수정
  ├─ 놓친 코드 결함    → lint·regression·edge·golden 수정
  └─ 잘못된 작업 방식  → 실행 규율·PR 확인 절차 수정

다음 실행에서 재검증

AI가 잘못된 정본을 읽었다면 다음 프롬프트에 “앞으로 조심해”라고 쓰는 데서 끝내지 않고 context routing을 고친다. 승인 범위를 넘어섰다면 지시를 길게 만드는 대신 permission이나 stop condition을 본다. 반복 가능한 결함이 test 바깥으로 빠져나갔다면 fixture로 만든다. 도구를 잘못 사용했다면 제품 코드가 아니라 실행 workflow를 고친다.

이렇게 보면 Harness는 한 번 작성하고 끝나는 설정 파일이 아니다. 실제 작업에서 어떤 실패가 빠져나갔는지 관찰하고, 그 실패를 가장 싼 비용으로 다시 잡을 위치를 선택하는 feedback system이다.

실패를 원인 분류를 거쳐 Context 구조, permission·stop condition, lint·test·review 항목 수정으로 되돌리는 feedback loop 다이어그램

하네스 자체도 실패하고 낡는다

이 글이 성공 사례만 모은 소개문이 되지 않으려면 내가 사용하는 방식도 같은 기준으로 검증해야 한다. 현재 evidence로 분명하게 말할 수 있는 한계들이 존재한다.

1. root 지침이 이미 너무 커졌다

앞서 본 root AGENTS.md는 이미 작은 map을 지향한다는 원칙과 충돌할 만큼 커졌다. 중요한 결정을 놓칠까 봐 root에 계속 추가한 결과다. 우선순위가 높은 불변식과 정본 색인만 남기고, 세부 규칙을 가까운 영역으로 옮긴 뒤 누락과 탐색 비용을 다시 측정해야 한다.

2. 일부 자동화는 여전히 Claude에 의존한다

프로젝트의 핵심 사실은 공통 문서가 소유하지만 일부 Skill, Hook, AI review 작업 메모는 Claude의 실행 구조에 남아 있다. Codex에서도 AGENTS.md, Skill, sandbox와 approval로 비슷한 의도를 구현할 수 있지만 두 연결이 자동으로 같아지지는 않는다.

따라서 현재 근거가 지지하는 주장은 “완전히 vendor-neutral하다”가 아니다. 사실의 소유권을 도구 밖에 두고, 제품별 연결을 얇게 만들려는 방향으로 운영한다는 것까지다.

3. local과 emulator evidence는 production 보장이 아니다

한 번의 Android emulator 성공과 한 번의 bundle build로 모든 환경의 안정성이나 성능 추세를 주장할 수 없다. 반복 실행, 실물 기기, iOS, production telemetry가 없다면 그 경계를 그대로 표시해야 한다. fake dependency로 통과한 test도 실제 외부 서비스 성공과 구분해야 한다.

4. 사람의 판단에는 처리량 비용이 있다

앞서 살펴봤듯 작업 전·후 사람 확인을 모든 작은 변경에 적용하면 대기 시간이 늘고 승인이 형식화될 수 있다. 위험 분류를 잘못할 가능성도 있으며, 현재는 이 비용과 효과를 장기간 측정한 수치가 없다. 사람 판단을 넣었다는 사실 자체를 품질 증거로 사용할 수는 없다.

이 한계들은 Harness가 쓸모없다는 증거가 아니다. AI 주변의 구조를 소프트웨어 시스템으로 본다면 당연히 관찰하고 고쳐야 할 대상이라는 뜻이다. 내가 사용하는 workflow도 evidence 없이 “잘 작동한다”고 선언할 수 없다.

현재 근거가 있는 범위와 추가 검증이 필요한 범위를 나눠 evidence의 경계를 표시한 비교 다이어그램
 

6. 사람은 실행이 아니라 기준을 소유한다

글의 처음으로 돌아가 보자. test와 build, emulator 흐름까지 통과했는데 왜 작업을 멈췄을까?

AI를 믿지 못해 모든 일을 사람이 다시 하려던 것이 아니다. 오히려 탐색, 구현, test 작성, 실패 수정처럼 AI가 잘할 수 있는 실행을 더 넓게 맡기기 위해서였다. 다만 AI는 개발 시스템 전체가 아니었다. 한 확률적 구성요소의 실행이 끝났다는 사실과, 그 변경을 받아들일 준비가 됐다는 판단을 분리해야 했다.

그 분리를 가능하게 한 것은 세 질문이었다.

AI에게 무엇을 보여줄 것인가?

사실의 소유권을 정하고, root 문서는 모든 내용을 복제하는 대신 필요한 정본을 가리키게 했다. 현재 판단에 필요한 원문은 구현 전에 찾아 읽고, 대량 탐색은 별도 context로 분리했다. 긴 작업의 범위와 상태, 실패와 원본 결과는 대화에만 두지 않고 plan과 report, evidence에도 남겼다.

Context를 설계한다는 말은 파일을 많이 만드는 것이 아니라, 무엇이 사실을 소유하며 언제 어떤 정보가 판단에 들어오는지를 정하는 일이다.

AI에게 어디까지 맡길 것인가?

AI는 승인된 범위 안에서 탐색과 구현, test 작성과 수정 순서를 자율적으로 선택한다. 사람은 모든 명령을 감시하거나 구현 줄을 미리 지시하는 대신 문제, 비범위, 성공 기준을 확인한다. 작업 뒤에는 결과가 무엇을 의미하는지, 외부에 어디까지 반영할지를 판단한다.

기계적으로 막을 수 있는 위험 행동은 permission과 guardrail로 줄인다. 하지만 행동 허용과 제품 판단을 혼동하지 않는다. 코드가 올바른 것과 PR base나 release 상태가 올바른 것도 구분한다.

AI가 실행을 더 많이 담당할수록 사람은 실행이 아니라 기준을 소유해야 한다.

AI가 만든 결과 중 무엇을 믿을 것인가?.

검증이 많다고 자동으로 신뢰할 수 있는 것은 아니다. 각 검사가 무엇을 잡고 무엇은 잡지 못하는지 알아야 한다. 마지막에는 사람이 evidence가 지지하는 범위를 해석한다.

나는 좋은 Harness는 AI를 억제하는 장치가 아니라고 생각한다. 무엇을 읽고 어디까지 행동하며 어떤 조건에서 멈춰야 하는지가 분명하기 때문에, 그 안에서 더 많은 자율성을 줄 수 있게 하는 장치다. 모델이 더 강해져도 이 역할은 사라지지 않는다. 실행 속도와 범위가 커질수록 잘못된 기준으로 더 빠르고 멀리 갈 수 있기 때문이다.

내가 하네스 엔지니어링에서 만드는 것은 항상 정답을 내는 AI가 아니라, 틀릴 수 있는 AI와 함께 일하면서도 무엇을 믿어도 되는지 추적할 수 있는 개발 시스템이다.


참고자료

  • Anthropic — Effective context engineering for AI agents
  • Claude Code Docs — How Claude remembers your project
  • Claude Code Docs — Automate actions with hooks
  • Claude Code Docs — Configure permissions
  • OpenAI Docs — Custom instructions with AGENTS.md
  • OpenAI Docs — Build skills
  • OpenAI Docs — Agent approvals & security

Comments