GITHUB 레포 딥다이브 · 2026-08-04 · BOLDSOFTWARE/MEAT · "AI가 쓴 diff를 스타일·import 다 빼고 '개념'만 읽게 깎아 주는 도구"

meat 딥다이브
"코드 리뷰에서 읽을 가치 없는 부분을 LLM에게 도려내게 하되, 출력은 절대 모델이 쓰지 않는다"

boldsoftware/meat는 git diff(코드 변경분)를 LLM에게 넘겨 "읽기용 diff(reading diff)"로 압축해 주는 순수 Go CLI 도구다. 핵심 주장은 도발적이다 — "이제 모델은 충분히 좋다. 에이전트가 쓴 코드를 리뷰할 때 스타일·nil 체크·import를 볼 필요는 없다. 개념·알고리즘 선택·아키텍처를 봐야 한다." meat는 그 "볼 필요 없는 부분"을 모델에게 골라내게 해, 리뷰어에게 고기(meat), 즉 알맹이만 보여 준다.

이 딥다이브가 meat를 고른 이유는 단순히 TrendShift Daily 1위라서가 아니다. 이 500줄 남짓한 도구 안에, "LLM에게 안전하게 편집을 시키는 법"의 교과서적 설계가 압축돼 있기 때문이다. 결정적 아이디어 하나 — 모델은 완성된 diff를 절대 쓰지 않는다. 대신 "몇 번째 줄을 지워라 / 접어라 / 이 부분만 …로 가려라"라는 편집 계획(edit plan)만 제출하고, meat가 그 계획을 변경 불가능한 원본 diff에 기계적으로 적용한다. 모델이 손대는 건 "무엇을 숨길지"뿐, 화면에 뜨는 코드는 언제나 진짜 코드다. 이 한 겹의 간접화(indirection)가 LLM 환각(hallucination)을 구조적으로 봉쇄한다.

만든 곳도 신뢰의 근거다. Bold Software, Inc. — 에이전트 코딩 도구 Sketch(sketch.dev)를 만든 회사이자, 코드 곳곳에 자사 에이전트 Shelley와 관리형 LLM 게이트웨이 exe.dev를 위한 배려가 녹아 있다. 즉 meat는 "장난감 데모"가 아니라 실제 프로덕션 에이전트 파이프라인에서 쓰려고 뽑아낸 부품이다.

