diff --git "a/backlog/tasks/gf-145 - Sigkill-Foundry-\355\230\225\354\213\235-\354\204\244\352\263\204-\353\254\270\354\204\234-\354\204\270\354\232\260\352\270\260.md" "b/backlog/tasks/gf-145 - Sigkill-Foundry-\355\230\225\354\213\235-\354\204\244\352\263\204-\353\254\270\354\204\234-\354\204\270\354\232\260\352\270\260.md" new file mode 100644 index 0000000..cb8ce4c --- /dev/null +++ "b/backlog/tasks/gf-145 - Sigkill-Foundry-\355\230\225\354\213\235-\354\204\244\352\263\204-\353\254\270\354\204\234-\354\204\270\354\232\260\352\270\260.md" @@ -0,0 +1,40 @@ +--- +id: GF-145 +title: Sigkill Foundry 형식 설계 문서 세우기 +status: Done +assignee: [] +created_date: '2026-10-05 11:52' +updated_date: '2026-10-10 22:38' +labels: + - docs +dependencies: [] +--- + +## Description + + +최상위 Sigkill Foundry 설계 문서와 같은 5문서 세트(design/: 0 문서 정보, 1 제안서 ~ 5 구현 결과 보고서, 부록)를 이 저장소에 세우고, Claude 앱 문서를 편집 원본으로 둔다. 이 저장소의 README·backlog 레퍼런스 문서·decision에서 1.01 배경과 문제, 1.02 원칙, 3.01 시스템 구성을 채우고 나머지 장은 골격으로 둔다(원천은 기존 문서). 진척 서술 금지. + +수용 기준(승격 시 AC로 등록): +1. design/ 구조·장 형식이 최상위 Sigkill Foundry와 같다 +2. 1.01 · 1.02 · 3.01을 기존 문서 근거로 채우고 나머지는 골격으로 둔다 +3. Claude 앱에 장별 문서와 목차 문서를 만들고 design/README.md에 링크한다 + + +## Acceptance Criteria + +- [x] #1 design/ 구조·장 형식이 최상위 Sigkill Foundry와 같다 +- [x] #2 1.01 · 1.02 · 3.01을 기존 문서 근거로 채우고 나머지는 골격으로 둔다 +- [x] #3 설계 문서의 원본은 design/ 하나다 — Claude 앱 편집 원본 문구 · 링크 칸을 두지 않고, 목차 상태는 실제 파일 기준 + + +## Final Summary + + +최상위 Sigkill Foundry와 같은 5문서 설계 세트(design/)를 세웠다. 장 파일 구성은 최상위와 일치. +- 0 문서 정보: 최상위 TRL · NFR 번호를 같은 ID · 같은 뜻으로 이어받음(로컬 번호 대역 없음). +- 1.01 배경과 문제(계측 컴포넌트, 검사와 기록, 문제-대응 7행, 세 기둥), 1.02 원칙 P1~P12(대체 관계 표 포함), 3.01 시스템 구성(구성 요소 8개, 커밋 생애주기 DOT 흐름도, 트레일러 11종 표, TRL ↔ 훅 함수 대응). 나머지는 골격. +- 3.01은 현재 코드 기준(훅 3개) — decision-18·19의 통합 구조와의 차이는 3.05 · 4.03에서 다룸. +- 원본은 design/ 하나(Claude 앱 편집 원본 규칙 없음). +- 검증: python3 -m unittest discover -s tests → 106 OK. + diff --git "a/design/00-\353\254\270\354\204\234-\354\240\225\353\263\264.md" "b/design/00-\353\254\270\354\204\234-\354\240\225\353\263\264.md" new file mode 100644 index 0000000..362ed5f --- /dev/null +++ "b/design/00-\353\254\270\354\204\234-\354\240\225\353\263\264.md" @@ -0,0 +1,77 @@ +# 0. 문서 정보 + +이 문서는 coding-agent-git-commit-tool의 설계 문서다. 상위 문서는 최상위 Sigkill Foundry 설계 문서이고, +이 컴포넌트는 그 3.03 컴포넌트 명세 §3에 해당한다. 스펙 주도 개발(SDD)을 따른다 — 구현은 이 명세에서 +도출하고, 명세와 코드가 어긋나면 명세를 먼저 고친 뒤 코드를 맞춘다. + +## 문서 구성 + +설계 문서는 다섯 문서로 이어진다. 문서마다 하나의 질문에 답하고, 앞 문서의 답이 뒤 문서의 입력이 된다. + +| 번호 | 문서 | 답하는 질문 | +| --- | --- | --- | +| 1 | 프로젝트 제안서 | 왜 만들고, 무엇을 어디까지 하는가 | +| 2 | 요구사항 명세서 | 무엇을 만족해야 하는가 | +| 3 | 아키텍처 설계서 | 어떻게 구성되고 동작하는가 | +| 4 | 구현 계획서 | 어떤 순서로 만들고, 무엇으로 판정하는가 | +| 5 | 구현 결과 보고서 | 무엇을 만들었고, 판정은 어떻게 나왔는가 | + +장은 `문서 번호.장 번호`로 가리킨다 (예: 3.05 설계 결정). 장 안의 절은 `§`로 가리킨다 (예: 3.03 §1). +최상위 Sigkill Foundry 문서의 장은 앞에 "최상위"를 붙여 가리킨다 (예: 최상위 3.03 §3). + +## 규범 용어 + +| 표현 | 뜻 | +| --- | --- | +| 반드시 ~한다 (MUST) | 어기면 명세 위반이다 | +| ~해야 한다 (SHOULD) | 어길 때는 이유를 3.05 설계 결정에 남긴다 | +| ~할 수 있다 (MAY) | 선택 사항이다 | + +## 요구사항 표기 + +- 목표는 `G-<번호>`로 식별하고 1.03 목표와 범위에 둔다. 최상위 목표는 "최상위 G-n"으로 적는다. +- 원칙은 `P<번호>`로 식별하고 1.02 원칙에 둔다. 최상위 원칙은 "최상위 P<번호>"로 적는다. +- 기능 요구사항의 영역 접두어는 `TRL`이다. 최상위 2.03이 이 컴포넌트에 쓰는 접두어와 같다. 품질 요구사항은 `NFR`이다. +- 요구사항 ID는 `<영역>-<세 자리 번호>`이고 번호는 재사용하지 않는다. 번호 공간은 최상위 문서 하나다. + - 최상위 2.03의 `TRL-nnn`과 2.04의 `NFR-nnn`(이 컴포넌트는 NFR-004 · NFR-005)을 같은 ID · 같은 뜻으로 이어받는다. + - 하위 번호나 이 저장소 전용 번호 대역(`TRL-101` 같은)은 두지 않는다. 최상위에 없는 요구사항이 필요하면 최상위 2.03 · 2.04에 먼저 올려 번호를 받는다. + +- 설계 결정은 `ADR-<번호>`로 3.05에, 미결 사항은 `Q-<번호>`로 4.03에 둔다. 둘 다 이 저장소 안의 번호이고, + 최상위 것은 "최상위 ADR-n" · "최상위 Q-n"으로 적는다. +- 원천인 backlog decision은 `decision-<번호>`, 태스크는 `GF-<번호>`로 그대로 적는다. +- 모든 요구사항은 수용 기준을 하나 이상 가진다(4.02). +- 아직 정하지 않은 값은 `TBD(Q-n)`으로 적고, `Q-n`을 4.03 미결 사항에 둔다. + +## 추적 규칙 + +모든 요구사항은 다음 사슬로 앞뒤가 이어진다. 사슬이 끊긴 항목은 둘 수 없다. + +1. 최상위 요구사항 `TRL-0nn` · `NFR-0nn` (최상위 2.03 · 2.04) — 사슬의 위쪽 끝이다. +2. → 이 저장소의 목표 `G-n` (1.03) — 목표는 자신이 이바지하는 최상위 G-n을 적는다. +3. → 요구사항 `TRL-nnn` · `NFR-nnn` (2.03 · 2.04) — 최상위 ID를 그대로 받거나, `1nn`번대로 세분하며 상위 ID를 적는다. +4. → 구성 요소 · 인터페이스 (3.01 · 3.03 · 3.04) — 구성 요소는 자신이 구현하는 요구사항 ID를 적는다. +5. → 수용 기준 (4.02) · backlog 태스크 (4.01) — 요구사항 하나는 backlog 태스크의 AC로 내려간다. 태스크 카드는 자신이 구현하는 요구사항 ID를 적는다. +6. → 결과 판정 (5.02) — 수용 기준마다 통과/실패와 그 증거(테스트 로그 · 실측 커밋)를 남긴다. + +- 최상위 요구사항의 문장이 바뀌면 이 저장소의 2.03 · 2.04를 먼저 맞춘다. 이 저장소가 최상위 요구사항을 바꿀 필요를 찾으면 최상위 문서에 먼저 올린다. +- 원칙(1.02)에 어긋나는 요구사항은 둘 수 없다. 원칙을 바꾸려면 3.05 설계 결정에 결정을 먼저 남긴다. +- 계획과 다르게 구현한 것은 5.03 계획 대비 편차에 적고, 해당 설계 결정(3.05)에 연결한다. + +## 대상 저장소 + +| 컴포넌트 | 저장소 | 최상위 장 | +| --- | --- | --- | +| coding-agent-git-commit-tool | [coralstay/coding-agent-git-commit-tool][repo] | 3.03 §3 | + +## 편집 규칙 + +- 설계 내용의 원본은 저장소의 `design/**/*.md` 하나다. 다른 곳(Claude 앱 문서 · 위키 등)에 사본이나 편집 원본을 두지 않는다. 고칠 때는 이 파일을 직접 고치고(Claude Code · GitHub 편집기) 커밋 · PR로 남긴다. 최상위 Sigkill Foundry도 같은 규칙이다. +- `backlog/docs` · `backlog/decisions` · README는 이 설계 문서의 원천이다. 설계 문서가 원천을 대신하지 않고, 원천을 지우지 않는다. +- 한 장 = 한 문서. 같은 내용을 두 장에서 고치고 있다면 한쪽을 장 참조 한 줄로 접는다. +- 외부 문헌은 저장소에 옮겨 담지 않는다(decision-14). 인용과 검증 상태는 부록 참고 문헌에서만 관리한다. +- 다이어그램 원본은 `design/diagrams/*.dot`이다. DOT으로 먼저 쓰고, DOT으로 표현할 수 없는 것(시퀀스 등)만 mermaid(`.mmd`)로 쓴다. +- `design/build-diagrams.py`가 원본을 `design/diagrams/<이름>.svg`로 렌더한다. 색은 원본에 센티넬 hex로 적고, 빌드가 SVG 안의 CSS 변수(라이트 기본 · 다크 재정의)로 바꾼다. 빌드 전제: graphviz(`dot`), mermaid 렌더용 `npx` + 로컬 Chrome. +- 생성된 SVG는 손으로 고치지 않는다. 원본을 고치고 빌드를 다시 돌려 같은 커밋에 넣는다. 장 사본은 `![설명](../diagrams/<이름>.svg)`로 참조한다. +- 저장소 Markdown의 외부 링크는 문서 맨 아래 링크 정의 블록에 모으고, 본문은 `[표시][키]`로만 쓴다. + +[repo]: https://github.com/coralstay/coding-agent-git-commit-tool diff --git "a/design/1-\354\240\234\354\225\210\354\204\234/01-\353\260\260\352\262\275\352\263\274-\353\254\270\354\240\234.md" "b/design/1-\354\240\234\354\225\210\354\204\234/01-\353\260\260\352\262\275\352\263\274-\353\254\270\354\240\234.md" new file mode 100644 index 0000000..4d42735 --- /dev/null +++ "b/design/1-\354\240\234\354\225\210\354\204\234/01-\353\260\260\352\262\275\352\263\274-\353\254\270\354\240\234.md" @@ -0,0 +1,88 @@ +# 1.01 배경과 문제 + +coding-agent-git-commit-tool은 Sigkill Foundry의 계측 컴포넌트다(최상위 3.03 §3) — 코딩 에이전트가 만드는 git 커밋을 검사하고, 그 커밋을 누가 · 어떤 도구와 모델로 · 무슨 태스크로 · 얼마를 들여 만들었는지 커밋 안에 트레일러로 남긴다. + +## 무엇을 하는가 + +커밋할 때마다 두 가지를 한다. + +1. **검사** — 사람(또는 에이전트)이 쓴 제목 · 본문이 정해진 형식을 따르는지, 브랜치명에 태스크 번호가 + 있는지 확인하고, 어기면 커밋을 거부한다. +2. **기록** — 통과한 커밋 끝에 `Task-Id` · `AI-Tool` · `AI-Model` · `Tokens-Used` · `Tool-Calls` · + `Hooks-Commit` 같은 트레일러를 자동으로 붙인다. 사람이 타이핑하지 않는다. + +```text +[docs][backlog] DRAFT-18을 GF-136으로 승격 ← 사람(또는 에이전트)이 쓴다 + +왜 바꿨는지 설명하는 본문. 한 줄 72자 이내. ← 선택 + +Task-Id: GF-136 ↓ 아래는 전부 훅이 붙인다 +AI-Tool: claude-code +AI-Tool-Version: 2.1.267 +Co-Authored-By: Claude +AI-Model: claude-opus-5 +Tokens-Used: in=5067994 out=15794 +Tool-Calls: 31 +Hooks-Commit: c828491 +Signed-off-by: cpu-once <231006716+cpu-once@users.noreply.github.com> +``` + +목표는 `git log` 한 번으로 "무엇을, 왜, 누가(사람인지 어떤 모델인지), 얼마를 써서 바꿨는지"가 읽히는 +것이다. 동작은 `core.hooksPath` · `init.templateDir` · `git interpret-trailers` 같은 git 자체 기능과 +Python 3 표준 라이브러리만으로 이뤄진다. + +## 문제 + +에이전트가 커밋을 대량으로 남기면 이력이 작업 로그 구실을 해야 한다. 그런데 git의 기본 커밋에는 그 +구실에 필요한 정보가 없고, 있는 정보도 믿을 수 없게 되기 쉽다. + +| 문제 | 왜 생기는가 | 이 컴포넌트의 대응 | +| --- | --- | --- | +| 커밋이 어느 작업의 일부인지 모른다 | 태스크 번호를 커밋마다 손으로 쓰게 하면 잊고, 강제할 방법이 없다 | 작업을 시작할 때 한 번 정하는 브랜치명에서 태스크 번호를 읽어 강제하고 `Task-Id`로 남긴다(decision-4) | +| 사람 커밋과 에이전트 커밋이 구별되지 않는다 | 커밋 객체에는 author · committer만 있고 도구 · 모델이 없다 | `AI-Tool` · `AI-Tool-Version` · `AI-Model`을 남긴다(decision-5) | +| 에이전트가 말한 값은 믿을 수 없다 | 모델명 · 토큰 수를 에이전트에게 물으면 자가신고가 된다. 토큰 수는 모델이 아는 값이 아니라 API가 응답마다 실어 주는 메타데이터다 | 값은 Claude Code가 하위 프로세스에 주입하는 환경변수와, API 응답을 그대로 기록한 세션 트랜스크립트(`message.model` · `message.usage`)에서만 얻는다(decision-5 · decision-20) | +| 작업의 비용이 이력에 남지 않는다 | 토큰 사용량은 세션 단위로만 보이고 커밋과 이어지지 않는다 | 이 커밋이 바꾼 파일을 실제로 건드린 응답의 토큰을 합해 `Tokens-Used: in=<입력> out=<출력>`으로 남긴다(decision-27) | +| 검사를 건너뛸 수 있다 | `git commit --no-verify`는 `commit-msg`를 건너뛴다 | 건너뛸 수 없는 `prepare-commit-msg`로 강제를 옮기고, 건너뛴 사실은 `Verify-Bypassed`로 기록한다(decision-3 · decision-18) | +| 기록이 나중에 거짓이 된다 | rebase · force push로 커밋을 다시 만들면 트레일러가 자기 커밋에 대해 거짓을 말한다 — 실측에서 `Signed-off-by`와 실제 커미터가 어긋났다 | 이력은 append-only로 둔다. push된 이력의 보호는 원격 저장소 설정이 맡는다(decision-24) | +| 훅에 버그가 있으면 어느 커밋이 영향을 받았는지 모른다 | 훅 버전이 커밋에 남지 않는다 | 이 커밋을 검사한 훅 클론의 커밋 해시를 `Hooks-Commit`으로 남긴다(decision-5) | + +## 다루는 범위 — 세 기둥 + +git 이력을 작업 로그로 만드는 데 필요한 세 가지다(decision-25). 세 기둥은 서로를 필요로 한다 — +이력이 다시 쓰이면 트레일러가 거짓이 되고, 기록할 내용이 정형화돼 있지 않으면 지킬 가치가 줄어든다. + +| 기둥 | 하는 일 | 해당하는 것 | +| --- | --- | --- | +| 형식 | 사람이 쓰는 제목 · 본문을 정해진 양식으로 | 제목 · 본문 검증, 에디터 경로 거부, 커밋 단위 규칙(decision-22) | +| 출처 기록 | 누가 · 어떤 도구와 모델로 · 얼마를 들여 만들었는지 자동으로 남김 | 트레일러 전체, 토큰 측정, `AI-Model` 게이트, `Task-Id` 브랜치 강제 | +| 이력 불변 | 한 번 남긴 기록은 다시 쓰지 않음 | append-only 정책(decision-24), 원격 저장소 설정(doc-20) | + +용어로 말하면 trail(감사 추적)이 세 기둥 전체이고, provenance(출처 기록)가 두 번째 기둥이다. +attestation(서명된 증명)과 audit(사후 검사)이 어느 훅 · 함수 · 설정에 구현돼 있는지는 doc-21 개념 +지도에 있다. + +**다루지 않는 것.** 언어별 lint는 세 기둥 어디에도 속하지 않고 사용자에게 언어 도구를 전제하게 만들어 +범위에서 뺐다(decision-23). push 단계에서 테스트 · 빌드를 돌리지 않는다(decision-12). + +## Sigkill Foundry 안의 자리 + +| 항목 | 내용 | +| --- | --- | +| 층 | 계측 — 커밋 형식 검사 · 트레일러(태스크 · 모델 · 토큰) 부착 (최상위 3.01 컴포넌트 지도) | +| 어디서 도는가 | 대상 저장소의 git 훅(`core.hooksPath`). 에이전트 프로세스의 커밋이 이 훅을 거친다 | +| 구현하는 최상위 요구사항 | TRL-001 ~ TRL-009, NFR-004 · NFR-005 (최상위 2.03 · 2.04) | +| 이바지하는 최상위 목표 | G-4 계측과 감사 추적 | +| 부트스트랩 순서 | git init → backlog.md CLI → `backlog init` → **coding-agent-git-commit-tool** → claude-code-agile-hooks (최상위 3.02) | +| 소비자 | agent-orchestrator의 `CommitLedger`가 Done 시점에 태스크 브랜치의 트레일러를 읽어 태스크 카드에 기록한다 (최상위 ORC-021) | + +최상위 설계는 "커밋 = 함수, 커밋당 토큰 계량"을 핵심 성질로 둔다(최상위 1.02 P7). 그 성질을 실제 +커밋 안에 남기는 것이 이 컴포넌트다. 역할마다 별도 프로세스로 뜨는 구조(최상위 1.02 P2)는 세션 +트랜스크립트가 역할마다 따로 생긴다는 뜻이고, 이 컴포넌트는 그 트랜스크립트로 커밋마다 토큰을 +잰다. 역할 세션별 귀속이 정확한지는 아직 실측 근거가 없다(최상위 TRL-007, `TBD(최상위 Q-18)`). + +## 원천 + +- README.md — 소개 · 완성되는 커밋은 이런 모양입니다 · 각 필드가 무엇이고 언제 만들어지는지 · 다루는 범위 +- decision-3 · decision-4 · decision-5 · decision-18 · decision-20 · decision-24 · decision-25 · decision-27 · decision-29 +- doc-21 개념 지도 — trail · provenance · attestation · audit +- 최상위 3.01 시스템 구성 · 3.03 §3 coding-agent-git-commit-tool diff --git "a/design/1-\354\240\234\354\225\210\354\204\234/02-\354\233\220\354\271\231.md" "b/design/1-\354\240\234\354\225\210\354\204\234/02-\354\233\220\354\271\231.md" new file mode 100644 index 0000000..00a33b4 --- /dev/null +++ "b/design/1-\354\240\234\354\225\210\354\204\234/02-\354\233\220\354\271\231.md" @@ -0,0 +1,50 @@ +# 1.02 원칙 + +이 컴포넌트의 모든 요구사항이 따라야 하는 불변 규칙이다. 원칙을 바꾸려면 3.05 설계 결정에 결정을 먼저 남긴다. + +| ID | 원칙 | 근거 | 최상위 연결 | +| --- | --- | --- | --- | +| P1 | **git 내장 메커니즘만 쓴다.** 배포는 `core.hooksPath`(기존 저장소) · `init.templateDir`(새 저장소)로, 트레일러 조작은 `git interpret-trailers`로, 설정 읽기는 `git config --file`로 한다. ini를 직접 파싱하지 않는다. | 기존 저장소와 새 저장소를 한 메커니즘으로는 덮지 못해 둘을 함께 쓴다(decision-2). 설정을 직접 파싱하면 다중값(`--get-all`) · 따옴표 처리 같은 git 자신의 의미론과 조용히 갈라진다(decision-16) | TRL-009 | +| P2 | **훅은 Python 3 표준 라이브러리만으로, 해석되는 소스 그대로 배포한다.** 설치할 패키지가 없다. 컴파일된 바이너리로 배포하지 않는다. 설치 스크립트(`install.sh`)만 POSIX sh다. | 사용자는 적용 전에 `hooks/` 코드를 직접 읽고 검토할 수 있어야 한다 — "읽는 파일 = 실행되는 파일". 바이너리 배포는 이 약속과 충돌해 기각됐다(decision-16) | TRL-009 | +| P3 | **파일 하나만 읽으면 그 훅의 동작을 전부 알 수 있다.** 훅 사이에 공유 모듈을 두지 않고, 겹치는 블록(자기 위치 해석 · conf 읽기 가드 · 브랜치 계산)은 파일마다 중복을 유지한다. 공유하는 것은 `gitformat.conf`의 값뿐이다. | 감사 가능성의 일부다. 언어 제약에서 온 것이 아니라 이 컴포넌트가 지키려는 성질이다(decision-16) | — | +| P4 | **값은 검증 가능한 출처에서만 얻는다.** 에이전트에게 묻지 않는다. 모델명과 토큰 수는 API 응답을 그대로 기록한 세션 트랜스크립트(`message.model` · `message.usage`)에서, 에이전트 여부는 실재를 확인할 수 있는 신호(`CLAUDE_CODE_SESSION_ID`가 가리키는 파일 · `CLAUDE_PID`가 가리키는 프로세스)로만 판정한다. 존재 여부뿐인 플래그성 환경변수는 판정에 쓰지 않는다. | 자가신고는 진실성을 검증할 수 없다(decision-5). 다른 AI 도구 8종을 조사했지만 세션 상관 채널과 서버가 확정한 모델 값을 동시에 가진 도구가 없어, 그 도구들은 존재 · 화이트리스트 검증까지만 한다(decision-15 · decision-17). 판정 신호는 위조 · 누락이 쉬운 것을 뺐다(decision-20) | TRL-004 · TRL-006 | +| P5 | **측정할 수 없으면 사유와 함께 그렇다고 남긴다.** 틀린 값이나 일부만 집계한 값을 정상값처럼 남기지 않는다. 구성요소를 못 구해도 트레일러 줄은 남긴다(`unavailable (<사유>)`). | 조용한 생략이 곧 잘못된 귀속이다 — 에이전트 커밋이 사람 커밋으로 보이게 된다(decision-20). 과소계상이 알려진 채널의 값도 같은 이유로 받지 않는다(decision-17) | TRL-005 · NFR-005 | +| P6 | **강제할 수 있는 자리에서 강제하고, 강제할 수 없는 자리에서는 기록한다.** 규칙 강제는 `--no-verify`로 건너뛸 수 없고 커밋 객체가 생기기 전에 도는 `prepare-commit-msg`에 모은다. 커밋이 이미 만들어진 뒤에 도는 `post-commit`은 막지 않고 기록만 한다. | git 2.54.0 실측: `prepare-commit-msg`는 `--no-verify`에도 실행되고 `exit 1`로 커밋을 막는다(decision-18, doc-15). 대가로 에디터 경로는 거부한다 — 에디터가 이 훅보다 뒤에 열려 최종 메시지를 볼 수 없다. 건너뛴 검사는 막지 못하는 대신 `Verify-Bypassed`로 남긴다(decision-3) | TRL-001 · TRL-008 | +| P7 | **이력은 append-only다.** 커밋이 만들어진 뒤에는 그 객체를 다시 만들지 않는다. 금지선은 공개(push)된 이력이다 — push 전 로컬 정리는 허용한다. 되돌리기는 revert, 통합은 머지 커밋으로 한다. 로컬 훅이 막지 못하는 것은 원격 저장소 설정이 막는다. | 이력이 다시 쓰이면 트레일러가 자기 커밋에 대해 거짓이 된다(decision-24의 실측). Linux 커널 메인테이너 문서도 공개된 이력을 바꾸지 말라고 한다 | — | +| P8 | **범위는 형식 · 출처 기록 · 이력 불변 세 기둥이다.** 세 기둥 어디에도 속하지 않는 기능(언어별 lint, push 단계 테스트 · 빌드)은 두지 않는다. | 언어 lint는 사용자에게 언어 도구를 전제하게 만들고, 도구 부재 시 조용히 건너뛰다 사고를 냈다(decision-23). 세 기둥이 이 컴포넌트의 목적이다(decision-25). push 단계의 무거운 검사는 범위 밖이다(decision-12) | — | +| P9 | **커밋 하나 = 논리적 변경 하나 = 함수 하나 수준.** 이 규칙은 정책이고 훅이 강제하지 않는다. "커밋 하나 = 파일 하나"는 규칙이 아니라 결과다. | 커밋 사이 구간이 짧을수록 토큰 귀속에 무관한 작업이 덜 섞인다 — 측정 정밀도가 규율에서 나온다(decision-22) | 최상위 P7 | +| P10 | **커밋에 남기면 안 되는 것은 남기지 않는다.** 세션 ID는 트랜스크립트를 찾는 데만 쓰고 커밋에 남기지 않는다. 트랜스크립트에서는 필요한 필드만 읽고 대화 내용을 복제하지 않는다. 인증 정보(`CLAUDE_CODE_MESSAGING_SOCKET` · `_TOKEN`)는 절대 남기지 않는다. | 커밋 이력은 공개 저장소에 영구히 남는다(decision-5 amendment · decision-20) | — | +| P11 | **외부 문서를 저장소에 옮겨 담지 않는다.** 규칙만 뽑아 적용하고, 필요할 때마다 다시 조회한다. | 라이선스가 다른 콘텐츠가 별도 라이선스 레이어를 만든다. 저장소를 단일 라이선스로 두는 것이 vendoring의 편의보다 중요하다(decision-14) | — | +| P12 | **테스트도 표준 라이브러리로 돈다.** 테스트는 `unittest`로 쓰고 `python3 -m unittest discover -s tests`로 실행한다. `pytest` · bats 같은 외부 도구를 요구하지 않는다. | 훅이 "설치할 것 없음"인데 테스트만 외부 도구를 요구하는 비대칭을 없앤다. 트랜스크립트 픽스처를 `json.dumps`로 만들고 트레일러를 개수까지 검증할 수 있다(decision-21) | — | + +## 원칙끼리의 관계 + +- P4 · P5는 한 쌍이다. 검증 가능한 출처에서 값을 못 얻으면(P4) 추측으로 채우지 않고 사유를 남긴다(P5). +- P6 · P7은 "로컬 훅이 어디까지 지킬 수 있는가"의 답이다. 커밋 단계는 `prepare-commit-msg`가 강제하고, + push된 이력은 로컬 훅으로 지킬 수 없으므로 원격 설정에 맡긴다. +- P2 · P3 · P11은 감사 가능성을 이룬다 — 사용자가 이 컴포넌트를 믿으려면 실행되는 코드를 그대로 읽을 + 수 있어야 하고, 그 코드가 다른 라이선스 · 다른 파일에 기대지 않아야 한다. +- P9는 최상위 P7(계층은 코드 단위에 고정된다)의 "커밋 = 함수"를 이 컴포넌트 쪽에서 받은 것이다. + +## 원칙을 바꾼 결정 + +원칙은 결정으로만 바뀐다. 지금의 원칙에 이른 대체 관계는 다음과 같다. decision의 상태 표시는 +대체돼도 `accepted`로 남으므로, 대체 여부는 본문으로 확인한다. + +| 대체된 것 | 대체한 것 | 바뀐 원칙 | +| --- | --- | --- | +| decision-9의 "POSIX sh 문법만" (훅 범위) | decision-16 | P2 | +| decision-13 외부 셸 스타일 가이드 vendoring 허용 | decision-14 | P11 | +| decision-3의 마커 기반 우회 탐지 | decision-18 (`prepare-commit-msg` 통합) | P6 | +| decision-6 SQL lint 지원 | decision-23 | P8 | +| decision-11 · decision-12의 범위 제외 조항 | decision-25 | P7 · P8 | +| decision-19의 `delta` 조항 | decision-27 | P5 | +| decision-19의 `Signed-off-by` 제거 조항 | decision-25 · decision-28 (유지) | — | +| bats 테스트 | decision-21 | P12 | + +## 원천 + +- decision-2 · decision-3 · decision-5 · decision-12 · decision-14 · decision-15 · decision-16 · decision-17 · decision-18 +- decision-20 · decision-21 · decision-22 · decision-23 · decision-24 · decision-25 · decision-27 · decision-28 +- doc-8 decision 이력 지도 · doc-15 훅 실행 경로 실측 +- CLAUDE.md — Backlog Decision 상태 확인 관례 diff --git "a/design/1-\354\240\234\354\225\210\354\204\234/03-\353\252\251\355\221\234\354\231\200-\353\262\224\354\234\204.md" "b/design/1-\354\240\234\354\225\210\354\204\234/03-\353\252\251\355\221\234\354\231\200-\353\262\224\354\234\204.md" new file mode 100644 index 0000000..3e19421 --- /dev/null +++ "b/design/1-\354\240\234\354\225\210\354\204\234/03-\353\252\251\355\221\234\354\231\200-\353\262\224\354\234\204.md" @@ -0,0 +1,18 @@ +# 1.03 목표와 범위 + +> 작성 전 + +이 컴포넌트가 이루려는 것과 하지 않는 것. + +## 담을 내용 + +- [ ] 목표 `G-n` — 최상위 G-4(계측과 감사 추적)를 이 컴포넌트에서 어떻게 나누는가 +- [ ] 범위 — 형식 · 출처 기록 · 이력 불변 세 기둥이 각각 어디까지인가 +- [ ] 범위 밖 — 언어별 lint(decision-23), push 단계 테스트 · 빌드(decision-12), 서명된 증명(attestation) +- [ ] 지원하는 AI 도구의 범위 — Claude Code와 그 외 도구의 보장 수준 차이(decision-15 · decision-17 · decision-29) + +## 원천 + +- README.md — 다루는 범위 +- decision-23 · decision-25 · decision-29 +- doc-21 개념 지도 diff --git "a/design/1-\354\240\234\354\225\210\354\204\234/04-\352\270\260\353\214\200-\355\232\250\352\263\274\354\231\200-\353\246\254\354\212\244\355\201\254.md" "b/design/1-\354\240\234\354\225\210\354\204\234/04-\352\270\260\353\214\200-\355\232\250\352\263\274\354\231\200-\353\246\254\354\212\244\355\201\254.md" new file mode 100644 index 0000000..5a32a2c --- /dev/null +++ "b/design/1-\354\240\234\354\225\210\354\204\234/04-\352\270\260\353\214\200-\355\232\250\352\263\274\354\231\200-\353\246\254\354\212\244\355\201\254.md" @@ -0,0 +1,17 @@ +# 1.04 기대 효과와 리스크 + +> 작성 전 + +이 컴포넌트가 주는 것과 그 대가. + +## 담을 내용 + +- [ ] 기대 효과 — `git log`만으로 태스크 · 모델 · 비용을 읽는다 +- [ ] 리스크 — 트랜스크립트 형식 변경, GUI git 클라이언트의 PATH, `core.hooksPath` 점유, 토큰 귀속의 근사성 +- [ ] 리스크마다 완화 수단과 남는 한계 + +## 원천 + +- doc-6 주의점과 한계 +- doc-16 토큰 · 툴콜 측정 방법과 한계 +- README.md — 필요조건 diff --git "a/design/2-\354\232\224\352\265\254\354\202\254\355\225\255-\353\252\205\354\204\270\354\204\234/01-\354\235\264\355\225\264\352\264\200\352\263\204\354\236\220\354\231\200-\354\232\264\354\230\201-\355\231\230\352\262\275.md" "b/design/2-\354\232\224\352\265\254\354\202\254\355\225\255-\353\252\205\354\204\270\354\204\234/01-\354\235\264\355\225\264\352\264\200\352\263\204\354\236\220\354\231\200-\354\232\264\354\230\201-\355\231\230\352\262\275.md" new file mode 100644 index 0000000..71b895e --- /dev/null +++ "b/design/2-\354\232\224\352\265\254\354\202\254\355\225\255-\353\252\205\354\204\270\354\204\234/01-\354\235\264\355\225\264\352\264\200\352\263\204\354\236\220\354\231\200-\354\232\264\354\230\201-\355\231\230\352\262\275.md" @@ -0,0 +1,17 @@ +# 2.01 이해관계자와 운영 환경 + +> 작성 전 + +누가 이 훅을 쓰고 읽으며, 어떤 환경에서 도는가. + +## 담을 내용 + +- [ ] 이해관계자 — 커밋하는 에이전트 · 사람, 트레일러를 읽는 agent-orchestrator(`CommitLedger`), 이력을 감사하는 사람 +- [ ] 운영 환경 — git 버전, `python3` PATH, Claude Code 세션 신호(`CLAUDE_CODE_SESSION_ID` · `CLAUDE_PID` · `AI_AGENT`) +- [ ] 설치 형태 — 로컬(`core.hooksPath`) · 전역(`init.templateDir`) + +## 원천 + +- doc-1 설치 가이드 +- doc-15 훅 실행 경로 실측 +- doc-17 에이전트 판정 신호 레퍼런스 diff --git "a/design/2-\354\232\224\352\265\254\354\202\254\355\225\255-\353\252\205\354\204\270\354\204\234/02-\353\217\204\353\251\224\354\235\270-\353\252\250\353\215\270.md" "b/design/2-\354\232\224\352\265\254\354\202\254\355\225\255-\353\252\205\354\204\270\354\204\234/02-\353\217\204\353\251\224\354\235\270-\353\252\250\353\215\270.md" new file mode 100644 index 0000000..d6c3081 --- /dev/null +++ "b/design/2-\354\232\224\352\265\254\354\202\254\355\225\255-\353\252\205\354\204\270\354\204\234/02-\353\217\204\353\251\224\354\235\270-\353\252\250\353\215\270.md" @@ -0,0 +1,19 @@ +# 2.02 도메인 모델 + +> 작성 전 + +커밋 · 트레일러 · 세션 · 응답 · 귀속의 개념과 관계. + +## 담을 내용 + +- [ ] 커밋 메시지의 구성 — 제목(type · subsystem · 설명) · 본문 · 트레일러 블록 +- [ ] 트레일러 키와 값의 형식 +- [ ] 세션 트랜스크립트 · 응답(`requestId`/`message.id`) · `tool_use` 블록 +- [ ] 귀속 — 응답이 어느 커밋에 들어가는가(decision-27) +- [ ] 용어 구분 — 턴 · 트랜스크립트 · 귀속 · 마커(doc-14) + +## 원천 + +- doc-14 용어 정리 +- doc-3 AI 귀속 트레일러 레퍼런스 +- decision-27 diff --git "a/design/2-\354\232\224\352\265\254\354\202\254\355\225\255-\353\252\205\354\204\270\354\204\234/03-\352\270\260\353\212\245-\354\232\224\352\265\254\354\202\254\355\225\255.md" "b/design/2-\354\232\224\352\265\254\354\202\254\355\225\255-\353\252\205\354\204\270\354\204\234/03-\352\270\260\353\212\245-\354\232\224\352\265\254\354\202\254\355\225\255.md" new file mode 100644 index 0000000..d4cf06e --- /dev/null +++ "b/design/2-\354\232\224\352\265\254\354\202\254\355\225\255-\353\252\205\354\204\270\354\204\234/03-\352\270\260\353\212\245-\354\232\224\352\265\254\354\202\254\355\225\255.md" @@ -0,0 +1,17 @@ +# 2.03 기능 요구사항 + +> 작성 전 + +이 컴포넌트가 만족해야 할 기능 요구사항. + +## 담을 내용 + +- [ ] 최상위 TRL-001 ~ TRL-009를 같은 ID로 받아 이 저장소의 말로 세분한 것 +- [ ] 최상위에 없어 새로 필요한 요구사항 — 최상위 2.03에 먼저 올려 번호를 받는다 +- [ ] 요구사항마다 목표 · 수용 기준(4.02) 연결 + +## 원천 + +- 최상위 2.03 TRL 절 +- README.md — 각 필드가 무엇이고 언제 만들어지는지 +- doc-2 커밋 메시지 규칙 상세 diff --git "a/design/2-\354\232\224\352\265\254\354\202\254\355\225\255-\353\252\205\354\204\270\354\204\234/04-\355\222\210\354\247\210-\354\232\224\352\265\254\354\202\254\355\225\255.md" "b/design/2-\354\232\224\352\265\254\354\202\254\355\225\255-\353\252\205\354\204\270\354\204\234/04-\355\222\210\354\247\210-\354\232\224\352\265\254\354\202\254\355\225\255.md" new file mode 100644 index 0000000..43c2fb9 --- /dev/null +++ "b/design/2-\354\232\224\352\265\254\354\202\254\355\225\255-\353\252\205\354\204\270\354\204\234/04-\355\222\210\354\247\210-\354\232\224\352\265\254\354\202\254\355\225\255.md" @@ -0,0 +1,18 @@ +# 2.04 품질 요구사항 + +> 작성 전 + +신뢰성 · 감사 가능성 · 이식성 같은 품질 요구사항. + +## 담을 내용 + +- [ ] 최상위 NFR-004 · NFR-005를 같은 ID로 받은 것 +- [ ] 견고성 — 비 UTF-8 로케일, 셸 메타문자, `python3` 부재, 설정 파일 읽기 실패 +- [ ] 감사 가능성 — 파일 하나로 훅 하나가 읽힌다(decision-16) +- [ ] 개인정보 — 세션 ID · 인증 정보 · 대화 내용을 커밋에 남기지 않는다 + +## 원천 + +- decision-8 견고성 테스트 전략 +- decision-16 · decision-20 +- tests/ diff --git "a/design/3-\354\225\204\355\202\244\355\205\215\354\262\230-\354\204\244\352\263\204\354\204\234/01-\354\213\234\354\212\244\355\205\234-\352\265\254\354\204\261.md" "b/design/3-\354\225\204\355\202\244\355\205\215\354\262\230-\354\204\244\352\263\204\354\204\234/01-\354\213\234\354\212\244\355\205\234-\352\265\254\354\204\261.md" new file mode 100644 index 0000000..a96dcc3 --- /dev/null +++ "b/design/3-\354\225\204\355\202\244\355\205\215\354\262\230-\354\204\244\352\263\204\354\204\234/01-\354\213\234\354\212\244\355\205\234-\352\265\254\354\204\261.md" @@ -0,0 +1,124 @@ +# 3.01 시스템 구성 + +coding-agent-git-commit-tool은 git이 커밋마다 실행하는 Python 훅 셋과, 그 훅들이 값만 공유하는 설정 파일 하나, 훅을 대상 저장소에 연결하는 POSIX sh 설치 스크립트로 이뤄진다 — 별도 프로세스나 서버 없이 대상 저장소의 `git commit` 안에서만 돈다. + +## 구성 요소 + +| 구성 요소 | 종류 | 언제 도는가 · 언제 쓰이는가 | 하는 일 | 구현하는 요구사항 | +| --- | --- | --- | --- | --- | +| `hooks/prepare-commit-msg` | git 훅 (Python 3) | 커밋 객체가 생기기 전, 에디터보다 앞. `--no-verify`로 건너뛸 수 없다 | 재생 · 병합 커밋 면제, 에디터 경로 거부, 검증 마커(`$GIT_DIR/.gitformat-verified`) 기록 | TRL-001 · TRL-008 | +| `hooks/commit-msg` | git 훅 (Python 3) | 메시지 확정 뒤, 커밋 객체가 생기기 전. `--no-verify`로 건너뛴다 | 제목 형식 · 길이, 제목과 본문 사이 빈 줄, 본문 줄 길이, `Fixes` 해시 실재, 브랜치명의 `-<번호>`, 비 Claude Code 도구의 `gitformat.aiModel` 존재 · 화이트리스트 검사. 거부할 때 검증 마커를 지운다 | TRL-001 · TRL-002 · TRL-006 | +| `hooks/post-commit` | git 훅 (Python 3) | 커밋 객체가 생긴 뒤. 건너뛸 수 없지만 커밋을 막지도 못한다 | 검증 마커 유무로 우회를 판정하고, 트레일러를 계산해 `git interpret-trailers` → `git commit --amend --no-verify`로 붙인다 | TRL-003 ~ TRL-008 | +| `hooks/gitformat.conf` | 설정 (git config 형식) | 각 훅이 시작할 때 `git config --file`로 읽는다 | 커밋 type 목록, 길이 제한, 트레일러 키 이름, 알려진 모델 ID, 예외 브랜치 기본값, 태스크 접두어 기본값, 마커 파일명. 값만 공유하고 로직은 공유하지 않는다 | TRL-001 · TRL-002 · TRL-006 | +| `install.sh` | 설치 스크립트 (POSIX sh) | 사람이 한 번 실행한다 | `python3` 존재 확인, 대상 저장소의 `core.hooksPath` · `commit.template` 설정. `--global`이면 `template/hooks/` 심볼릭 링크를 만들고 전역 `init.templateDir`를 설정한다 | TRL-009 | +| `template/` | git 템플릿 디렉터리 | `git init` · `git clone` 때 git이 새 저장소의 `.git/`로 복사한다 | `template/hooks/*`가 `hooks/`의 파일을 가리키는 절대경로 심볼릭 링크다. 클론 위치마다 달라 커밋하지 않는다 | TRL-009 | +| `.gitmessage` | 커밋 메시지 템플릿 | `commit.template`으로 연결된다 | 제목 · 본문 · 트레일러 형식 안내 | TRL-001 | +| `tests/test_*.py` | 테스트 (`unittest`) | 개발할 때 `python3 -m unittest discover -s tests` | 임시 저장소(`tests/isolated_repo.py`)에서 실제 `git commit`을 돌려 훅 동작을 검증한다 | 전부 (4.02) | + +훅 파일끼리는 서로를 import하지 않는다. 자기 위치 해석, conf 읽기 가드, 태스크 접두어 · 브랜치 계산 +같은 겹치는 블록은 파일마다 중복으로 둔다(1.02 P3). conf를 읽지 못하면 각 훅 앞부분의 같은 가드가 +즉시 멈춘다 — 빈 값으로 진행하면 원인을 알 수 없는 거부가 된다. + +## 커밋 생애주기 + +커밋 한 번에 훅이 도는 순서다. + +![커밋 한 번의 훅 생애주기](../diagrams/01-commit-flow.svg) + +| 순서 | 단계 | 커밋 객체 | `--no-verify`로 건너뛰나 | 실패하면 | +| --- | --- | --- | --- | --- | +| 1 | `prepare-commit-msg` | 아직 없음 | 건너뛸 수 없음 | 커밋이 막힌다 | +| 2 | (에디터) | 아직 없음 | — | 에디터 경로는 1번에서 이미 거부된다. 최종 메시지 검증은 `git commit -m` · `-F`로만 한다 | +| 3 | `commit-msg` | 아직 없음 | 건너뜀 | 커밋이 막히고 검증 마커가 지워진다 | +| 4 | 커밋 객체 생성 | **생성됨** | — | — | +| 5 | `post-commit` | 있음 | 건너뛸 수 없음 | 커밋 결과에 영향을 주지 못한다. 트레일러만 빠진다 | +| 6 | `git commit --amend --no-verify` | 다시 만들어짐 | — | 1번 훅이 다시 돌지만 `_GITFORMAT_AMEND_GUARD=1`을 보고 즉시 끝난다. `post-commit`도 같은 가드로 재귀를 끊는다 | + +- **재생 · 병합 커밋은 면제한다.** `MERGE_HEAD` · `CHERRY_PICK_HEAD` · `REBASE_HEAD` · `REVERT_HEAD`가 있거나 + source가 `merge` · `squash`면 `prepare-commit-msg`가 아무것도 하지 않는다. 재생 커밋은 이미 검증된 커밋의 + 복제이므로 다시 도장을 찍지 않는다(decision-18). +- **`--no-verify` 판정은 마커로 한다.** `prepare-commit-msg`가 마커를 쓰고, 거부한 `commit-msg`는 마커를 + 지운다. `post-commit`은 마커를 읽어 지우고, 마커가 없으면 `commit-msg`를 건너뛴 것으로 보고 + `Verify-Bypassed: true`를 붙인다. +- **`post-commit`은 기록만 한다.** 커밋이 이미 만들어진 뒤라 막을 수 없다(1.02 P6). `python3`가 훅 실행 + 시점의 PATH에 없으면 1 · 3번은 커밋을 막아 바로 드러나지만, 5번은 트레일러만 조용히 빠진다. +- 검사 · 트레일러 삽입을 `prepare-commit-msg` 하나로 모으는 구조(decision-18 · decision-19)와 이 장의 + 구성 사이의 차이는 3.05 설계 결정과 4.03 미결 사항에서 다룬다. + +## 트레일러 + +훅이 붙이는 트레일러다. 키 이름은 `gitformat.conf`의 `[gitformat "trailer"]`에서 읽는다. 메시지에 같은 +`키: 값`이 이미 있으면 다시 붙이지 않는다 — 사람이나 에이전트가 직접 쓴 값을 덮어쓰지 않는다. + +| 트레일러 | 값 | 값의 출처 | 붙는 조건 | 신뢰 수준 | 요구사항 | +| --- | --- | --- | --- | --- | --- | +| `Task-Id` | `-<번호>` | 브랜치명 (`git symbolic-ref --short HEAD`). 접두어는 `gitformat.taskPrefix`, 없으면 conf 기본값 | 브랜치명에 패턴이 있을 때. 예외 브랜치(`main` · `master` · `develop` · `release/*`)가 아니면 패턴이 없을 때 `commit-msg`가 커밋을 거부한다 | 자동 | TRL-002 · TRL-003 | +| `AI-Tool` | 도구 식별자 (예: `claude-code`) | `AI_AGENT` 환경변수의 첫 필드. Claude Code가 하위 프로세스에 주입한다 | `AI_AGENT`가 있을 때 | 프로세스 주입값 — LLM이 만든 값이 아님 | TRL-006 | +| `AI-Tool-Version` | 도구 버전 (예: `2.1.267`) | `AI_AGENT`의 둘째 필드 | `AI_AGENT`에 구분자가 있을 때 | 위와 같음 | TRL-006 | +| `Co-Authored-By` | `Claude ` | conf의 `gitformat.coAuthoredBy` | 도구가 `claude-code`일 때만 | 자동 | TRL-006 | +| `AI-Model` | 모델 ID (예: `claude-opus-5`) | Claude Code: 세션 트랜스크립트 (`~/.claude/projects//<세션>.jsonl`)의 마지막 assistant `message.model`. 그 외 도구: `gitformat.aiModel` | 모델 값을 얻었을 때. 트랜스크립트 조회 실패는 커밋을 막지 않는다 | Claude Code는 API 응답 그대로 / 그 외는 자가신고(존재 · 화이트리스트만 검증) | TRL-006 | +| `Tokens-Used` | `in=<입력> out=<출력>` | 이 커밋이 바꾼 파일을 건드린 응답(`tool_use`의 경로 대조)의 `message.usage` 합. 입력은 `input_tokens` + 캐시 생성 · 캐시 읽기 입력, 출력은 `output_tokens`. 서브에이전트 트랜스크립트 포함. 응답 하나는 한 커밋에만 귀속된다 | 에이전트 커밋일 때. 실패하면 `unavailable (<사유>)`, 귀속된 응답이 없으면 `in=0 out=0 (no-attributed-turn)` | API 응답 그대로(Claude Code 한정). 귀속은 근사치 | TRL-004 · TRL-005 · TRL-007 | +| `Tool-Calls` | 개수 | 귀속된 응답에서 대상 파일을 건드린 `tool_use` 블록 수 | `Tokens-Used`와 같음 | `Tokens-Used`와 같음 | TRL-004 · TRL-005 | +| `Hooks-Commit` | 짧은 커밋 해시 | 훅이 놓인 클론의 `git rev-parse --short HEAD` | 훅이 클론 안에 있을 때 | 완전 자동 | TRL-008 | +| `Verify-Bypassed` | `true` | 검증 마커의 부재 | `commit-msg`를 건너뛰었을 때 | 자동 | TRL-008 | +| `Signed-off-by` | `<이름> <이메일>` | 커미터 정보 (`git log -1 --format='%cn <%ce>'`) | 모든 커밋 | 완전 자동. 기록 당시의 커미터라 이력 재작성 여부를 사후에 대조하게 한다(decision-28) | — | +| `Fixes` | `<해시> ("<제목>")` | **사람이 쓴다** | 원인 커밋을 알 때만 | `commit-msg`가 해시 실재를 검증한다 | TRL-001 | + +`unavailable`의 사유는 `no-session-id` · `transcript-not-found` · `transcript-unreadable` · +`transcript-parse-failed` · `no-usage-channel`이다. 세션 ID는 트랜스크립트를 찾는 데만 쓰고 커밋에 남기지 +않는다(1.02 P10). + +## 요구사항 대응 + +최상위 TRL 요구사항이 어느 구성 요소에서 구현되는지다. + +| 요구사항 | 내용 (요약) | 구현 위치 | +| --- | --- | --- | +| TRL-001 | 제목 · 본문 형식 검사, 어기면 거부 | `commit-msg`의 `validate_format` · `validate_length` · `validate_fixes_trailer`, `prepare-commit-msg`의 에디터 경로 거부, conf의 `type` · `subjectMaxLength` · `bodyLineMaxLength` | +| TRL-002 | 브랜치명에 태스크 번호가 없으면 거부 | `commit-msg`의 `enforce_task_id_branch`, conf의 `taskPrefixDefault` · `branchExempt` | +| TRL-003 | `Task-Id` 트레일러 | `post-commit`의 `trailer_task_id` | +| TRL-004 | `Tokens-Used` · `Tool-Calls`를 사용량 메타데이터에서 | `post-commit`의 `measure_claude_code_token_usage` · `trailer_tokens_used` | +| TRL-005 | 측정 실패 시 `unavailable (<사유>)` | `post-commit`의 `trailer_tokens_used` | +| TRL-006 | `AI-Tool` · `AI-Tool-Version` · `AI-Model` | `post-commit`의 `trailer_ai_tool` · `trailer_ai_model`, `commit-msg`의 `enforce_ai_model_gate` | +| TRL-007 | 역할 세션의 토큰은 그 세션 트랜스크립트로만 | `post-commit`이 `CLAUDE_CODE_SESSION_ID`로 트랜스크립트를 고르고 귀속 기록(`$GIT_DIR/.gitformat-token-attributed`)을 세션 ID로 구분한다. 정확도는 `TBD(최상위 Q-18)` | +| TRL-008 | `Hooks-Commit` · `Verify-Bypassed` | `post-commit`의 `trailer_hooks_commit` · `detect_verify_bypass`, `prepare-commit-msg`의 마커 기록 | +| TRL-009 | Python 3 표준 라이브러리 + git 내장 메커니즘만 | 훅 셋 전부(표준 라이브러리만 import), `install.sh`의 `core.hooksPath` · `init.templateDir` 설정 | +| NFR-004 | 커밋 단위 토큰 관측 | TRL-004와 같음 | +| NFR-005 | 측정 불가는 사유와 함께 | TRL-005와 같음 | + +## 설정 레이어 + +값은 두 층에서 온다. 대상 저장소의 git config가 있으면 그쪽이 이긴다. + +| 층 | 위치 | 키 | +| --- | --- | --- | +| 내부 기본값 | `hooks/gitformat.conf` | `markerFile` · `taskPrefixDefault` · `branchExempt` · `aiToolClaudeCode` · `coAuthoredBy` · `knownModel` · `type` · `subjectMaxLength` · `bodyLineMaxLength` · `[gitformat "trailer"]` 키 이름 | +| 사용자 오버라이드 | 대상 저장소의 로컬 · 전역 git config | `gitformat.taskPrefix` · `gitformat.branchExempt`(기본값에 더해진다) · `gitformat.aiModel` | + +설정 키와 파일명은 기존 설치를 깨지 않도록 옛 이름(`gitformat.*`)을 쓴다. + +## 배치 + +| 설치 방식 | 명령 | git 설정 | 훅이 실행되는 경로 | +| --- | --- | --- | --- | +| 기존 저장소 | `install.sh [대상]` | 대상 저장소의 `core.hooksPath` = 클론의 `hooks/`, `commit.template` = 클론의 `.gitmessage` | 클론의 `hooks/*`를 직접 | +| 새 저장소 | `install.sh --global` | 전역 `init.templateDir` = 클론의 `template/` | `.git/hooks/*` 심볼릭 링크 → 클론의 `hooks/*`. 훅은 `realpath`로 링크를 따라가 conf를 찾는다 | + +어느 쪽이든 실행되는 파일은 클론의 `hooks/`에 있는 바로 그 파일이다. `core.hooksPath`를 점유하므로 +대상 저장소의 `.git/hooks/*`는 무시된다. + +훅이 `$GIT_DIR`에 남기는 상태 파일은 git이 추적하지 않는다. + +| 파일 | 쓰는 훅 | 수명 | +| --- | --- | --- | +| `.gitformat-verified` | `prepare-commit-msg`가 쓰고, `commit-msg`(거부 시) · `post-commit`이 지운다 | 커밋 한 번 | +| `.gitformat-token-attributed` | `post-commit` | 세션 동안 자란다. 첫 줄이 세션 ID이고 다르면 새로 시작한다 | + +## 원천 + +- README.md — 각 필드가 무엇이고 언제 만들어지는지 · 커밋 한 번에 훅이 도는 순서 · 필요조건 +- hooks/readme.md · template/README.md · hooks/gitformat.conf +- hooks/prepare-commit-msg · hooks/commit-msg · hooks/post-commit · install.sh 코드와 주석 +- decision-2 · decision-3 · decision-4 · decision-5 · decision-16 · decision-18 · decision-20 · decision-27 · decision-28 +- doc-3 AI 귀속 트레일러 레퍼런스 · doc-15 훅 실행 경로 실측 · doc-16 토큰 · 툴콜 측정 방법과 한계 +- 최상위 2.03 TRL 절 · 2.04 NFR-004 · NFR-005 diff --git "a/design/3-\354\225\204\355\202\244\355\205\215\354\262\230-\354\204\244\352\263\204\354\204\234/02-\353\217\231\354\236\221-\353\252\205\354\204\270.md" "b/design/3-\354\225\204\355\202\244\355\205\215\354\262\230-\354\204\244\352\263\204\354\204\234/02-\353\217\231\354\236\221-\353\252\205\354\204\270.md" new file mode 100644 index 0000000..deb61e4 --- /dev/null +++ "b/design/3-\354\225\204\355\202\244\355\205\215\354\262\230-\354\204\244\352\263\204\354\204\234/02-\353\217\231\354\236\221-\353\252\205\354\204\270.md" @@ -0,0 +1,19 @@ +# 3.02 동작 명세 + +> 작성 전 + +훅마다 입력에서 결과까지 어떤 순서로 판단하는가. + +## 담을 내용 + +- [ ] `prepare-commit-msg` — 재생 · 병합 면제, 에디터 경로 거부, 검증 마커 +- [ ] `commit-msg` — 형식 · 길이 · `Fixes` · 브랜치 `Task-Id` · `AI-Model` 게이트 +- [ ] `post-commit` — 트레일러 계산 순서, 중복 판정, `--amend` 재진입 가드 +- [ ] 토큰 귀속 알고리즘 — 응답 묶기, 대상 경로 대조, 귀속 기록(decision-27) +- [ ] 재생 · 병합 · rebase 같은 이력 연산에서의 동작(doc-19) + +## 원천 + +- hooks/ 코드 docstring +- doc-10 실행 순서와 병렬 구조 +- doc-16 · doc-19 diff --git "a/design/3-\354\225\204\355\202\244\355\205\215\354\262\230-\354\204\244\352\263\204\354\204\234/03-\354\273\264\355\217\254\353\204\214\355\212\270-\353\252\205\354\204\270.md" "b/design/3-\354\225\204\355\202\244\355\205\215\354\262\230-\354\204\244\352\263\204\354\204\234/03-\354\273\264\355\217\254\353\204\214\355\212\270-\353\252\205\354\204\270.md" new file mode 100644 index 0000000..ec4fe7e --- /dev/null +++ "b/design/3-\354\225\204\355\202\244\355\205\215\354\262\230-\354\204\244\352\263\204\354\204\234/03-\354\273\264\355\217\254\353\204\214\355\212\270-\353\252\205\354\204\270.md" @@ -0,0 +1,20 @@ +# 3.03 컴포넌트 명세 + +> 작성 전 + +훅 파일 하나하나의 책임 · 입력 · 출력 · 구현하는 요구사항. + +## 담을 내용 + +- [ ] §1 `prepare-commit-msg` +- [ ] §2 `commit-msg` +- [ ] §3 `post-commit` +- [ ] §4 `gitformat.conf` +- [ ] §5 `install.sh` · `template/` +- [ ] 컴포넌트마다 구현하는 요구사항 ID + +## 원천 + +- hooks/readme.md +- doc-5 저장소 구조 +- hooks/ 코드 docstring diff --git "a/design/3-\354\225\204\355\202\244\355\205\215\354\262\230-\354\204\244\352\263\204\354\204\234/04-\354\235\270\355\204\260\355\216\230\354\235\264\354\212\244-\352\263\204\354\225\275.md" "b/design/3-\354\225\204\355\202\244\355\205\215\354\262\230-\354\204\244\352\263\204\354\204\234/04-\354\235\270\355\204\260\355\216\230\354\235\264\354\212\244-\352\263\204\354\225\275.md" new file mode 100644 index 0000000..9d02534 --- /dev/null +++ "b/design/3-\354\225\204\355\202\244\355\205\215\354\262\230-\354\204\244\352\263\204\354\204\234/04-\354\235\270\355\204\260\355\216\230\354\235\264\354\212\244-\352\263\204\354\225\275.md" @@ -0,0 +1,19 @@ +# 3.04 인터페이스 계약 + +> 작성 전 + +이 컴포넌트가 밖과 주고받는 형식. + +## 담을 내용 + +- [ ] 커밋 메시지 형식 — 제목 · 본문 · 트레일러의 문법(소비자: agent-orchestrator `CommitLedger`, 최상위 3.04 §2) +- [ ] `unavailable (<사유>)` 사유 슬러그 목록 +- [ ] 컨슈머가 오버라이드하는 git config 키(`gitformat.taskPrefix` · `branchExempt` · `aiModel`) +- [ ] 환경변수 입력 — Claude Code가 주입하는 신호 +- [ ] 훅이 남기는 `$GIT_DIR` 상태 파일 + +## 원천 + +- doc-2 · doc-3 · doc-4 커스터마이즈 가이드 +- hooks/gitformat.conf +- 최상위 3.04 §2 diff --git "a/design/3-\354\225\204\355\202\244\355\205\215\354\262\230-\354\204\244\352\263\204\354\204\234/05-\354\204\244\352\263\204-\352\262\260\354\240\225.md" "b/design/3-\354\225\204\355\202\244\355\205\215\354\262\230-\354\204\244\352\263\204\354\204\234/05-\354\204\244\352\263\204-\352\262\260\354\240\225.md" new file mode 100644 index 0000000..2093334 --- /dev/null +++ "b/design/3-\354\225\204\355\202\244\355\205\215\354\262\230-\354\204\244\352\263\204\354\204\234/05-\354\204\244\352\263\204-\352\262\260\354\240\225.md" @@ -0,0 +1,16 @@ +# 3.05 설계 결정 + +> 작성 전 + +구조를 정한 결정과 그 대체 관계. + +## 담을 내용 + +- [ ] ADR 목록 — backlog decision 중 지금 유효한 것을 ADR-n으로 옮긴다 +- [ ] 대체 관계 — decision-1→10, 3→18, 6→23, 9→16, 13→14, 19의 일부→25 · 27 · 28, 25 · 26의 이름→29 +- [ ] 결정된 구조 변경 — 검사 · 트레일러 삽입을 `prepare-commit-msg` 하나로 모은다(decision-18 · decision-19) + +## 원천 + +- backlog/decisions +- doc-8 decision 이력 지도 diff --git "a/design/4-\352\265\254\355\230\204-\352\263\204\355\232\215\354\204\234/01-\353\247\210\354\235\274\354\212\244\355\206\244\352\263\274-\354\236\221\354\227\205-\353\266\204\355\225\264.md" "b/design/4-\352\265\254\355\230\204-\352\263\204\355\232\215\354\204\234/01-\353\247\210\354\235\274\354\212\244\355\206\244\352\263\274-\354\236\221\354\227\205-\353\266\204\355\225\264.md" new file mode 100644 index 0000000..0faf723 --- /dev/null +++ "b/design/4-\352\265\254\355\230\204-\352\263\204\355\232\215\354\204\234/01-\353\247\210\354\235\274\354\212\244\355\206\244\352\263\274-\354\236\221\354\227\205-\353\266\204\355\225\264.md" @@ -0,0 +1,15 @@ +# 4.01 마일스톤과 작업 분해 + +> 작성 전 + +어떤 순서로 무엇을 만드는가. + +## 담을 내용 + +- [ ] 마일스톤과 backlog 태스크 — 태스크마다 구현하는 요구사항 ID +- [ ] 작업 사이의 의존성 + +## 원천 + +- backlog 태스크 · draft +- doc-18 재설계 작업 순서와 의존성 diff --git "a/design/4-\352\265\254\355\230\204-\352\263\204\355\232\215\354\204\234/02-\354\210\230\354\232\251-\352\270\260\354\244\200\352\263\274-\352\262\200\354\246\235.md" "b/design/4-\352\265\254\355\230\204-\352\263\204\355\232\215\354\204\234/02-\354\210\230\354\232\251-\352\270\260\354\244\200\352\263\274-\352\262\200\354\246\235.md" new file mode 100644 index 0000000..2136c05 --- /dev/null +++ "b/design/4-\352\265\254\355\230\204-\352\263\204\355\232\215\354\204\234/02-\354\210\230\354\232\251-\352\270\260\354\244\200\352\263\274-\352\262\200\354\246\235.md" @@ -0,0 +1,17 @@ +# 4.02 수용 기준과 검증 + +> 작성 전 + +요구사항마다 무엇으로 판정하는가. + +## 담을 내용 + +- [ ] 요구사항 ID ↔ 수용 기준 ↔ 테스트 파일(`tests/test_*.py`) +- [ ] 검증 명령 — `python3 -m unittest discover -s tests`, `ruff`, `shellcheck -s sh install.sh` +- [ ] 실측으로만 판정할 수 있는 항목(토큰 귀속 정확도) + +## 원천 + +- tests/ +- decision-21 +- .github/workflows diff --git "a/design/4-\352\265\254\355\230\204-\352\263\204\355\232\215\354\204\234/03-\353\257\270\352\262\260-\354\202\254\355\225\255.md" "b/design/4-\352\265\254\355\230\204-\352\263\204\355\232\215\354\204\234/03-\353\257\270\352\262\260-\354\202\254\355\225\255.md" new file mode 100644 index 0000000..e808466 --- /dev/null +++ "b/design/4-\352\265\254\355\230\204-\352\263\204\355\232\215\354\204\234/03-\353\257\270\352\262\260-\354\202\254\355\225\255.md" @@ -0,0 +1,19 @@ +# 4.03 미결 사항 + +> 작성 전 + +아직 정하지 않은 것 — `Q-n`. + +## 담을 내용 + +- [ ] 역할 세션별 토큰 귀속의 정확도(최상위 Q-18) +- [ ] 컨슈머 저장소 자신의 훅과 함께 쓰는 방법(`core.hooksPath` 점유) +- [ ] 설정 키 · 파일명의 옛 이름(`gitformat.*`) 정리 +- [ ] CI 트레일러 검사를 컨슈머에게 배포할지(decision-25) +- [ ] 비 Claude Code 도구의 usage 실측 채널(decision-17) + +## 원천 + +- backlog draft +- decision-17 · decision-25 +- 최상위 4.03 diff --git "a/design/5-\352\265\254\355\230\204-\352\262\260\352\263\274-\353\263\264\352\263\240\354\204\234/01-\354\202\260\354\266\234\353\254\274.md" "b/design/5-\352\265\254\355\230\204-\352\262\260\352\263\274-\353\263\264\352\263\240\354\204\234/01-\354\202\260\354\266\234\353\254\274.md" new file mode 100644 index 0000000..a4e1d9e --- /dev/null +++ "b/design/5-\352\265\254\355\230\204-\352\262\260\352\263\274-\353\263\264\352\263\240\354\204\234/01-\354\202\260\354\266\234\353\254\274.md" @@ -0,0 +1,16 @@ +# 5.01 산출물 + +> 작성 전 + +구현으로 만든 것 — 코드 · 문서 · 배포물과 그 위치. + +## 담을 내용 + +- [ ] 훅 · 설정 · 설치 스크립트 +- [ ] 문서 산출물 +- [ ] 커밋 · PR 링크 + +## 원천 + +- backlog 태스크 final summary +- git log · PR diff --git "a/design/5-\352\265\254\355\230\204-\352\262\260\352\263\274-\353\263\264\352\263\240\354\204\234/02-\354\210\230\354\232\251-\352\270\260\354\244\200-\355\214\220\354\240\225.md" "b/design/5-\352\265\254\355\230\204-\352\262\260\352\263\274-\353\263\264\352\263\240\354\204\234/02-\354\210\230\354\232\251-\352\270\260\354\244\200-\355\214\220\354\240\225.md" new file mode 100644 index 0000000..532da33 --- /dev/null +++ "b/design/5-\352\265\254\355\230\204-\352\262\260\352\263\274-\353\263\264\352\263\240\354\204\234/02-\354\210\230\354\232\251-\352\270\260\354\244\200-\355\214\220\354\240\225.md" @@ -0,0 +1,15 @@ +# 5.02 수용 기준 판정 + +> 작성 전 + +수용 기준마다 통과 · 실패와 그 증거. + +## 담을 내용 + +- [ ] 수용 기준별 판정 +- [ ] 증거 — 테스트 로그 · 실측 커밋의 트레일러 + +## 원천 + +- 4.02 수용 기준과 검증 +- 테스트 로그 diff --git "a/design/5-\352\265\254\355\230\204-\352\262\260\352\263\274-\353\263\264\352\263\240\354\204\234/03-\352\263\204\355\232\215-\353\214\200\353\271\204-\355\216\270\354\260\250.md" "b/design/5-\352\265\254\355\230\204-\352\262\260\352\263\274-\353\263\264\352\263\240\354\204\234/03-\352\263\204\355\232\215-\353\214\200\353\271\204-\355\216\270\354\260\250.md" new file mode 100644 index 0000000..9f6b7a7 --- /dev/null +++ "b/design/5-\352\265\254\355\230\204-\352\262\260\352\263\274-\353\263\264\352\263\240\354\204\234/03-\352\263\204\355\232\215-\353\214\200\353\271\204-\355\216\270\354\260\250.md" @@ -0,0 +1,14 @@ +# 5.03 계획 대비 편차 + +> 작성 전 + +계획과 다르게 구현한 것과 그 이유. + +## 담을 내용 + +- [ ] 편차 항목과 연결된 설계 결정(3.05) + +## 원천 + +- 4.01 마일스톤과 작업 분해 +- backlog 태스크 · decision diff --git "a/design/5-\352\265\254\355\230\204-\352\262\260\352\263\274-\353\263\264\352\263\240\354\204\234/04-\355\233\204\354\206\215-\352\263\274\354\240\234.md" "b/design/5-\352\265\254\355\230\204-\352\262\260\352\263\274-\353\263\264\352\263\240\354\204\234/04-\355\233\204\354\206\215-\352\263\274\354\240\234.md" new file mode 100644 index 0000000..fcf59cd --- /dev/null +++ "b/design/5-\352\265\254\355\230\204-\352\262\260\352\263\274-\353\263\264\352\263\240\354\204\234/04-\355\233\204\354\206\215-\352\263\274\354\240\234.md" @@ -0,0 +1,16 @@ +# 5.04 후속 과제 + +> 작성 전 + +남은 일과 다음 반복으로 넘기는 것. + +## 담을 내용 + +- [ ] 실패하거나 보류한 수용 기준(5.02) +- [ ] 남은 미결 사항(4.03) +- [ ] 다음 반복의 backlog 태스크 + +## 원천 + +- 5.02 수용 기준 판정 +- backlog 태스크 · draft diff --git a/design/README.md b/design/README.md new file mode 100644 index 0000000..55f62b1 --- /dev/null +++ b/design/README.md @@ -0,0 +1,77 @@ +# coding-agent-git-commit-tool 설계 문서 + +코딩 에이전트의 git 커밋을 검사하고, 누가 · 어떤 모델로 · 무슨 태스크로 · 얼마를 들여 만들었는지 커밋 +트레일러로 남기는 컴포넌트의 설계 문서. Sigkill Foundry의 계측 컴포넌트이고(최상위 3.03 §3), 스펙 주도 +개발(SDD)을 따른다. 규범 용어 · 요구사항 ID · 추적 규칙은 [0. 문서 정보](./00-문서-정보.md)에 있다. + +**원본은 이 디렉토리의 `**/*.md` 하나다.** 고칠 때는 이 파일들을 직접 고치고(Claude Code · GitHub 편집기) 커밋 · PR로 남긴다. + +## 문서 흐름 + +| 문서 | 답하는 질문 | +| --- | --- | +| 1. 프로젝트 제안서 | 왜 만들고, 무엇을 어디까지 하는가 | +| 2. 요구사항 명세서 | 무엇을 만족해야 하는가 | +| 3. 아키텍처 설계서 | 어떻게 구성되고 동작하는가 | +| 4. 구현 계획서 | 어떤 순서로 만들고, 무엇으로 판정하는가 | +| 5. 구현 결과 보고서 | 무엇을 만들었고, 판정은 어떻게 나왔는가 | + +## 목차 + +### 공통 + +| 장 | 제목 | 상태 | +| --- | --- | --- | +| 0 | [문서 정보](./00-문서-정보.md) | 초안 | + +### 1. 프로젝트 제안서 + +| 장 | 제목 | 상태 | +| --- | --- | --- | +| 1.01 | [배경과 문제](./1-제안서/01-배경과-문제.md) | 초안 | +| 1.02 | [원칙](./1-제안서/02-원칙.md) | 초안 | +| 1.03 | [목표와 범위](./1-제안서/03-목표와-범위.md) | 작성 전 | +| 1.04 | [기대 효과와 리스크](./1-제안서/04-기대-효과와-리스크.md) | 작성 전 | + +### 2. 요구사항 명세서 + +| 장 | 제목 | 상태 | +| --- | --- | --- | +| 2.01 | [이해관계자와 운영 환경](./2-요구사항-명세서/01-이해관계자와-운영-환경.md) | 작성 전 | +| 2.02 | [도메인 모델](./2-요구사항-명세서/02-도메인-모델.md) | 작성 전 | +| 2.03 | [기능 요구사항](./2-요구사항-명세서/03-기능-요구사항.md) | 작성 전 | +| 2.04 | [품질 요구사항](./2-요구사항-명세서/04-품질-요구사항.md) | 작성 전 | + +### 3. 아키텍처 설계서 + +| 장 | 제목 | 상태 | +| --- | --- | --- | +| 3.01 | [시스템 구성](./3-아키텍처-설계서/01-시스템-구성.md) | 초안 | +| 3.02 | [동작 명세](./3-아키텍처-설계서/02-동작-명세.md) | 작성 전 | +| 3.03 | [컴포넌트 명세](./3-아키텍처-설계서/03-컴포넌트-명세.md) | 작성 전 | +| 3.04 | [인터페이스 계약](./3-아키텍처-설계서/04-인터페이스-계약.md) | 작성 전 | +| 3.05 | [설계 결정](./3-아키텍처-설계서/05-설계-결정.md) | 작성 전 | + +### 4. 구현 계획서 + +| 장 | 제목 | 상태 | +| --- | --- | --- | +| 4.01 | [마일스톤과 작업 분해](./4-구현-계획서/01-마일스톤과-작업-분해.md) | 작성 전 | +| 4.02 | [수용 기준과 검증](./4-구현-계획서/02-수용-기준과-검증.md) | 작성 전 | +| 4.03 | [미결 사항](./4-구현-계획서/03-미결-사항.md) | 작성 전 | + +### 5. 구현 결과 보고서 + +| 장 | 제목 | 상태 | +| --- | --- | --- | +| 5.01 | [산출물](./5-구현-결과-보고서/01-산출물.md) | 작성 전 | +| 5.02 | [수용 기준 판정](./5-구현-결과-보고서/02-수용-기준-판정.md) | 작성 전 | +| 5.03 | [계획 대비 편차](./5-구현-결과-보고서/03-계획-대비-편차.md) | 작성 전 | +| 5.04 | [후속 과제](./5-구현-결과-보고서/04-후속-과제.md) | 작성 전 | + +### 부록 + +| 장 | 제목 | 상태 | +| --- | --- | --- | +| 부록 | [참고 문헌](./부록-참고-문헌.md) | 작성 전 | +| 부록 | [용어집](./부록-용어집.md) | 작성 전 | diff --git a/design/build-diagrams.py b/design/build-diagrams.py new file mode 100755 index 0000000..6faa509 --- /dev/null +++ b/design/build-diagrams.py @@ -0,0 +1,169 @@ +#!/usr/bin/env python3 +"""design/diagrams/*.dot (·.mmd) → design/diagrams/<이름>.svg 독립 SVG 렌더. + +표기 원칙 — DOT 우선: + 그래프 · 트리 · DAG · 플로우차트 · 파이프라인 · 클래스 다이어그램 · 경계 표현은 전부 + Graphviz DOT(.dot)으로 쓴다. DOT에 대응물이 없는 것(시퀀스 다이어그램 등)만 mermaid(.mmd)로 + 쓴다. 이유는 레이아웃 품질(특히 의존성 DAG)과 소스 분리다. + +원본과 생성물: + 원본은 design/diagrams/ 안의 .dot(그리고 시퀀스 하나는 .mmd)이다. 같은 디렉토리의 .svg는 + 이 스크립트가 만드는 생성물이므로 직접 고치지 않는다. 그림을 고치려면 .dot/.mmd를 고치고 + 이 스크립트를 다시 돌린다. + +색 — 라이트/다크 대응: + 색은 .dot(그리고 mermaid-theme.json)에 센티넬 hex(#f00001 …)로 적는다. 이 스크립트가 + 센티넬을 CSS 변수(var(--diagram-*))로 치환하고, 그 변수를 SVG 안의 ") + + +def tokenize(svg: str) -> str: + for table in (TOKENS, MERMAID_TOKENS): + for sentinel, var in table.items(): + svg = svg.replace(sentinel, var).replace(sentinel.upper(), var) + return svg + + +def finish(svg: str, name: str, mermaid: bool) -> str: + """정리 · 토큰화 · 테마 + +commit_flow + +start + +git commit -m / -F +(--no-verify 포함) + + +pcm + +prepare-commit-msg +재생 · 병합 면제 → 에디터 경로 거부 +→ 검증 마커 기록 + + +start->pcm + + + + +replay + +재생 · 병합 커밋 +(MERGE_HEAD · CHERRY_PICK_HEAD · +REBASE_HEAD · REVERT_HEAD) +아무것도 하지 않음 + + +pcm->replay + + +재생 · 병합 + + +bypass + +--no-verify? + + +pcm->bypass + + +통과 + + +reject + +커밋 거부 (exit 1) +검증 마커 삭제 + + +pcm->reject + + +에디터 경로 + + +cm + +commit-msg +제목 형식 · 길이 · 빈 줄 · Fixes 해시 +· 브랜치 Task-Id · AI-Model 게이트 + + +bypass->cm + + +아니오 + + +create + +커밋 객체 생성 (git) + + +bypass->create + + +예 — commit-msg 건너뜀 + + +cm->reject + + +위반 + + +cm->create + + +통과 + + +pc + +post-commit +마커 유무로 우회 판정 → 트레일러 계산 +(Task-Id · AI-* · Tokens-Used · Tool-Calls · +Hooks-Commit · Signed-off-by · Verify-Bypassed) + + +create->pc + + + + +amend + +git interpret-trailers +→ git commit --amend --no-verify +(_GITFORMAT_AMEND_GUARD=1) + + +pc->amend + + +붙일 트레일러 있음 + + +done + +최종 커밋 +(트레일러 포함) + + +pc->done + + +없음 + + +amend->pcm + + +재진입 — 가드로 즉시 종료 + + +amend->done + + + + + diff --git a/design/diagrams/_common.gv b/design/diagrams/_common.gv new file mode 100644 index 0000000..fb19796 --- /dev/null +++ b/design/diagrams/_common.gv @@ -0,0 +1,6 @@ +// 이 파일은 참고용 주석이다. 실제 공통 설정은 각 .dot 상단에 복제돼 있다. +// 색은 전부 센티넬 hex로 쓰고, build-diagrams.py가 CSS 변수로 치환한다. +// #f00001 ink #f00002 ink-muted #f00003 line #f00004 surface +// #f00005 surface-2 #f00006 accent #f00007 accent-bg #f00008 accent-ink +// #f00009 role-impl #f0000a role-impl-bg #f0000b role-review #f0000c role-review-bg +// #f0000d role-verify #f0000e role-verify-bg #f0000f role-logger #f00010 role-logger-bg diff --git "a/design/\353\266\200\353\241\235-\354\232\251\354\226\264\354\247\221.md" "b/design/\353\266\200\353\241\235-\354\232\251\354\226\264\354\247\221.md" new file mode 100644 index 0000000..54ae1ac --- /dev/null +++ "b/design/\353\266\200\353\241\235-\354\232\251\354\226\264\354\247\221.md" @@ -0,0 +1,16 @@ +# 부록 · 용어집 + +> 작성 전 + +설계 문서 전반에서 쓰는 용어. + +## 담을 내용 + +- [ ] 트레일러 · 검증 마커 · 귀속 · 재생 커밋 +- [ ] trail · provenance · attestation · audit(doc-21) +- [ ] 옛 이름 — git-format · git-logbook · git-trail + +## 원천 + +- doc-14 용어 정리 +- doc-21 개념 지도 diff --git "a/design/\353\266\200\353\241\235-\354\260\270\352\263\240-\353\254\270\355\227\214.md" "b/design/\353\266\200\353\241\235-\354\260\270\352\263\240-\353\254\270\355\227\214.md" new file mode 100644 index 0000000..0dc6c44 --- /dev/null +++ "b/design/\353\266\200\353\241\235-\354\260\270\352\263\240-\353\254\270\355\227\214.md" @@ -0,0 +1,18 @@ +# 부록 · 참고 문헌 + +> 작성 전 + +설계 문서가 인용하는 외부 문헌과 검증 상태. + +## 담을 내용 + +- [ ] githooks(5) · git-interpret-trailers(1) · git-config(1) +- [ ] Linux 커널 메인테이너 문서(이력 재작성) +- [ ] provenance 연구(arXiv 2607.02774) +- [ ] Gerrit `Change-Id` +- [ ] 외부 문서는 저장소에 옮겨 담지 않는다(decision-14) + +## 원천 + +- decision-24 외부 근거 +- doc-15 · doc-19