# ClariDoc Harness 0.2.0 ClariDoc은 기술 블로그와 기술 문서를 계획·작성·검토·수정하는 멀티 모델 하네스다. 처음 `brief`와 프로젝트 문서를 넣으면 바로 글부터 쓰지 않는다. 로컬 문서 저장소에서 근거를 찾고, 문서 유형에 맞춰 독자가 문제와 선택을 따라갈 순서를 먼저 잡는다. 그다음 Codex, Claude, Google Antigravity가 계획과 작성, 검토와 수정을 나누어 맡는다. 이 과정에서는 두 가지를 끝까지 지킨다. 1. **근거 추적 정보와 독자용 글을 분리한다.** source ID, repository path, access date, prompt tag는 `provenance.md`와 `evidence-map.json`에만 남는다. 2. **기술 선택은 이유 없이 선언할 수 없다.** “의도적으로 사용한다”, “허용했다”, “금지했다”라고 썼다면 제약, 선택 이유, 대안, 수용 비용, 가드레일까지 이어져야 한다. ## 해결하려는 실패 최종 문서에서 다음 문장이 보이면 ClariDoc은 실패로 처리한다. ```text 예시는 2026-07-23 기준이다. Retries can increase load ... [S1] 제공된 근거 팩은 다음 사실을 확인 대상으로 제시한다. application-core는 Spring DI와 SLF4J를 의도적으로 사용한다. ``` 처음 세 문장에는 독자가 볼 필요가 없는 작성 과정과 provenance가 섞여 있다. 마지막 문장은 Spring DI를 선택했다는 사실만 있고 **왜 선택했는지**, **무슨 대안을 검토했는지**, **어떤 비용을 감수했는지**, **어디까지 허용했는지**는 알 수 없다. 그래서 ClariDoc 0.2.0은 독자가 읽을 내용과 근거를 추적할 때 필요한 기록을 서로 다른 파일에 남긴다. ```text reader-facing document.md └─ 문제, 제약, 대안, 선택 이유, 동작, 검증, 트레이드오프만 노출 internal provenance.md / evidence-map.json └─ source ID, 원본 경로, heading, line range, status, claim/decision ID 보존 ``` ## 전체 흐름 ```text brief.json + manual sources.json (선택) + local documentation repository │ ▼ [local corpus collector] canonical project / concept / branch note / official docs / company tech blogs를 chunk 검색 │ ▼ [문서 유형별 구조 계약] │ planner: Codex ▼ 질문 기반 outline + decision requirements │ writer: Claude ▼ reader-facing draft │ ┌──────────┼──────────┐ │ │ │ deterministic logic/ reader/editor/ linter decision evidence/operations │ reviews reviews └──────────┼──────────┘ ▼ quality gate 실패 │ │ 통과 ▼ ▼ reviser: Claude │ ▼ document.md + quality-report.md provenance.md + evidence-map.json + manifest.json ``` ## 기술 블로그의 기본 논리 구조 `technical_blog`는 다음 순서를 기본 계약으로 사용한다. 1. **구체적인 문제 장면**: 어떤 상황과 비용이 있었는가 2. **제약**: 단순한 해법을 막은 조건은 무엇인가 3. **선택지**: 어떤 대안과 실패한 시도를 검토했는가 4. **결정 이유**: 왜 골랐고, 무엇을 포기했으며, 어떤 경계를 지켰는가 5. **메커니즘**: 실제 모듈·인터페이스·제어 흐름에 어떻게 반영됐는가 6. **검증**: 어떤 테스트·빌드 규칙·관측값이 무엇을 증명하는가 7. **트레이드오프**: 얻은 것, 잃은 것, 적용하지 않을 조건은 무엇인가 8. **결론**: 다른 환경에서도 가져갈 판단은 무엇인가 우아한형제들 기술 블로그를 조사하면서 저는 여러 문제 해결 글이 `팀과 시스템의 상황 → 구체적인 문제와 비용 → 검토한 접근 → 선택과 구현 → 검증과 한계` 순서로 이어지는 것을 확인했다. 여기에 독자와 메시지, 개요와 문단 흐름을 다룬 개발자 글쓰기 자료를 더해 위 순서를 만들었다. 우아한형제들의 공식 편집 규정을 그대로 옮긴 것은 아니다. 어떤 글을 조사했고 어디까지 해석했는지는 [`research/FOUNDATIONS.md`](research/FOUNDATIONS.md)에 기록했다. ## 지원 문서 유형 | `document_type` | 기본 독자 과업 | 필수 논리 축 | |---|---|---| | `technical_blog` | 문제와 설계 판단 이해 | 문제 → 제약 → 대안 → 선택 이유 → 메커니즘 → 검증 → 비용 → 판단 | | `tutorial` | 따라 하며 결과와 개념 학습 | 결과 → 준비 → 경로 → 단계 → 체크포인트 → 검증 → 다음 학습 | | `how_to` | 특정 작업을 안전하게 완료 | 적용 조건 → 사전 조건 → 절차 → 확인 → 롤백 → 문제 해결 | | `explanation` | 개념과 인과 관계 이해 | 질문/답 → 익숙한 기준 → 모델 → 메커니즘 → 예시 → 대안 → 한계 | | `reference` | 정확한 항목 조회 | 범위 → 구문 → 필드 → 동작 → 오류 → 최소 예시 → 관련 항목 | | `troubleshooting` | 증상에서 원인·복구로 이동 | 증상 → 영향 → 안전 → 진단 → 원인 → 조치 → 복구 → 예방 | | `design_doc` | 대안을 비교하고 결정 승인 | 요약 → 문제 → 목표 → 제약 → 대안 → 결정 → 구조 → 실패 → 배포 → 관측 → 위험 | ## 설치 Python 3.10 이상이 필요하다. core runtime은 외부 Python package에 의존하지 않는다. ```bash python3 -m venv .venv . .venv/bin/activate python -m pip install -e . ``` Antigravity provider를 사용할 때만 선택 의존성을 설치한다. ```bash python -m pip install -e '.[antigravity]' ``` ## 로컬 문서 저장소를 근거로 사용하기 검색기는 기본으로 이 경로를 훑는다. ```text wiki/projects wiki/concepts raw/branch-notes raw/official-docs raw/company-tech-blogs ``` 프로젝트 문서 저장소를 직접 지정할 때: ```bash claridoc run \ --brief examples/briefs/application-core-spring-di-blog.json \ --source-root /path/to/local-document-repository \ --config config/pipeline.multi-agent.example.json \ --output .run/application-core-live ``` 검색 결과만 먼저 확인할 수도 있다. ```bash claridoc collect \ --root /path/to/local-document-repository \ --query 'application-core Spring DI 선택 이유 대안 비용 가드레일' \ --query 'TransactionPort spring-tx 금지 ArchUnit 검증' \ --top-k 24 \ --output .run/application-core-sources.json ``` 검색기는 먼저 Markdown 문서를 heading 단위로 나눈다. 그런 다음 BM25 계열 점수에 source type과 status, decision/rationale 용어의 가중치를 더해 관련 chunk를 고른다. Source pack에는 절대 경로를 넣지 않고 저장소를 기준으로 한 상대 경로만 남긴다. ### Source hierarchy | source type | 주 용도 | 주의점 | |---|---|---| | `canonical-project` | 현재 프로젝트의 검증된 상태 | 현재 상태의 우선 근거 | | `canonical-concept` | 재사용 가능한 개념 | 프로젝트 구현 사실과 구분 | | `branch-note` | 선택 배경, 대안, 결정 이력, 로컬 검증 | status를 보존하고 현재 canonical과 충돌 여부 확인 | | `official-doc` | vendor·protocol·표준 동작 | 프로젝트가 실제 채택했다는 증거는 아님 | | `company-tech-blog` | 선례와 경험 보고 | 보편 법칙으로 일반화하지 않음 | 검색 결과에 같은 기술 이름이 나온다고 바로 선택의 근거로 쓰지는 않는다. Planner는 이유와 대안, 제약과 비용을 실제로 설명하는 chunk를 결정 섹션에 먼저 배치한다. 그런 근거를 찾지 못하면 모델이 이유를 만들어 내지 않고 주장을 좁히거나 빼도록 한다. ## 독자용 인용 정책 독자에게 출처를 어떻게 보여 줄지는 `brief.json`의 `constraints.citation_style`에서 정한다. | 값 | 독자용 문서 | 내부 sidecar | |---|---|---| | `hidden` | source ID, URL, path, access date를 표시하지 않음 | 전체 provenance 보존 | | `footnote` | 공개 가능한 Markdown footnote | 내부 provenance도 보존 | | `inline_link` | 자연스러운 공개 링크 | 내부 provenance도 보존 | | `source_id` | `[SOURCE_ID]` 형식 허용 | 내부 provenance도 보존 | 기술 블로그에서 기본값인 `hidden`을 선택하면 독자용 문서에는 출처 표시가 나오지 않는다. `[S1]`, `Labc123...`, `raw/branch-notes/...`, “제공된 근거 팩” 같은 문자열이 남아 있으면 lint가 error로 잡는다. ## 날짜 정책 날짜와 버전을 본문에 표시할지는 `constraints.date_policy`에서 정한다. - `only_when_material`: 버전·날짜가 동작, 호환성, 재현성에 영향을 줄 때만 본문에 표시 - `always`: 제공된 version context를 자연스럽게 표시 - `never`: 날짜·버전 context를 독자용 글에 표시하지 않음 Source의 `accessed`는 독자에게 보여 주지 않고 내부 provenance에만 남긴다. 그래서 “예시는 2026-07-23 기준이다”처럼 접근 날짜만 알리는 문장은 기본 정책에서 error 또는 warning이 된다. ## 선택 이유 계약 문서에 다음 한 문장만 있다면 선택 이유가 빠진 것이다. ```text application-core는 Spring DI를 의도적으로 사용한다. ``` 이 한 문장만으로는 왜 Spring DI를 허용했는지 알 수 없다. ClariDoc은 기술 선택을 설명할 때 적어도 아래 내용을 함께 요구한다. ```text context / constraint → chosen option → why it was chosen → realistic alternative → accepted cost → guardrail or boundary ``` 실제 문장으로 옮기면 다음과 같다. ```text application-core는 use case를 component scanning으로 등록하기 위해 @Service와 @Component를 허용했다. Spring DI까지 제거하면 use case마다 @Configuration에서 bean을 수동 등록해야 해 조립 코드가 빠르게 늘어나기 때문이다. 대신 application-core가 spring-context와 spring-beans에 의존하는 비용을 수용한다. 그 비용이 transaction·transport·persistence 의존으로 번지지 않도록 spring-tx, Spring Web, JPA는 금지하고 Gradle과 ArchUnit으로 검사한다. ``` 이렇게 쓰면 Spring DI의 장점뿐 아니라 검토한 대안과 감수한 비용, 의존성이 번지지 않게 막은 범위까지 함께 확인할 수 있다. ## 포함된 `application-core` 예시 - 독자용 완성 예시: [`examples/golden/application-core-spring-di-boundary.md`](examples/golden/application-core-spring-di-boundary.md) - 내부 provenance 예시: [`examples/golden/application-core-spring-di-boundary.provenance.md`](examples/golden/application-core-spring-di-boundary.provenance.md) - machine-readable evidence map: [`examples/golden/application-core-spring-di-boundary.evidence-map.json`](examples/golden/application-core-spring-di-boundary.evidence-map.json) - brief: [`examples/briefs/application-core-spring-di-blog.json`](examples/briefs/application-core-spring-di-blog.json) - 최소 로컬 corpus: [`examples/corpus/llm-wiki-mini/`](examples/corpus/llm-wiki-mini/) 예시 글은 Spring DI 허용 이유를 수동 bean 등록 비용과 연결한다. `spring-tx`·Spring Web·JPA 금지, `TransactionPort`, Gradle/ArchUnit 검사, reflection 우회 한계까지 설명한다. corpus에서 명시적인 선택 이유를 확보하지 못한 SLF4J는 독자용 글에서 언급하지 않는다. ## Provider 역할 기본 multi-agent 예제에서는 다음과 같이 작업을 나눈다. | 역할 | provider | 책임 | |---|---|---| | planner | Codex | 구조 계약 정교화, evidence allocation | | writer | Claude | 독자용 완성 초안 | | logic reviewer | Codex | 인과·전제·결론 검사 | | decision reviewer | Codex | 선택 이유·대안·비용·가드레일 검사 | | reader reviewer | Claude | 독자 맥락·인지 부하·정보 누락 검사 | | editor reviewer | Claude | 도입·문단 초점·전환·반복·상투적 LLM 문구 검사 | | evidence reviewer | Antigravity | source fit·status·과장 검사 | | operations reviewer | Antigravity | 절차·안전·검증·롤백 검사 | | reviser | Claude | blocker/error 수정 | 실행하기 전에는 각 provider가 설치되어 있고 인증할 수 있는지 먼저 확인한다. ```bash claridoc doctor --config config/pipeline.multi-agent.example.json ``` 자세한 통합 계약은 [`docs/PROVIDERS.md`](docs/PROVIDERS.md)를 참조한다. ## Mock 실행 Mock을 실행하면 외부 모델을 부르지 않고도 파이프라인 연결과 artifact 생성을 확인할 수 있다. ```bash claridoc run \ --brief examples/briefs/retry-policy-blog.json \ --sources examples/sources/retry-policy-sources.json \ --config config/pipeline.mock.json \ --output .run/retry-policy-mock ``` Mock은 source excerpt를 글에 복사하지 않는다. 실행 결과가 PASS여도 문장이 잘 쓰였다는 뜻은 아니다. 여기서 확인할 수 있는 것은 구조와 계약, 파이프라인 fixture가 연결됐다는 점까지다. ## 명령어 ```text claridoc init [directory] [--force] claridoc collect --root ROOT --query QUERY [--query QUERY] --output SOURCES claridoc validate --brief BRIEF [--sources SOURCES] [--source-root ROOT] claridoc outline --brief BRIEF [--sources SOURCES] [--source-root ROOT] [--output OUTLINE] claridoc lint DOCUMENT --brief BRIEF [--sources SOURCES] [--source-root ROOT] [--json] claridoc run --brief BRIEF [--sources SOURCES] [--source-root ROOT] [--config PIPELINE] --output RUN_DIR claridoc doctor --config PIPELINE [--json] ``` `validate`, `outline`, `lint`, `run`은 local corpus 옵션을 공유한다. ```text --source-root ROOT --source-include RELATIVE_DIR # 반복 가능 --source-top-k N --source-max-per-file N ``` ## 결정적 lint 주요 검사: - 정확히 하나의 H1과 필수 H2의 존재·중복·순서 - 기술 블로그가 prompt contract가 아니라 구체적 문제에서 시작하는지 - “제공된 근거 팩”, prompt tag, section-planning narration 누출 - hidden citation 모드에서 source ID와 repository path 누출 - access-date/example-date boilerplate - 기술 선택 선언 뒤 이유 누락 (`RAT001`) - 대안·수용 비용·가드레일 누락 (`RAT002`) - decision section에 rationale evidence가 배치되지 않은 경우 (`RAT003`) - 코드 fence, heading depth, 문단·문장 밀도 - 한국어 기술 블로그에서 `첫 번째/두 번째/세 번째 + 추상 분류명`이 가까운 문단에 반복되는 문장 scaffolding (`STYLE001`) - 절차의 사전 조건, 단계, 검증, 롤백 - 파괴적 명령 주변의 영향 경고, checkpoint, verification - 금지 주장과 미해결 TODO Lint를 통과했다고 문장의 의미까지 맞는 것은 아니다. Lint가 정해진 규칙을 검사한 뒤에도 모델 reviewer와 프로젝트 소유자가 내용을 다시 확인해야 한다. ## 산출물 ```text run-dir/ ├── inputs/ │ ├── brief.normalized.json │ ├── sources.normalized.json │ └── pipeline.normalized.json ├── stages/ │ ├── 01-planner.raw.txt │ ├── 02-outline.json │ ├── 02-outline.md │ └── 03-writer.raw.txt ├── rounds/round-*/ │ ├── draft.md │ ├── lint.json │ ├── lint.md │ ├── review-*.json │ └── quality-gate.json ├── final/ │ ├── document.md # 독자용 │ ├── quality-report.md │ ├── provenance.md # 내부용 │ └── evidence-map.json # 내부용 ├── provider-events.jsonl ├── run.json └── manifest.json ``` `manifest.json`은 자신을 제외한 모든 artifact의 크기와 SHA-256을 기록한다. ## 검증 ```bash bash scripts/verify.sh ``` 이 명령은 unit/integration test부터 Python 3.10 grammar parse, JSON과 JSON Schema, Markdown local link, local corpus retrieval, golden example lint를 차례로 확인한다. 이어서 Mock end-to-end, provenance sidecar, manifest 재검산, wheel build/install smoke test까지 실행한다. 최신 결과는 [`verification/TEST_REPORT.md`](verification/TEST_REPORT.md)에서 확인할 수 있다. ## 한계 - 로컬 corpus 검색은 lexical ranking이다. 의미가 유사하지만 단어가 다른 근거는 놓칠 수 있다. - source chunk가 검색됐다고 그 내용을 바로 본문에 쓸 수 있는 것은 아니다. status와 governing source를 함께 확인해야 한다. - LLM reviewer의 합의는 진실의 증명이 아니다. - 실제 코드 예시, command, 운영 수치, 보안 주장은 대상 시스템에서 별도로 검증해야 한다. - provider binary, SDK, 인증, quota, model ID는 실행 환경마다 다르다. - Mock 실행은 문서 품질을 증명하지 않는다. 위협 모델과 prompt-injection 경계는 [`docs/SECURITY.md`](docs/SECURITY.md)에 정리했다.