(저장소 boldsoftware/meat · 라이선스 Apache-2.0 (© 2026 Bold Software) · 언어 순수 Go (go 1.24, 서드파티 의존성 0개) · 규모 약 1.4만 줄(테스트가 소스보다 많음) · 설치 go install meat.dev/cmd/meat@latest · 기본 모델 OpenAI gpt-5.6-sol / Anthropic claude-opus-4-8 · TrendShift Daily #1)
목차
  1. 한 줄 정체
  2. 왜 주목받는가 — "모델은 이제 충분히 좋다"라는 전제 전환
  3. 기술 스택 전체 지도
  4. 아키텍처 심화 — 편집 계획·에이전트 루프·캐시
  5. 디렉토리 구조 해부
  6. 학습 포인트 — 기술별 배울 것
  7. 시스템 요구사항 / 비용 감각
  8. 직접 해볼 수 있는 실습 과제
  9. 관련 기술 심화 로드맵 (주차별)
  10. 핵심 키워드 사전
  11. 참고 링크

1한 줄 정체

이 프로젝트가 정확히 무엇인지 한 문장으로

meat는 "unified diff를 입력받아, LLM 에이전트에게 읽을 가치 없는 부분을 골라내게 하고, 그 편집 계획을 원본 diff에 기계적으로 적용해 '읽기용 diff'를 출력하는 명령줄(CLI) 도구"다. 사용법은 git show만큼 단순하다 — 저장소 안에서 그냥 meat라고 치면 최신 커밋을, meat HEAD~3이나 meat main...HEAD처럼 git 인자를 주면 그 범위를, git diff | meat처럼 파이프로 넘기면 그 diff를 압축한다.

한 장의 비유

"형광펜으로 밑줄 긋기" 대신 "읽을 필요 없는 문단을 접어서 ...로 바꿔 주는 편집자"

두꺼운 계약서를 검토한다고 하자. 보통의 요약 도구는 "제가 요약해 드릴게요"라며 자기 말로 새로 써 준다 — 그러면 원문에 없던 말이 섞일 위험(환각)이 생긴다. meat의 편집자는 다르다. 원본 계약서에는 한 글자도 새로 쓰지 않는다. 대신 "1~4쪽은 표준 약관이라 통째로 접기", "7쪽 둘째 줄은 회사명만 남기고 나머지는 로 가리기" 같은 지시서(편집 계획)만 만든다.

그 지시서를 실제 원본에 적용하는 건 편집자가 아니라 기계다. 그래서 결과물에 보이는 모든 글자는 반드시 원본에 실제로 있던 글자다. 편집자(LLM)가 아무리 헛소리를 하고 싶어도, 할 수 있는 일은 "무엇을 숨길지 고르는 것"뿐이라 거짓말이 원천 봉쇄된다. 남는 건 계약의 핵심 조항, 즉 고기(meat)뿐이다.

용어
diff · unified diff · reading diff · abridge · 헝크(hunk)
diff는 코드의 "바뀐 부분"만 +(추가)·-(삭제)로 보여 주는 형식. unified diff는 그중 가장 흔한 표준 포맷(git diff가 뱉는 그것). reading diff(읽기용 diff)는 meat가 만든 개념 — "적용 가능한 패치가 아니라, 사람이 읽고 이해하기 위한 압축된 diff". abridge는 "요약·축약하다"라는 뜻으로, meat의 핵심 동사다. 헝크(hunk)는 diff 안에서 @@ ... @@로 구분되는 "변경 덩어리" 하나를 가리킨다.

기술적으로 정확히 말하면, meat는 순수 Go로 짠, 서드파티 의존성이 0개인 단일 바이너리다(go.modrequire 블록이 아예 없다). 표준 라이브러리만으로 OpenAI Responses API와 Anthropic Messages API를 직접 호출하고, git CLI로 diff를 읽어 오며, 결과를 ~/.meat내용 주소 기반(content-addressed)으로 캐시한다. "기능 = 하나의 잘 정의된 편집 프로토콜 + 그 프로토콜을 강제하는 컴파일러"라는 등식이 이 저장소 전체를 관통한다.

2왜 주목받는가 — "모델은 이제 충분히 좋다"라는 전제 전환

"AI 코드 리뷰 도구"는 넘친다. meat가 1위에 오른 진짜 차별점

첫째, 문제 정의가 반(反)직관적이고 시의적절하다. 대부분의 "AI 코드 리뷰" 도구는 "AI가 당신 대신 버그를 찾아 드립니다"라고 말한다. meat는 정반대 전제에서 출발한다 — "코드는 컴파일되고 테스트도 통과한다. 리뷰어는 nil 패닉을 사냥하는 사람이 아니다." 에이전트가 코드를 쓰는 시대의 병목은 "버그 찾기"가 아니라 "거대한 diff에서 개념적으로 중요한 변화를 사람이 빠르게 파악하기"라는 것이다. meat는 리뷰를 대신하지 않는다. 리뷰할 것을 줄여 준다.

차별 포인트
"모델이 출력을 쓰지 않는다"는 제약 생성(constrained generation)

보통의 LLM 요약기는 모델이 자유 텍스트로 결과를 생성한다 — 그래서 원본에 없던 코드·주석·식별자가 슬쩍 섞일 수 있다(환각). meat는 이 문을 아예 닫는다. 모델이 제출할 수 있는 건 오직 원본 줄 번호를 가리키는 세 가지 연산뿐이다: remove(줄 범위 삭제)·replace(한 줄의 일부만 로 가리기)·fold(연속된 여러 줄을 ... 한 줄로 접기). 결과 diff는 meat가 변경 불가능한 원본에 이 연산을 적용해 기계적으로 렌더한다. "출력을 자유 생성이 아니라 원본에 대한 연산으로 제약한다"는, 안전한 LLM 편집의 정석 패턴을 극단까지 밀어붙인 표본이다.

둘째, 제작진의 무게가 다르다. meat는 Bold Software가 만들었다 — 에이전트 코딩 도구 Sketch를 만드는 회사다. 코드 주석에는 자사 에이전트 Shelley가 meat를 "임베딩(embed)"해 자기 LLM 클라이언트로 갈아끼울 수 있다고 명시돼 있고, exe.dev VM에 붙은 관리형 LLM 게이트웨이를 자동 감지해 API 키 없이도 돌아가는 경로까지 마련돼 있다. 즉 이건 데모가 아니라 실제 프로덕션 에이전트 파이프라인의 한 부품으로 설계됐다.

셋째, "실전 벤치마크를 저장소에 같이 넣었다". analysis/ 폴더에는 Django·Flask·pytest의 실제 유명 커밋에 meat를 돌린 결과가 JSON·diff·리포트로 통째로 들어 있다. 심지어 리포트는 자기 도구의 약점("Django에서 좋은 테스트 자극을 숨기고 기계적 호출부 변경을 남겼다")까지 솔직히 적는다. 마케팅이 아니라 엔지니어링 문서다.

대상 커밋원본 변경 줄meat가 보여 준 줄바이트 절감
Django178줄112줄 (62.9%)34.6% ↓
Flask124줄96줄 (77.4%)23.4% ↓
pytest109줄83줄 (76.1%)29.3% ↓

▲ 저장소 analysis/에 실린 자체 벤치마크(2026-08-02, 기본 모델 claude-opus-4-8, -no-cache). "얼마나 지웠나"가 아니라 "얼마나 안 읽어도 되게 만들었나"가 지표다.

넷째, 결과에 대한 정직함이 코드에 박혀 있다. meat는 "몇 줄을 남겼는지"를 모델의 자기 보고를 믿지 않고, 읽기용 diff를 원본에 다시 정렬해 직접 세어 화면 맨 아래에 kept 12/240 changed lines in 3/7 files처럼 찍는다(elision.go). "당신은 지금 240줄 중 12줄만 보고 있다"를 리뷰어가 항상 알 수 있게 — 압축의 대가로 무엇을 안 보고 있는지를 숨기지 않는다.

3기술 스택 전체 지도

의존성 0개. 이 도구가 "무엇을 안 쓰는가"가 곧 설계 철학이다

meat의 첫 번째 놀라움은 "의존성이 없다"는 점이다. go.mod은 딱 두 줄(module meat.dev, go 1.24.13)이고 require 블록이 없다. HTTP 클라이언트·JSON 파서·API SDK를 전부 Go 표준 라이브러리로 직접 짰다. LLM 회사가 만든 도구인데 공식 SDK조차 안 쓴다 — "단일 바이너리로 go install 한 방에 끝나고, 공급망 위험(supply chain)이 0"이라는 가치를 의존성 편의보다 위에 둔 선택이다.

영역기술 / 근거역할
언어·런타임Go 1.24 (표준 라이브러리 only)단일 바이너리, 크로스 플랫폼, 의존성 0
LLM — OpenAIResponses API (스트리밍, 직접 HTTP)기본 공급자. gpt-5.6-sol, reasoning effort medium
LLM — AnthropicMessages API (직접 HTTP)claude-로 시작하는 모델 ID면 자동 선택. 기본 claude-opus-4-8
관리형 게이트웨이exe.dev reflection endpointexe.dev VM이면 llm 통합을 자동 감지 → API 키 불필요
입력 수집git CLI (show/diff)커밋·범위·스테이징·워킹트리 diff를 읽어 옴
주변 소스 탐색git grep, 파일 읽기(루트 제한)에이전트가 "이 줄이 중요한가"를 판단할 단서 수집
출력 렌더git pager + color.diff 설정 재사용터미널이면 git show처럼 색·페이저, 파이프면 평문
캐시~/.meat, JSON 파일, SHA-256 키같은 (diff+모델+rubric)이면 즉시 반환(네트워크·키 불필요)
출력 스키마 검증표준 encoding/json (strict decode)모델의 편집 계획을 엄격 파싱(미지 필드 거부)

왜 "두 공급자를 하나의 인터페이스로" 감쌌나

meat의 코어(meat 패키지)는 특정 LLM 회사를 모른다. 오직 Model이라는 메서드 하나짜리 인터페이스만 안다: Generate(ctx, system, messages, tools). OpenAI Responses(스트리밍·암호화된 reasoning 항목)와 Anthropic Messages(블록 기반)라는 서로 완전히 다른 API 두 개를, Block이라는 공통 자료형 하나로 정규화해 인터페이스 뒤로 숨겼다. 그래서 임베더(Sketch의 Shelley 등)는 자기 LLM 클라이언트를 Model로 감싸기만 하면 meat의 엔진을 그대로 재사용한다.

용어
Responses API · Messages API · reasoning 토큰 · 인터페이스
Responses API는 OpenAI의 에이전트용 API로, 모델의 "생각(reasoning)" 항목을 암호화된 상태로 다음 턴에 되돌려줘야 한다 — meat는 이걸 ProviderData에 그대로 담아 두었다가 재생한다. Messages API는 Anthropic의 대화 API. reasoning 토큰은 모델이 답 전에 "속으로 생각하는" 데 쓰는 토큰(비용에 포함되지만 화면엔 안 보임). 인터페이스(interface)는 Go에서 "이런 메서드를 가진 건 뭐든 받아들인다"는 계약 — meat는 Generate 하나만 요구한다.

4아키텍처 심화 — 편집 계획·에이전트 루프·캐시

diff 하나가 "읽기용 diff"가 되기까지, 데이터는 어떤 길을 지나는가

meat의 아키텍처는 세 개의 축 위에 서 있다: ① 캐시 우선(cache-first) 오케스트레이션, ② 편집 계획 간접화(edit-plan indirection)를 강제하는 에이전트 루프, ③ 계획을 원본에 적용하는 컴파일러. 전체 그림부터 보자.

git show / git diff / stdin │ ▼ readDiff() ──▶ 캐시 키 = SHA-256( rubricHash + model + diff ) │ │ │ ┌──────────────┴──────────────┐ │ 캐시 HIT 캐시 MISS │ │ │ │ 즉시 렌더 Abridge(ctx, model, req) │ (네트워크·키 불필요) │ │ numberedDiff — 각 줄에 "N|" 부여 │ │ │ ┌───────────────────────────────────┴────────────────┐ │ │ 에이전트 루프 (최대 24턴 / 4분 예산) │ │ │ system = rubric(규칙) + 번호매긴 원본 diff │ │ │ │ │ │ │ model.Generate ──▶ tool_use 있나? │ │ │ • read_file · grep (주변 소스로 근거 수집) │ │ │ • preview_plan (계획 검증 + 미리보기) │ │ │ • submit (최종 편집 계획 제출) │ │ └───────────────────────────┬────────────────────────┘ │ │ remove / replace / fold │ ▼ │ compileEditPlan — 계획을 "변경 불가능한 원본"에 적용 │ • import 자동 제거 • move(이동) 대칭 강제 │ • Python 스위트 검증 • elision(가림) 투영 검사 │ │ smartDiff (읽기용 diff) │ ▼ └──────────────────────────────▶ 캐시 저장 ──▶ render + "kept N/M lines"

① 편집 계획 간접화 — 이 저장소의 심장

모델은 완성된 diff를 절대 만들지 않는다. 대신 submit 툴로 세 배열을 제출한다. 좌표는 전부 원본 diff의 1-based 물리 줄 번호이고, 이 번호는 절대 밀리지 않는다(줄을 지워도 나머지 좌표가 바뀌지 않음 — 항상 원본 기준).

연산하는 일제약(컴파일러가 강제)
remove줄 범위를 통째로 뺀다순수 노이즈에 사용
replace한 줄의 일부로 가림newold의 "투영"이어야 함 — 글자는 지우고 .../만 넣을 수 있음. 식별자·주석·구두점 몰래 추가 금지
fold연속된 2줄 이상을 ... 한 줄로 접음같은 헝크·같은 부호(+/-/공백). 들여쓰기는 meat가 계산. 모델은 좌표만, 텍스트는 절대 못 씀
왜 이게 안전한가

모델의 "쓰기 권한"을 원천 차단하고 "지우기·가리기 권한"만 준 것. 결과 diff에 등장하는 모든 문자는 반드시 원본 diff에 실제로 있던 문자다(fold가 만드는 고정된 ... 한 종류만 예외인데, 이건 기계가 만들고 들여쓰기까지 원본에서 계산한다). 모델이 replace로 "이 부분만 로 가려라"라고 할 때도, meat는 newold에서 글자를 빼고 를 넣은 것 이상이 아닌지 정규식으로 검증한다. 통과 못 하면 계획을 거부한다. LLM이 거짓말을 하고 싶어도 프로토콜이 허락하지 않는다.

실제 편집 예시 (rubric의 worked example 중 하나)

# 원본 diff (번호매김) — 기계적인 필드 복사 6줄
101|+    // Extra data used for cache management but not routing.
102|+    resp.SSHKeyID = rd.sshKeyID
103|+    resp.UserID   = rd.userID
104|+    resp.BoxID    = int64(rd.boxID)
105|+    resp.BoxName  = rd.boxName
106|+    resp.ExpiresAt = timestamppb.New(rd.expiresAt)

# 모델이 제출하는 계획
remove : 103~106 삭제
replace: 102번 줄에서 ".sshKeyID"  →  "..."

# meat가 렌더한 읽기용 결과 (원본 글자만 사용)
+    resp.SSHKeyID = rd...

리뷰어는 "여기서 응답 구조체에 라우팅과 무관한 캐시용 데이터를 복사한다"는 의미만 한 줄로 읽는다. 나머지 다섯 줄의 기계적 반복은 사라졌지만, 지어낸 요약 문장이 아니라 실제 코드 조각으로 압축됐다.

② 에이전트 루프 — 근거를 쥔 채로 판단하기

abridgeOne은 전형적인 툴 사용 에이전트 루프다. 시스템 프롬프트(=rubric)와 번호매긴 diff를 넣고, 모델이 submit할 때까지 최대 24턴·4분 예산 안에서 돈다. 결정적인 건 모델에게 읽기 전용 도구를 쥐여 줬다는 점이다 — read_file(주변 파일 읽기)과 grep(git grep). "이 줄이 정말 중요한가? 이 파일은 자동 생성된 건가?"를 diff 텍스트만으로 판단이 안 설 때 실제 소스를 들춰 보라는 것. 단, 이 도구들은 resolveInRoot저장소 루트 밖으로 못 나가게 막혀 있다(../·절대경로 traversal 방어).

설계 디테일
"과잉 조사 금지"와 자동 정제(refine) 한 번

rubric은 "대부분의 줄은 diff만 보고 판단하라, 단서가 판단을 바꿀 때만 도구를 써라"라고 못 박는다. 또 모델이 처음 제출한 계획이 여전히 너무 장황하면(retention pressure가 높으면), meat는 그 계획을 폴백(fallback)으로 보관한 뒤 "한 번 더 다듬어 봐"라고 딱 한 번 넛지한다. 두 번째가 실패하거나 시간이 다 되면 보관해 둔 첫 계획을 반환한다 — 무한 루프도, 빈손 반환도 없다.

③ 자동 처리 3종 — import 제거 · move 대칭 · chunking

모델이 신경 쓰지 않아도 되도록, meat가 기계적으로 알아서 처리하는 것들이 있다.

④ 캐시와 "동결된 프롬프트 표면(frozen prompt surface)"

meat는 결과를 ~/.meat에 캐시한다. 키는 SHA-256(rubricHash + model + diff). 여기서 rubricHash가 정교하다 — 단순히 시스템 프롬프트만 해싱하는 게 아니라, 모델이 볼 수 있는 모든 문자열(시스템 프롬프트, 유저 프롬프트의 모든 분기, 툴 설명·스키마, 넛지 메시지, 계획 피드백)을 실제 빌더 함수로 렌더한 결과 전체를 해싱한다(promptSurface()).

용어
frozen prompt surface · rubric hash · content-addressed 캐시
동결된 프롬프트 표면은 meat의 프롬프트 설계 철학: 프롬프트는 모델이 행동해야 할 것만 말하고, 컴파일러 내부 규칙(예: import 제거와 move 강제의 우선순위)은 프롬프트에 절대 쓰지 않는다. 대신 모델의 계획과 컴파일러가 충돌하면 그 충돌에 대한 피드백만이 유일한 설명 채널이다. rubric hash는 이 표면 전체의 지문. 프롬프트를 한 글자만 바꿔도, 프로토콜을 바꿔도 해시가 달라져 캐시가 자동 무효화된다. content-addressed 캐시는 "내용의 해시를 키로 삼는" 방식 — 같은 입력이면 항상 같은 키.

5디렉토리 구조 해부

파일 이름만 읽어도 아키텍처가 보인다 — CLI 껍데기와 엔진 코어

구조는 Go 관례대로 딱 둘로 갈린다: cmd/meat/(사용자와 맞닿는 CLI 껍데기)와 meat/(재사용 가능한 엔진 라이브러리). "명령어는 얇게, 로직은 라이브러리에"라는 Go의 정석이다.

meat/ ├── go.mod ← module meat.dev, go 1.24, 의존성 0 ├── README.md · LICENSE ← Apache-2.0 │ ├── cmd/meat/ ★ CLI 껍데기 (git·캐시·렌더·터미널) │ ├── main.go 진입점: 플래그·git 호출·run() 오케스트레이션 │ ├── cache.go ~/.meat 읽기/쓰기, cacheKey 계산 │ ├── render.go git pager + color.diff 로 결과 출력 │ ├── stdin.go 파이프 입력 감지·수집 │ └── isatty_*.go OS별 "터미널인가?" 판별(linux/bsd/unix/other) │ └── meat/ ★ 엔진 (provider-agnostic 라이브러리) ├── meat.go Abridge() — 공개 진입점, 에이전트 루프 ├── rubric.go systemPrompt(규칙) + promptSurface 해싱 ├── tools.go read_file·grep·preview_plan·submit 정의 ├── editplan.go 계획 컴파일러 — 계획을 원본에 적용(최대 로직) ├── chunk.go 거대 diff 분할·병합 ├── elision.go "kept N/M lines" 로컬 집계 ├── moves.go 정확한 코드 이동 탐지 ├── imports.go 언어별 import 자동 제거 마스크 ├── python.go Python 전용 검증(스위트·삼중따옴표 균형) ├── diff.go unified diff 파서(불변 레이아웃) ├── model.go Model 인터페이스 + Block 공통 자료형 ├── provider.go 모델 ID → 공급자 선택, 폴백 체인 ├── openai.go OpenAI Responses API(스트리밍) ├── anthropic.go Anthropic Messages API ├── gateway.go exe.dev 관리형 게이트웨이 자동 감지 └── *_test.go golden test 등 — 소스보다 많은 테스트 │ └── analysis/ 실전 벤치마크(Django·Flask·pytest 커밋 결과)

주목할 점: 테스트 파일이 소스 파일보다 크다. 예컨대 chunk.go(1,086줄)에 chunk_test.go(1,559줄)가 붙어 있고, editplan.go(770줄)에 editplan_test.go(923줄)가 붙는다. python_golden_test.go·rubric_e2e_test.go 같은 골든 테스트(golden test)가 "이 diff를 넣으면 이 읽기용 diff가 나와야 한다"를 통째로 고정해 둔다. 컴파일러형 도구답게, 동작을 예제로 못 박는 문화가 코드에 배어 있다.

6학습 포인트 — 기술별 배울 것

이 작은 저장소 하나로 배울 수 있는 것들
배울 것 ①

제약 생성(Constrained Generation) — LLM에게 안전하게 편집시키기

meat의 가장 이식성 높은 교훈. "모델이 결과물을 자유 생성하게 두지 말고, 원본에 대한 연산으로 출력을 제약하라." 이 패턴은 코드 리뷰뿐 아니라 문서 편집·데이터 마스킹·법률 문서 redaction·리팩터링 도구 어디에나 적용된다. 핵심 실천: ① 모델의 출력 공간을 좁은 스키마(JSON)로 강제, ② 그 계획을 불변의 원본에 기계적으로 적용, ③ 적용 전에 계획을 컴파일러가 검증(투영·균형·대칭)해 거부 가능하게.

배울 것 ②

프롬프트 표면 해싱으로 캐시 무효화하기

LLM 앱에서 캐시는 필수지만, "프롬프트를 고쳤는데 옛 캐시가 나오는" 버그가 흔하다. meat의 해법: 모델이 볼 수 있는 모든 문자열을 실제 빌더로 렌더해 통째로 해싱하고, 그 해시를 캐시 키에 섞는다. 프롬프트·프로토콜·모델 중 하나라도 바뀌면 자동으로 캐시 미스. "프롬프트 = 코드"라면 "프롬프트 해시 = 버전"이라는 발상. rubric.gopromptSurface()abridgeProtocolVersion 상수를 정독할 가치가 있다.

배울 것 ③

Provider 추상화 — 이질적 API 두 개를 인터페이스 하나로

OpenAI Responses(스트리밍·암호화 reasoning)와 Anthropic Messages(블록형)를 Model.Generate 하나로 정규화한 방식은 어댑터 패턴의 좋은 표본이다. 특히 Block을 인터페이스가 아니라 평평한 구조체로 두고 Type 필드로 분기시킨 선택(타입 스위치 최소화), 그리고 공급자별 불투명 상태를 ProviderData에 격리한 설계를 배울 것.

배울 것 ④

테스트 가능성을 위한 Go 관용구

main.gorun()은 flag·git·LLM 없이도 단위 테스트되도록, compute·render를 클로저로 주입받는다(의존성 역전). abridgeBudget·singleRunDiffBytes처럼 상수 대신 테스트에서 덮어쓸 수 있는 변수로 둔 패턴, exeDevMarkerPath를 var로 두어 게이트웨이 감지를 스텁하는 기법도 실전 Go 테스트 설계의 교본이다.

배울 것 ⑤

diff/텍스트 처리의 기본기

diff.goanalyzeDiff는 모든 물리 줄을 헤더/헝크경계/헝크소스로 분류하고 파일·헝크·언어를 태깅한 불변 레이아웃을 만든다 — 하류 로직은 절대 diff를 재파싱하지 않는다. "한 번 파싱해 불변 자료구조로 굳히고 모두가 그것만 본다"는 원칙, 그리고 -- counter--- 메타데이터로 오인하지 않는 헝크 인식 분류 같은 디테일을 배울 것.

7시스템 요구사항 / 비용 감각

돌리는 데 필요한 것과, 한 번에 얼마나 드는지
항목요구/기본값비고
Go1.24 이상go install meat.dev/cmd/meat@latest 한 줄
git필수diff 수집·git grep·페이저/색 재사용에 사용
API 키OPENAI_API_KEY 또는 ANTHROPIC_API_KEYexe.dev VM이면 게이트웨이 자동 감지로 키 불필요
네트워크모델 호출 시에만캐시 HIT이면 오프라인·키 없이 즉시 반환
메모리·디스크매우 가벼움단일 바이너리 + ~/.meat JSON 캐시
입력 상한단일 실행 ~400KB / 전체 ~4MB초과 시 분할(최대 32조각) 또는 "범위를 좁혀라" 안내
현실 감각
"느리고, 토큰을 쓴다" — 그래서 캐시와 사전처리가 설계에 박혀 있다

meat는 커밋 하나를 처리하는 데 시간이 걸린다(reasoning 모델 + 최대 24턴의 툴 사용). README도 대놓고 권한다 — "에이전트를 시켜 meat를 devtools에 넣어 커밋을 미리 처리해 두라". 그래서 캐시가 1급 시민이다: 같은 diff·모델·rubric이면 두 번째부터는 즉시(meat: cached). 토큰 사용량은 실행 후 tokens in=… out=…로 항상 표시돼, 비용을 눈으로 확인하며 쓸 수 있다.

8직접 해볼 수 있는 실습 과제

읽기만 하지 말고 손으로 — 난이도별
난이도 ★☆☆ · 입문

meat를 내 커밋에 돌려 보고 "무엇이 사라졌나" 관찰하기

go install meat.dev/cmd/meat@latest 후, 최근 리팩터링 커밋에서 git show HEAD | meat와 원본 git show HEAD를 나란히 비교하라. 목표: meat가 지운 줄들이 정말 "안 읽어도 되는 것"이었는지 스스로 판정 → 맨 아래 kept N/M 수치와 체감이 맞는지 확인. import·에러 메시지·필드 복사가 어떻게 처리됐는지 눈으로 익히기.

난이도 ★★☆ · 초중급

-json 출력으로 편집 계획의 "정직함" 검증하기

git show <sha> | meat -json으로 smart_diff를 뽑은 뒤, 그 안의 모든 코드 줄이 원본 diff에 실제로 존재하는지 스크립트로 대조하라(fold가 만든 ... 줄만 예외). 목표: "모델이 출력을 쓰지 않는다"는 주장이 참인지 직접 확인 → 제약 생성의 안전성을 체감. 여러 커밋에 반복해 반례가 나오는지 찾아보기.

난이도 ★★☆ · 초중급

rubric을 한 글자 바꿔 캐시 무효화 관찰하기

저장소를 클론해 rubric.gosystemPrompt 문구를 아주 살짝 고친 뒤 RubricHash()가 달라지는지, 같은 diff의 캐시가 미스로 바뀌는지 확인하라. 목표: "프롬프트 표면 전체를 해싱해 캐시를 무효화한다"는 메커니즘을 손으로 재현 → promptSurface()가 왜 툴 스키마·피드백까지 다 렌더해 넣는지 이해하기.

난이도 ★★★ · 중급

나만의 Model 구현으로 다른 공급자 붙이기

Model 인터페이스(Generate 하나)를 구현해 로컬 LLM(Ollama)이나 다른 API를 meat 엔진에 꽂아라. OpenAI/Anthropic의 응답을 []Block으로 정규화하는 부분이 핵심. 목표: provider 추상화의 경계가 어디인지, tool_use/tool_result 블록을 어떻게 왕복시키는지 체득 → 어댑터 패턴 실전 연습.

난이도 ★★★ · 중급+

"editplan 컴파일러"의 거부 규칙 깨 보기

preview_plan일부러 잘못된 계획을 넣어 보라 — replacenew에 원본에 없는 식별자 추가, move의 한쪽만 접기, Python 삼중따옴표 균형 깨기. 목표: 컴파일러가 각 위반을 어떤 메시지로 거부하는지 수집 → "프롬프트에 규칙을 쓰지 않고 피드백으로만 가르친다"는 frozen-surface 철학이 실제로 어떻게 작동하는지 관찰하기.

9관련 기술 심화 로드맵 (주차별)

meat를 발판으로 "안전한 LLM 편집 시스템"까지
주차주제무엇을 / 왜
1주차unified diff & git plumbinggit show/git diff/범위 문법(A..B, A...B), 헝크·@@ 헤더 읽기. diff.goanalyzeDiff를 따라 파서 미니 구현.
2주차LLM 툴 사용 에이전트 루프system/user/tool_use/tool_result의 왕복, 턴 상한·시간 예산·폴백. meat.goabridgeOne을 정독하고 최소 루프를 직접 작성.
3주차제약 생성 & 스키마 강제JSON Schema로 출력 공간 좁히기, strict decode(미지 필드 거부), 계획→적용 분리. 문서 redaction 도구를 같은 패턴으로 프로토타이핑.
4주차프롬프트 버저닝 & 캐시content-addressed 캐시, 프롬프트 표면 해싱, 프로토콜 버전 상수. "프롬프트를 바꾸면 캐시가 깨지는" 안전 장치를 자기 프로젝트에 이식.
5주차Provider 추상화 & 어댑터이질적 API를 공통 Block으로 정규화, 불투명 상태 격리(ProviderData), 스트리밍 처리. 로컬/원격 모델 2종을 하나의 인터페이스로.
6주차대용량 입력 분할·병합구조 경계 기반 chunking, 각 조각을 유효 diff로 재구성, 전역 속성(move)의 조각 간 매핑. 스트리밍/맵-리듀스형 LLM 파이프라인 설계.

10핵심 키워드 사전

이 문서에서 반복된 용어를 한자리에
용어
reading diff (읽기용 diff)
meat의 출력물. unified diff의 모양(색·탐색성)은 유지하되, 적용 가능한 패치가 아니라 사람이 개념을 읽기 위한 압축본. 원본 헝크 카운트가 안 맞을 수 있음 — 어차피 다시 적용할 게 아니므로.
용어
edit plan (편집 계획)
모델이 제출하는 remove/replace/fold 배열. 좌표는 전부 원본 1-based 줄 번호. 모델이 만드는 유일한 산출물이며, meat가 이걸 원본에 적용해 결과를 만든다.
용어
constrained generation (제약 생성)
모델의 출력을 자유 텍스트가 아니라 좁은 스키마·연산으로 제한하는 기법. meat에선 "원본에 대한 지우기/가리기 연산"으로 제한해 환각을 봉쇄한다.
용어
fold / replace / remove
fold=연속된 여러 줄을 ... 한 줄로 접기(텍스트는 기계가). replace=한 줄의 일부만 로 가리기(원본 글자의 투영만 허용). remove=줄 범위 통째 삭제.
용어
frozen prompt surface (동결된 프롬프트 표면)
프롬프트는 "모델이 행동할 것"만 말하고 컴파일러 내부 규칙은 숨긴다는 원칙. 충돌은 계획 피드백으로만 설명. 이 표면 전체를 해싱한 게 rubric hash.
용어
retention pressure (잔존 압력)
계획이 원본을 너무 많이 남겼을 때(=덜 압축했을 때) 뜨는 신호. meat는 이때 "한 번 더 다듬어"라고 넛지하지만, 할당량이 아니라 조언이라 불확실하면 남기라고 명시.
용어
move detection (이동 탐지)
코드 블록이 A에서 삭제되고 B에서 동일하게 추가된 경우(=이동)를 정확한 근거로 탐지. 양쪽을 대칭 처리하게 강제해 "삭제+추가"로 오독되는 걸 막음. 전역 속성이라 chunk 단위에선 탐지 안 하고 매핑만 함.
용어
exe.dev gateway / Shelley
exe.dev=Bold의 관리형 VM/LLM 게이트웨이. VM에 llm 통합이 붙어 있으면 meat가 자동 감지해 API 키 없이 호출. Shelley=meat를 임베딩해 쓰는 에이전트(코드 주석에 등장) — meat가 라이브러리로 재사용되는 증거.

11참고 링크

원본을 직접 파고들 때