루프 마이닝: 변경 사항이 조직의 기억이 되는 과정
> Git 커밋은 구조화된 체인지로그 항목과 아키텍처 결정 기록이 되고, 이는 다시 쿼리 가능한 조직의 기억으로서 AI 에이전트에게 피드백됩니다.
이 글의 수치는 게시 시점(2026년 2월) 기준입니다. 최신 수치는 팀 페이지를 참고하세요.
모든 엔지니어링 팀은 같은 문제를 마주합니다. 변경은 끊임없이 일어나지만, 그 변경 뒤에 있는 이유는 사라집니다. 6개월 뒤 누군가 “왜 우리가 파이프라인 단계에 DuckDB를 채택했지?“라고 물으면, 그 답은 그 결정을 내린 사람의 머릿속에만 존재합니다. 그 사람이 아직 있다면 말이죠.
우리는 이 고리를 닫는 마이닝 워크플로를 만들었습니다. 변경 사항은 git 커밋을 거쳐 흐르고, 우리의 마이닝 파이프라인에서 처리되며, 구조화된 체인지로그 항목과 아키텍처 결정 기록이 되고, 그런 다음 CLI 쿼리를 통해 우리 AI 에이전트에게 다시 피드백됩니다. 결과는 사람과 AI 모두가 접근할 수 있는 조직의 기억입니다.
문제: 결정은 증발한다
전형적인 시나리오를 생각해봅시다. 어떤 개발자가 커밋합니다.
feat(canonical): add DuckDB runtime for pipeline stages
이 커밋은 중요한 아키텍처적 선택을 대표합니다. 팀은 옵션을 평가하고, 트레이드오프를 고려한 뒤, 특정한 이유로 DuckDB에 정착했습니다. 하지만 그 모든 맥락은 다음과 같은 곳에 존재합니다.
- 슬랙 스레드(아마 삭제되었을)
- 누군가의 기억(분명 희미해지고 있는)
- 코드 안의 주석(운이 좋다면 어쩌면)
3개월 뒤, 새로 합류한 팀원이 묻습니다. “이 새 단계에는 DuckDB를 써야 하나요, SQLite를 써야 하나요?” 조직의 기억이 없으면, 그들은 바퀴를 재발명하거나 일관성 없는 선택을 하게 됩니다.
루프: 커밋에서 컨텍스트로
우리의 마이닝 워크플로는 git 히스토리를 쿼리 가능한 지식으로 변환합니다.
Git Commits
│
▼
┌─────────────────────┐
│ mine sync │ ← Build index from git history
└─────────────────────┘
│
▼
┌─────────────────────┐
│ mine candidates │ ← Surface commits for review
└─────────────────────┘
│
▼
┌─────────────────────┐
│ Classification │ ← Human or LLM assessment
│ (changelog or ADR) │
└─────────────────────┘
│
├──────────────────────┐
▼ ▼
┌─────────────┐ ┌───────────────┐
│ Changelog │ │ Decisions │
│ Ledger │ │ Registry │
│ (JSONL) │ │ (YAML files) │
└─────────────┘ └───────────────┘
│ │
▼ ▼
┌─────────────┐ ┌───────────────┐
│ CHANGELOG.md│ │ orkestra CLI │
│ per package │ │ queries │
└─────────────┘ └───────────────┘
│ │
└──────────────────────┘
│
▼
┌───────────────┐
│ AI Agents │
│ (via CLI) │
└───────────────┘
핵심 통찰은 이렇습니다. 체인지로그와 아키텍처 결정 모두 동일한 git 히스토리에서 흘러나와, 통합된 파이프라인을 거쳐 처리됩니다. 이는 아무것도 빠져나가지 않도록 보장합니다.
마이닝이 작동하는 방식
1단계: 인덱스 동기화
uv run orkestra mine sync
이 명령은 git 히스토리를 스캔하고 모든 커밋의 인덱스를 만듭니다. 각 커밋에서 구조화된 신호를 추출합니다.
- 컨벤셔널 커밋 타입 (
feat,fix,chore,docs) - 스코프(어떤 패키지나 영역인지)
- 브레이킹 체인지 마커
- 수정된 파일과 복잡도 지표
2단계: 커버리지 상태 확인
uv run orkestra mine status
현재 우리의 상태는 다음과 같습니다.
Mining Status
=============
Decisions
---------
Coverage: 100.0%
Processed: 15637 (of 15637)
Extracted: 476
Skipped: 15161
Changelog
---------
Coverage: 100.0%
Processed: 15637 (of 15637)
Released: 6799
Skipped: 8838
15,637개 커밋이 처리되었습니다. 476개는 아키텍처 결정이 되었습니다. 6,799개는 체인지로그 항목이 되었습니다. 모든 커밋이 분류되었습니다.
3단계: 검토 후보 가져오기
uv run orkestra mine candidates --limit 50 --full
이는 아직 처리되지 않은 커밋을 분류에 필요한 전체 맥락과 함께 표면화합니다.
on
{
"sha": "90571786d99166c0039ca2e80e4d9cd96184bd85",
"date": "2026-01-26",
"subject": "feat(canonical): add DuckDB runtime for pipeline stages",
"signals": {
"commit_type": "feat",
"scope": "canonical",
"breaking": false,
"is_releasable_type": true,
"domains_affected": ["pipeline", "data-architecture"]
},
"body": "Establishes DuckDB as canonical in-process analytical database...",
"files_changed": ["packages/canonical/pipelines/stages/duckdb_runtime.py", "..."],
"stats": {"files": 8, "insertions": 450, "deletions": 120}
}
이 신호들은 분류를 안내하는 데 도움이 됩니다. is_releasable_type: true는 이것이 체인지로그에 실려야 함을 시사합니다. 큰 삽입 라인 수와 인프라 파일들은 이것이 아키텍처 결정이기도 할 수 있음을 시사합니다.
4단계: 커밋 분류
여기서 두 갈래로 나뉩니다. 체인지로그 항목과 아키텍처 결정입니다.
체인지로그 항목의 경우:
uv run orkestra mine classify abc123 --changelog added
이는 커밋 abc123가 체인지로그의 “Added” 카테고리에 실려야 함을 기록합니다.
아키텍처 결정의 경우:
먼저 실제 결정 ID를 받습니다.
uv run orkestra decisions new --domain pipeline --dry-run
# Returns: DEC-PL-143
그런 다음 그 결정 ID로 분류합니다.
uv run orkestra mine classify abc123 --decision DEC-PL-143
이는 커밋을 생성되거나 업데이트될 결정 기록에 연결합니다.
배치 처리의 경우 (우리가 실제로 하는 방식):
# Generate classifications file with LLM assistance
uv run orkestra mine commit --input classifications.jsonl
JSONL 포맷은 한 번의 패스로 두 도메인을 모두 지원합니다.
on
l
{"sha":"abc123","domain":"changelog","action":"release","category":"added","summary":"Add DuckDB runtime for pipeline stages"}
{"sha":"abc123","domain":"decisions","action":"extract","decision_id":"DEC-PL-142"}
{"sha":"def456","domain":"changelog","action":"skip","reason":"Chore: dependency update"}
5단계: 출력 렌더링
uv run orkestra changelog render --package <pkg>
이는 원장(ledger)으로부터 패키지별 CHANGELOG.md 파일을 생성합니다. 체인지로그는 파생 산출물입니다. 삭제해도 원본 원장에서 완벽하게 다시 생성됩니다.
결정 기록의 구조
추출된 결정은 풍부한 메타데이터를 가진 YAML 파일이 됩니다.
id: DEC-PL-142
title: Adopt DuckDB as Canonical Processing Runtime for Pipeline Stages
domain: pipeline
status: active
created: '2026-01-26'
summary: |
Establishes DuckDB as the canonical in-process analytical database for pipeline
stage transformations. Provides a shared runtime module that resolves settings
from pipeline defaults with stage-level overrides.
context: |
Pipeline stages performing data transformations each independently configured
DuckDB connections. This led to inconsistent settings, duplicated configuration
code, and no way to tune DuckDB globally for a pipeline run.
rationale:
- DuckDB provides efficient in-process OLAP with zero configuration deployment
- Centralized runtime module eliminates duplicated DuckDB setup across stages
- Hierarchical settings enable global tuning with stage-level overrides
- Memory limits and thread counts can be adjusted per-pipeline
impact:
positive:
- Consistent DuckDB configuration across all pipeline stages
- Single point of control for memory/thread tuning
- Reduced code duplication in conversion and export stages
negative:
- Adds dependency on shared runtime module
- Stages must adopt new configuration pattern
source_commits:
- sha: 90571786d99166c0039ca2e80e4d9cd96184bd85
message: 'feat(canonical): add DuckDB runtime for pipeline stages'
date: '2026-01-26'
role: primary
files:
- packages/canonical/pipelines/stages/duckdb_runtime.py
- packages/canonical/pipelines/runner.py
- packages/canonical/pipelines/stages/convert_hdx_admin_boundaries_to_geoparquet.py
related:
- DEC-DA-014 # Data architecture decisions that influenced this
모든 결정은 자신의 출처 커밋으로 다시 연결됩니다. 모든 결정은 자신이 영향을 미치는 파일을 명시합니다. 결정들 사이의 관계는 명시적입니다.
CLI 통합: 조직의 기억 쿼리하기
여기서 고리가 닫힙니다. 에이전트는 CLI를 통해 결정을 쿼리할 수 있습니다.
# Search by topic
uv run orkestra decisions search --query "retry"
재시도 로직, 오류 처리, 복구 패턴에 관한 결정을 반환합니다.
# Get full details on a specific decision
uv run orkestra decisions info DEC-PL-142
맥락, 근거, 영향을 포함한 완전한 결정 기록을 반환합니다.
# List recent decisions for context
uv run orkestra decisions list --limit 15
최근에 어떤 아키텍처적 선택이 이루어졌는지 보여줍니다.
에이전트는 이것을 어떻게 사용하는가
우리 오케스트레이터의 기본 지침에는 다음이 포함됩니다.
**Essential CLI commands:**
- `orkestra decisions search "X"` — Find architectural decisions
DuckDB와 관련된 무언가를 구현하라는 요청을 받으면, 에이전트는 먼저 다음을 확인할 수 있습니다.
uv run orkestra decisions search --query "DuckDB"
그리고 DEC-PL-142를 발견하여 다음을 배웁니다.
- 왜 우리가 DuckDB를 선택했는지 (context)
- 올바르게 사용하는 방법 (agent_guidance)
- 어떤 파일을 봐야 하는지 (files)
- 어떤 관련 결정이 존재하는지 (related)
에이전트는 바퀴를 재발명하지 않습니다. 확립된 패턴 위에 쌓아 올립니다.
세 가지 질문 테스트
모든 커밋이 결정 기록으로 남을 자격이 있는 것은 아닙니다. 우리는 걸러내기 위해 세 가지 질문 테스트를 사용합니다.
- 이것을 결정하기 어려웠는가? 상당한 분석, 트레이드오프 평가, 혹은 논의가 필요했는가?
- 바꾸는 데 비용이 많이 드는가? 이 결정을 되돌리려면 상당한 재작업이 필요한가?
- 시스템 전반에 영향을 미치는가? 여러 패키지에 영향을 주거나 다른 사람들이 따를 패턴을 확립하는가?
커밋이 이 질문 중 최소 하나에 “예”라고 답한다면, 이는 결정 추출의 후보가 됩니다. 우리의 일반적인 비율은 커밋 100개당 14개의 결정(약 14%)입니다.
체인지로그 항목의 경우 기준이 더 낮습니다. 사용자에게 보이는 모든 변경(기능, 수정, 개선)은 기록됩니다. 내부적인 잡무, 문서 업데이트, 리팩터링은 보통 건너뜁니다. 우리의 일반적인 비율은 커밋 100개당 30~50개의 체인지로그 항목입니다.
데이터 저장: 추가 전용 원장
마이닝 시스템은 충돌 없는 다중 에이전트 운영을 위해 추가 전용(append-only) JSONL 원장을 사용합니다.
packages/orchestration/ai_assets/reference/changelog/
├── commits_processed.jsonl # Classification ledger (both domains)
├── release_notes.jsonl # Changelog entries
└── commits_index.yaml # Derived index (gitignored)
packages/orchestration/ai_assets/reference/decisions/
├── registry.yaml # Decision index
└── records/
├── DEC-AD-001.yaml
├── DEC-AD-002.yaml
└── ...
.gitattributes 안의 merge=union을 사용하는 JSONL 포맷 덕분에, 여러 에이전트가 병합 충돌 없이 동시에 커밋을 분류할 수 있습니다. 각 줄은 독립적입니다.
검증 게이트
모든 마이닝 세션 전에 우리는 검증을 실행합니다.
uv run orkestra mine validate --quick
이는 다음을 확인합니다.
- SHA 형식의 유효성
- 결정 ID 형식 준수
- 동일한 SHA에 대한 중복 항목 없음
- 참조된 결정이 실제로 존재함
분류 이후, 변경을 커밋하기 전에 다시 검증합니다.
이것이 중요한 이유
우리가 만든 피드백 루프는 여러 문제를 해결합니다.
새 팀원에게: “왜 우리가 X를 했지?“라고 묻는 대신, 결정 레지스트리를 검색할 수 있습니다. 맥락이 보존되어 있습니다.
AI 에이전트에게: 이들은 진공 상태에서 작동하지 않습니다. 권고를 하기 전에 조직의 지식을 쿼리할 수 있습니다. 새로운 파이프라인 단계를 추가해달라는 요청을 받으면, DuckDB 패턴을 발견하고 그것을 따를 수 있습니다.
아키텍처적 일관성에: 결정은 명시적이고 검색 가능합니다. 누군가 기존 결정과 모순되는 접근법을 제안하면, 시스템이 그 충돌을 드러낼 수 있습니다.
체인지로그 생성에: 릴리스 노트는 마지막 순간의 허둥지둥이 아닙니다. 개발 과정에서 이루어지는 지속적인 분류의 부산물입니다.
온보딩에: 새 에이전트는 코드베이스의 전체 맥락을 물려받습니다. 코드만 보는 것이 아니라, 그 코드를 만든 결정도 봅니다.
현재 상태
오늘 기준으로:
- 파이프라인을 거쳐 처리된 커밋 15,637개
- 추출되어 문서화된 아키텍처 결정 476개
- 기록된 체인지로그 항목 6,799개
- 두 도메인 모두에서 100% 커버리지
우리가 시작한 이래 모든 커밋이 분류되었습니다. 조직의 기억은 완전하고 쿼리 가능합니다.
시작하기
비슷한 것을 구현하고 싶다면:
-
컨벤셔널 커밋으로 시작하세요. 마이닝 파이프라인은 커밋에 구조화된 접두사(
feat:,fix:,chore:)가 있을 때 가장 잘 작동합니다. -
도메인을 정의하세요. 우리는
pipeline,agent-design,observability,data-modeling같은 도메인을 사용합니다. 이들은 결정을 영역별로 정리합니다. -
분류하는 습관을 만드세요. 마이닝은 팀이 정기적으로 커밋을 분류할 때 작동합니다. LLM의 도움을 받는 배치 처리는 규모를 키우는 데 도움이 됩니다.
-
결정을 쿼리 가능하게 만드세요. 에이전트가 CLI를 통해 결정을 검색할 수 있을 때 가치가 복리로 쌓입니다. 기계가 소비하기 좋게 출력을 구조화하세요.
-
고리를 닫으세요. 결정은 앞으로의 작업에 영향을 미쳐야 합니다. 에이전트 지침과 코드 리뷰 체크리스트에 결정 참조를 포함시키세요.
목표는 완벽한 문서화가 아닙니다. 오늘도, 6개월 뒤에도 사람과 AI 모두가 변경 뒤의 이유에 접근할 수 있도록 하는 것입니다. 변경이 조직의 기억이 되면, 팀은 그것을 재발명하는 대신 확립된 패턴 위에 쌓아 올립니다.
마이닝 워크플로는 우리 오케스트레이션 엔진의 일부이며, 구체적으로는 우리 오케스트레이션 패키지의 컨텍스트 엔진 모듈에 속합니다.
관련 글
Maguyva 빌드 로그의 다른 글들
우리가 코드 검색을 voyage-4-large로 업그레이드한 이유_
우리는 코드 임베딩을 voyage-4-large로 옮겼습니다. 현재 공개된 RTEB 코드 검색 리더보드에서 1위인 모델입니다. 솔직한 버전을 말하자면, 우리가 감수하는 트레이드오프, 실제로 우리가 인덱싱하는 것, 그리고 프리미엄 임베딩에 비용을 지불하는 이유입니다.
언어 재귀적 자기 개선: 약 280개 언어에 걸친 코드 인텔리전스 갈아내기_
우리는 약 280개 언어에 대한 코드 인텔리전스를 지원합니다. 사람이 이를 일일이 검수할 수는 없습니다. 그래서 우리는 언어 재귀적 자기 개선 루프 — 무작위 점검, LLM 판정, 한 가지 수정, 재검증 — 를 만들었고, 추출이 단순히 초록불(green)이 아니라 실제로 옳아질 때까지 격리된 에이전트 플릿으로 이를 실행합니다.
다중 모달 융합 검색: 모든 쿼리에 맞는 검색기를 고르는 법_
"parseConfig는 어디서 정의되었나" 같은 쿼리는 "인증은 어떻게 동작하나"와는 다른 검색을 원합니다. Maguyva는 의도를 분류하고, 그에 맞춰 네 가지 검색 방식에 가중치를 부여한 뒤, 가중 상호 순위 융합(Reciprocal Rank Fusion)으로 결과를 결합합니다.