From db54c0c1426bb554f1aa9d55a32e99b0389b39c8 Mon Sep 17 00:00:00 2001 From: cpu-once <231006716+cpu-once@users.noreply.github.com> Date: Sat, 3 Oct 2026 12:44:07 +0900 Subject: [PATCH 1/6] =?UTF-8?q?[docs][backlog]=20decision-26=20git-trail?= =?UTF-8?q?=20=EA=B0=9C=EB=AA=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-Id: GF-140 AI-Tool: claude-code AI-Tool-Version: 2.1.285 Co-Authored-By: Claude AI-Model: claude-opus-5-5 Tokens-Used: 1685983 Tool-Calls: 7 Hooks-Commit: 702126c Signed-off-by: cpu-once <231006716+cpu-once@users.noreply.github.com> --- ...0\355\225\255-\353\214\200\354\262\264.md" | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 "backlog/decisions/decision-26 - \354\235\264\353\246\204\354\235\204-git-trail\353\241\234-\353\260\224\352\276\274\353\213\244-decision-25\354\235\230-\354\235\264\353\246\204-\354\241\260\355\225\255-\353\214\200\354\262\264.md" diff --git "a/backlog/decisions/decision-26 - \354\235\264\353\246\204\354\235\204-git-trail\353\241\234-\353\260\224\352\276\274\353\213\244-decision-25\354\235\230-\354\235\264\353\246\204-\354\241\260\355\225\255-\353\214\200\354\262\264.md" "b/backlog/decisions/decision-26 - \354\235\264\353\246\204\354\235\204-git-trail\353\241\234-\353\260\224\352\276\274\353\213\244-decision-25\354\235\230-\354\235\264\353\246\204-\354\241\260\355\225\255-\353\214\200\354\262\264.md" new file mode 100644 index 0000000..d74cf13 --- /dev/null +++ "b/backlog/decisions/decision-26 - \354\235\264\353\246\204\354\235\204-git-trail\353\241\234-\353\260\224\352\276\274\353\213\244-decision-25\354\235\230-\354\235\264\353\246\204-\354\241\260\355\225\255-\353\214\200\354\262\264.md" @@ -0,0 +1,40 @@ +--- +id: decision-26 +title: 이름을 git-trail로 바꾼다 (decision-25의 이름 조항 대체) +date: '2026-10-03 03:43' +status: accepted +--- +## Context + +decision-25가 목적을 세 기둥(형식·출처 기록·이력 불변)으로 정하면서 이름을 git-logbook으로 +바꿨다. 같은 날(2026-10-03) 유저가 이름을 다시 정했다 — 개발자들이 이런 도구를 부를 때 실제로 +쓰는 말에서 고르기로 했다. + +| 용어 | 뜻 | 이 프로젝트에서 | +| --- | --- | --- | +| trail (audit trail) | 누가·언제·무엇을 했는지 고칠 수 없게 남긴 기록 | 세 기둥 전체 | +| provenance | 결과물이 어디서 어떻게 만들어졌는지 (SLSA 용어) | 출처 기록 트레일러 | +| attestation | provenance를 기계가 검증할 수 있게 남긴 증명 (in-toto, Sigstore) | 아직 서명 없음 | +| audit | 기록을 사후에 대조·검사하는 일 | Fixes 검증, CI 검사 계획 | + +유저 판단: 저장소 이름은 하나로 짧게 하고, 네 용어는 설명과 문서에 모두 적는다. + +## Decision + +**이름을 git-trail로 바꾼다.** audit trail에서 왔고, 이 도구가 남기는 git trailer와 소리도 +겹친다. decision-25의 이름 조항(git-logbook)만 대체하고, 목적 세 기둥과 decision-11·12·19 +대체 조항은 그대로 유효하다. + +네 용어가 이 저장소의 어느 훅·함수·파일·원격 설정에 구현돼 있는지, 어디가 비어 있는지는 +개념 지도 문서(GF-140)에 적는다. 이름에 용어를 다 넣는 대신 문서로 연결한다. + +GitHub 저장소는 다시 rename한다(coralstay/git-logbook → coralstay/git-trail). 예전 이름 +두 개(git-format, git-logbook)는 GitHub이 리다이렉트한다. + +## Consequences + +- 표시 이름만 바뀐다. 설정 키·파일명(`gitformat.*`)은 GF-133, 태스크 접두어 GF와 지난 기록 + 본문은 decision-25와 같은 이유로 그대로 둔다. decision-25 본문의 "git-logbook"도 당시 + 기록이라 고치지 않는다. +- attestation이라는 말을 쓰는 이상, 지금은 서명된 증명이 없다는 점을 개념 지도에 분명히 + 적어야 한다. 기대와 실제가 어긋나지 않게 하기 위해서다. From 45317e483b7261ce4b3d8c54d0b3672c6b73302a Mon Sep 17 00:00:00 2001 From: cpu-once <231006716+cpu-once@users.noreply.github.com> Date: Sat, 3 Oct 2026 12:45:30 +0900 Subject: [PATCH 2/6] =?UTF-8?q?[chore]=20=ED=91=9C=EC=8B=9C=20=EC=9D=B4?= =?UTF-8?q?=EB=A6=84=EC=9D=84=20git-trail=EB=A1=9C=20=EB=B0=94=EA=BE=BC?= =?UTF-8?q?=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-Id: GF-140 AI-Tool: claude-code AI-Tool-Version: 2.1.285 Co-Authored-By: Claude AI-Model: claude-opus-5-5 Tokens-Used: 857474 Tool-Calls: 3 Hooks-Commit: 516b12d Signed-off-by: cpu-once <231006716+cpu-once@users.noreply.github.com> --- .github/workflows/test.yml | 2 +- .gitmessage | 2 +- README.md | 22 ++++++++--------- backlog/config.yml | 2 +- ...0-\352\260\200\354\235\264\353\223\234.md" | 20 ++++++++-------- ...5\354\206\214-\354\204\244\354\240\225.md" | 12 +++++----- ...5\354\206\214-\352\265\254\354\241\260.md" | 4 ++-- hooks/gitformat.conf | 4 ++-- hooks/post-commit | 4 ++-- hooks/prepare-commit-msg | 4 ++-- install.sh | 24 +++++++++---------- tests/test_end_to_end_commit.py | 4 ++-- tests/test_install_script.py | 2 +- 13 files changed, 53 insertions(+), 53 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7174cbb..0d5e099 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,6 +1,6 @@ name: test -# git-logbook 자체의 개발용 CI(GF-21, decision-8). 컨슈머에 배포하는 verify.yml과 +# git-trail 자체의 개발용 CI(GF-21, decision-8). 컨슈머에 배포하는 verify.yml과 # 달리 이건 이 저장소 자신의 훅 코드 품질만 검사한다 — 훅도 테스트도 Python 표준 # 라이브러리만 쓰므로(decision-16, decision-21) 테스트 프레임워크를 따로 설치하는 # 스텝이 없다. 아래 ruff 설치는 이 저장소 자신의 Python 코드를 검사하는 개발 도구다 — diff --git a/.gitmessage b/.gitmessage index 615556c..01ad6eb 100644 --- a/.gitmessage +++ b/.gitmessage @@ -39,4 +39,4 @@ # # ── 이 템플릿 사용법 ── # git config commit.template .gitmessage -# (git-logbook install.sh를 사용하면 자동으로 설정됩니다 — GF-8 참고) +# (git-trail install.sh를 사용하면 자동으로 설정됩니다 — GF-8 참고) diff --git a/README.md b/README.md index 9ccdf5e..cee9462 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@
-# 📒 git-logbook +# 👣 git-trail -**git 이력을 작업 로그로 만드는 도구 — 커밋 형식 · 출처 기록 · 이력 불변** +**git 이력을 감사 추적(audit trail)으로 만드는 도구 — 커밋 형식 · 출처 기록(provenance) · 이력 불변** [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE) [![Hooks: Python 3](https://img.shields.io/badge/hooks-Python%203-3776ab.svg)](./hooks/) @@ -66,7 +66,7 @@ Signed-off-by: cpu-once <231006716+cpu-once@users.noreply.github.com> | `Tokens-Used` | 이 커밋에 든 토큰. 작업 비용을 이력에서 읽게 합니다 | `post-commit` · 직전 커밋 이후 세션 구간의 델타(누적 아님) | 서버 발급 사실(Claude Code 한정), **측정 방법론은 실험 단계** | | `Tool-Calls` | 같은 구간의 도구 호출 수 | `post-commit` · `tool_use` 블록 개수 | 위와 동일 | | `Co-Authored-By` | 공동저자 귀속 | `post-commit` · `AI-Tool`이 `claude-code`일 때만 | 자동 | -| `Hooks-Commit` | 이 커밋을 검사한 git-logbook 자체의 버전. 훅에 버그가 있었을 때 어느 커밋들이 그 훅을 거쳤는지 역추적합니다 | `post-commit` · 훅 클론의 `rev-parse --short HEAD` | 완전 자동, 모든 커밋 | +| `Hooks-Commit` | 이 커밋을 검사한 git-trail 자체의 버전. 훅에 버그가 있었을 때 어느 커밋들이 그 훅을 거쳤는지 역추적합니다 | `post-commit` · 훅 클론의 `rev-parse --short HEAD` | 완전 자동, 모든 커밋 | | `Signed-off-by` | 커미터 정보(DCO 관례) | `post-commit` · `git log -1 --format='%cn <%ce>'` | 완전 자동, 모든 커밋 | | `Verify-Bypassed` | `--no-verify`로 검사를 건너뛴 사실. 우회를 막지는 못하니 대신 기록합니다(decision-3) | `post-commit` · `prepare-commit-msg`가 남긴 검증 마커의 **부재** | 자동 | | `Fixes` | 이 버그를 만든 커밋 | **사람이 씀**(원인 커밋을 아는 경우만) · `commit-msg`가 해시 실재를 검증 | 검증됨 | @@ -78,10 +78,10 @@ Signed-off-by: cpu-once <231006716+cpu-once@users.noreply.github.com> ## 🪝 커밋 한 번에 훅이 도는 순서 > 공식 문서: [githooks(5)](https://git-scm.com/docs/githooks). git이 제공하는 훅은 28개이고, -> git-logbook의 훅은 지금 커밋 단계만 다룹니다. push된 이력의 보호는 원격 설정이 맡습니다 +> git-trail의 훅은 지금 커밋 단계만 다룹니다. push된 이력의 보호는 원격 설정이 맡습니다 > ([다루는 범위](#-다루는-범위)). -| 순서 | 훅 | 커밋 객체가 있는가 | `--no-verify`로 건너뛰나 | git-logbook이 하는 일 | +| 순서 | 훅 | 커밋 객체가 있는가 | `--no-verify`로 건너뛰나 | git-trail이 하는 일 | | --- | --- | --- | --- | --- | | 1 | [`prepare-commit-msg`](hooks/prepare-commit-msg) | 아직 없음 | **건너뛸 수 없음** | 재생·병합 커밋 면제, 에디터 경로 거부, 검증 마커 기록 | | 2 | (에디터) | 아직 없음 | — | 사람이 메시지를 씁니다. 훅보다 **뒤**라서 1번은 최종 메시지를 볼 수 없습니다 | @@ -111,10 +111,10 @@ git 이력을 작업 로그로 만드는 데 필요한 세 가지입니다(decis **언어별 lint는 다루지 않습니다**(decision-23) — 세 기둥 어디에도 속하지 않고, 컨슈머에게 npm/ruff/clang-format 같은 도구를 전제하게 만듭니다. 언어 검사가 필요하면 CI에서 돌리시기 -바랍니다. git-logbook이 `core.hooksPath`를 점유하므로 그 저장소의 `.git/hooks/*`는 +바랍니다. git-trail이 `core.hooksPath`를 점유하므로 그 저장소의 `.git/hooks/*`는 무시되고, 자기 훅과 함께 쓰는 방법은 아직 정하지 않았습니다. -> 이름을 git-format에서 git-logbook으로 바꿨습니다(decision-25). 설정 키와 파일명 +> 이름은 git-format → git-logbook → git-trail 순으로 바뀌었습니다(decision-25, decision-26). 설정 키와 파일명 > (`gitformat.*`, `gitformat.conf`, `.gitformat-*`)은 기존 설치를 깨지 않도록 아직 예전 이름입니다(GF-133). 구성은 Python 훅 + 설정 파일 하나(`hooks/gitformat.conf`) + POSIX sh 설치 @@ -151,17 +151,17 @@ python3는 이런 환경에서 보이지 않을 수 있습니다. ### 기존 저장소에 적용하는 방법입니다 ```sh -git clone https://github.com/coralstay/git-logbook.git ~/git-logbook # 원하는 위치에 한 번만 클론 +git clone https://github.com/coralstay/git-trail.git ~/git-trail # 원하는 위치에 한 번만 클론 cd ~/my-project -~/git-logbook/install.sh +~/git-trail/install.sh ``` -대상 디렉터리를 인자로 주셔도 됩니다: `~/git-logbook/install.sh ~/my-project`. +대상 디렉터리를 인자로 주셔도 됩니다: `~/git-trail/install.sh ~/my-project`. ### 앞으로 만들 모든 새 저장소에 자동 적용하는 방법입니다 ```sh -~/git-logbook/install.sh --global +~/git-trail/install.sh --global ``` `git init`/`git clone`을 실행할 때마다 훅과 커밋 템플릿이 자동으로 심어집니다 diff --git a/backlog/config.yml b/backlog/config.yml index 84ee07a..d496723 100644 --- a/backlog/config.yml +++ b/backlog/config.yml @@ -1,4 +1,4 @@ -project_name: "git-logbook" +project_name: "git-trail" default_status: "To Do" statuses: ["To Do", "In Progress", "Done"] labels: [] diff --git "a/backlog/docs/doc-1 - \354\204\244\354\271\230-\352\260\200\354\235\264\353\223\234.md" "b/backlog/docs/doc-1 - \354\204\244\354\271\230-\352\260\200\354\235\264\353\223\234.md" index d4a6147..7a763b1 100644 --- "a/backlog/docs/doc-1 - \354\204\244\354\271\230-\352\260\200\354\235\264\353\223\234.md" +++ "b/backlog/docs/doc-1 - \354\204\244\354\271\230-\352\260\200\354\235\264\353\223\234.md" @@ -3,24 +3,24 @@ id: doc-1 title: 설치 가이드 type: guide created_date: '2026-09-19 05:27' -updated_date: '2026-10-03 03:40' +updated_date: '2026-10-03 03:44' --- ## 🚀 설치 ### 기존 저장소에 적용 ```sh -git clone https://github.com/coralstay/git-logbook.git ~/git-logbook # 원하는 위치에 한 번만 클론 +git clone https://github.com/coralstay/git-trail.git ~/git-trail # 원하는 위치에 한 번만 클론 cd ~/my-project -~/git-logbook/install.sh +~/git-trail/install.sh ``` -대상 디렉터리를 인자로 줘도 됩니다: `~/git-logbook/install.sh ~/my-project`. +대상 디렉터리를 인자로 줘도 됩니다: `~/git-trail/install.sh ~/my-project`. ### 앞으로 만들 모든 새 저장소에 자동 적용 ```sh -~/git-logbook/install.sh --global +~/git-trail/install.sh --global ``` `git init`/`git clone`을 실행할 때마다 훅과 커밋 템플릿이 자동으로 심어집니다 @@ -35,10 +35,10 @@ cd ~/my-project | 대상 | 명령 | 효과 | | ----------------- | -------------------------------------------------------------- | ------------------------------------------ | -| 로컬(대상 저장소) | `git config core.hooksPath /hooks` | `.git/hooks/`의 기존 로컬 훅을 완전히 대체 | -| 로컬(대상 저장소) | `git config commit.template /.gitmessage` | 커밋 에디터에 스켈레톤 표시 | -| 전역(`--global`) | `git config --global init.templateDir /template` | 이후 모든 신규 저장소에 자동 적용 | -| 전역(`--global`) | `git config --global commit.template /.gitmessage` | 위와 동일, 전역 기본값 | +| 로컬(대상 저장소) | `git config core.hooksPath /hooks` | `.git/hooks/`의 기존 로컬 훅을 완전히 대체 | +| 로컬(대상 저장소) | `git config commit.template /.gitmessage` | 커밋 에디터에 스켈레톤 표시 | +| 전역(`--global`) | `git config --global init.templateDir /template` | 이후 모든 신규 저장소에 자동 적용 | +| 전역(`--global`) | `git config --global commit.template /.gitmessage` | 위와 동일, 전역 기본값 | **실행 중 새로 생기는 파일** @@ -46,7 +46,7 @@ cd ~/my-project | ------------------------- | ---------------------------------------- | -------------------------------------------------- | ----------------------------------------------------------------------- | | `.gitformat-verified` | `<대상 저장소>/.git/` | `pre-commit` 통과 시 생성, `post-commit`이 곧 삭제 | 커밋 사이에 남지 않는 임시 마커 | | `.gitformat-token-cursor` | `<대상 저장소>/.git/` | Claude Code 실측 성공 시에만 `post-commit`이 갱신(`unavailable`일 때는 갱신 안 함) | `Tokens-Used`/`Tool-Calls` 델타 계산용 커서(`<세션 ID> <줄 수>`, 세션이 바뀌면 0줄부터, GF-139), 커밋 간 유지됨 | -| `template/hooks/*` | 이 git-logbook 클론 자신의 `template/` 안 | `install.sh --global` 실행 시 | 클론 위치를 가리키는 심볼릭 링크, 커밋 안 됨(`.gitignore`) | +| `template/hooks/*` | 이 git-trail 클론 자신의 `template/` 안 | `install.sh --global` 실행 시 | 클론 위치를 가리키는 심볼릭 링크, 커밋 안 됨(`.gitignore`) | **커밋 자체가 바뀌는 경우**: `post-commit`이 조건에 따라 `git commit --amend`로 방금 만든 커밋의 footer에 트레일러를 추가합니다(README의 "훅 생애주기" 섹션과 diff --git "a/backlog/docs/doc-20 - GitHub-\354\233\220\352\262\251-\352\260\225\354\240\234-\354\204\244\354\240\225-\352\264\200\353\246\254-\342\200\224-rulesets\354\231\200-\354\240\200\354\236\245\354\206\214-\354\204\244\354\240\225.md" "b/backlog/docs/doc-20 - GitHub-\354\233\220\352\262\251-\352\260\225\354\240\234-\354\204\244\354\240\225-\352\264\200\353\246\254-\342\200\224-rulesets\354\231\200-\354\240\200\354\236\245\354\206\214-\354\204\244\354\240\225.md" index 18aea05..a94e123 100644 --- "a/backlog/docs/doc-20 - GitHub-\354\233\220\352\262\251-\352\260\225\354\240\234-\354\204\244\354\240\225-\352\264\200\353\246\254-\342\200\224-rulesets\354\231\200-\354\240\200\354\236\245\354\206\214-\354\204\244\354\240\225.md" +++ "b/backlog/docs/doc-20 - GitHub-\354\233\220\352\262\251-\352\260\225\354\240\234-\354\204\244\354\240\225-\352\264\200\353\246\254-\342\200\224-rulesets\354\231\200-\354\240\200\354\236\245\354\206\214-\354\204\244\354\240\225.md" @@ -3,7 +3,7 @@ id: doc-20 title: GitHub 원격 강제 설정 관리 — rulesets와 저장소 설정 type: guide created_date: '2026-09-26 12:54' -updated_date: '2026-10-03 03:16' +updated_date: '2026-10-03 03:44' --- # GitHub 원격 강제 설정 관리 @@ -66,15 +66,15 @@ decision-24(append-only)는 **로컬 훅만으로 달성할 수 없다.** `--no- ```sh # 병합 방식 -gh api repos/coralstay/git-logbook \ +gh api repos/coralstay/git-trail \ --jq '{allow_merge_commit, allow_rebase_merge, allow_squash_merge}' # ruleset 목록 -gh api repos/coralstay/git-logbook/rulesets \ +gh api repos/coralstay/git-trail/rulesets \ --jq '.[] | "\(.name) | \(.enforcement) | id=\(.id)"' # 특정 ruleset 상세 -gh api repos/coralstay/git-logbook/rulesets/23313297 \ +gh api repos/coralstay/git-trail/rulesets/23313297 \ --jq '{name, enforcement, include: .conditions.ref_name.include, rules: [.rules[].type]}' ``` @@ -82,11 +82,11 @@ gh api repos/coralstay/git-logbook/rulesets/23313297 \ ```sh # 병합 방식 변경 -gh api -X PATCH repos/coralstay/git-logbook \ +gh api -X PATCH repos/coralstay/git-trail \ -f allow_merge_commit=true -F allow_rebase_merge=false -F allow_squash_merge=false # ruleset 갱신 (PUT은 전체 교체다 - 기존 규칙을 빠뜨리면 사라진다) -gh api -X PUT repos/coralstay/git-logbook/rulesets/23313297 --input ruleset.json +gh api -X PUT repos/coralstay/git-trail/rulesets/23313297 --input ruleset.json ``` ## 함정 diff --git "a/backlog/docs/doc-5 - \354\240\200\354\236\245\354\206\214-\352\265\254\354\241\260.md" "b/backlog/docs/doc-5 - \354\240\200\354\236\245\354\206\214-\352\265\254\354\241\260.md" index 10d184d..9983468 100644 --- "a/backlog/docs/doc-5 - \354\240\200\354\236\245\354\206\214-\352\265\254\354\241\260.md" +++ "b/backlog/docs/doc-5 - \354\240\200\354\236\245\354\206\214-\352\265\254\354\241\260.md" @@ -3,12 +3,12 @@ id: doc-5 title: 저장소 구조 type: guide created_date: '2026-09-19 05:27' -updated_date: '2026-10-03 03:16' +updated_date: '2026-10-03 03:44' --- ## 📁 저장소 구조 ``` -git-logbook/ +git-trail/ ├── hooks/ # core.hooksPath가 가리키는 실제 훅(Python 3, decision-16) │ ├── readme.md # 훅 3개의 역할·생애주기와 python3 실행 요구사항 │ ├── prepare-commit-msg # 재생 커밋 면제, 에디터 경로 거부(decision-18) diff --git a/hooks/gitformat.conf b/hooks/gitformat.conf index fde85fc..0c2130d 100644 --- a/hooks/gitformat.conf +++ b/hooks/gitformat.conf @@ -1,9 +1,9 @@ -# git-logbook 내부 기본값 상수 파일(git config 포맷). 훅 3개(prepare-commit-msg, +# git-trail 내부 기본값 상수 파일(git config 포맷). 훅 3개(prepare-commit-msg, # commit-msg, post-commit)가 이 파일을 `git config --file`로 읽어 값을 가져온다 — # 각 훅의 실행 로직(함수/제어흐름)은 이 파일과 무관하게 파일마다 독립적으로 # 유지된다(공유하는 건 값뿐). # -# 이 파일은 git-logbook 자체의 "내부 기본값"이고, 컨슈머 저장소가 오버라이드하는 +# 이 파일은 git-trail 자체의 "내부 기본값"이고, 컨슈머 저장소가 오버라이드하는 # `git config gitformat.taskPrefix`/`gitformat.branchExempt`/`gitformat.aiModel` # (컨슈머 저장소의 로컬/전역 git config)과는 레이어가 다르다 — 오버라이드가 있으면 # 그 값이 우선하고, 없으면 여기 기본값을 쓴다. diff --git a/hooks/post-commit b/hooks/post-commit index 91e5530..6773514 100755 --- a/hooks/post-commit +++ b/hooks/post-commit @@ -496,9 +496,9 @@ def trailer_tokens_used(): queue_trailer(TRAILER_TOOL_CALLS, calls_value) -# Hooks-Commit(decision-5): 이 커밋을 검증한 git-logbook 훅 코드 자체의 커밋 해시. +# Hooks-Commit(decision-5): 이 커밋을 검증한 git-trail 훅 코드 자체의 커밋 해시. # 수동 버전 문자열 대신 훅 스크립트가 위치한 저장소의 HEAD를 그대로 읽으므로 항상 -# 정확하다. hooks/가 git-logbook 클론 안에 있지 않으면(예: 훅만 복사해 배포한 경우) +# 정확하다. hooks/가 git-trail 클론 안에 있지 않으면(예: 훅만 복사해 배포한 경우) # 조회가 실패하는데, 그때는 커밋을 막지 않고 이 트레일러만 생략하는 게 의도된 # 동작이다 - 이 함수의 판단이 rev-parse 성공 여부에 달려 있으므로 예외가 아니라 # 종료 코드로 좁게 확인한다. diff --git a/hooks/prepare-commit-msg b/hooks/prepare-commit-msg index 424784c..8973b1c 100755 --- a/hooks/prepare-commit-msg +++ b/hooks/prepare-commit-msg @@ -5,7 +5,7 @@ # 옮겨온다. 그때까지 commit-msg/post-commit이 그대로 자기 일을 계속한다. # # GF-126에서 언어별 lint를 pre-commit에서 이 파일로 옮겼다가 GF-135에서 그 lint를 -# 통째로 지웠다 — 언어 lint는 git-logbook의 범위(형식·출처 기록·이력 불변, decision-25) +# 통째로 지웠다 — 언어 lint는 git-trail의 범위(형식·출처 기록·이력 불변, decision-25) # 어디에도 속하지 않는 기능이었다(decision-23). 남은 것은 검증마커 기록뿐이고 그 이유는 # write_verified_marker()의 주석에 있다. # @@ -18,7 +18,7 @@ # 에디터 경로 판정에 $1의 내용을 쓴다 — $2로 에디터를 판정할 수 없는 이유는 아래 # has_author_message()의 주석에 적었다. # -# template/(GF-9)로 설치된 저장소는 .git/hooks/prepare-commit-msg 자체가 git-logbook +# template/(GF-9)로 설치된 저장소는 .git/hooks/prepare-commit-msg 자체가 git-trail # 클론을 가리키는 심볼릭 링크다. 링크가 놓인 .git/hooks/가 아니라 링크가 가리키는 # 실제 위치를 기준으로 gitformat.conf를 찾아야 한다(GF-16) — realpath가 링크 체인을 # 따라간다. diff --git a/install.sh b/install.sh index d9344a0..3f0d604 100755 --- a/install.sh +++ b/install.sh @@ -1,10 +1,10 @@ #!/bin/sh -# git-logbook 설치 스크립트: 기존 저장소에 core.hooksPath와 commit.template을 설정하고 +# git-trail 설치 스크립트: 기존 저장소에 core.hooksPath와 commit.template을 설정하고 # (decision-2), 훅 실행에 필요한 python3 존재를 확인한다(decision-16). 이 스크립트가 -# 위치한 git-logbook 클론 자체를 훅 소스로 사용한다. +# 위치한 git-trail 클론 자체를 훅 소스로 사용한다. # # 사용법: -# /path/to/git-logbook/install.sh [target-repo-dir] +# /path/to/git-trail/install.sh [target-repo-dir] # target-repo-dir을 생략하면 현재 디렉터리를 대상으로 한다. set -eu @@ -30,7 +30,7 @@ fi # 훅은 Python으로 실행된다(decision-16). 여기서 막지 않으면 설정은 다 끝난 # 뒤 첫 커밋에서야 훅이 실행 실패로 깨져 원인을 찾기 어렵다. if ! command -v python3 >/dev/null 2>&1; then - echo "install.sh: python3을 찾을 수 없습니다 - git-logbook 훅은 Python으로 실행됩니다." >&2 + echo "install.sh: python3을 찾을 수 없습니다 - git-trail 훅은 Python으로 실행됩니다." >&2 exit 1 fi @@ -56,7 +56,7 @@ fi git -C "$TARGET" config core.hooksPath "$HOOKS_DIR" git -C "$TARGET" config commit.template "$GITMESSAGE" -echo "git-logbook: ${TARGET} 설정 완료" +echo "git-trail: ${TARGET} 설정 완료" echo " core.hooksPath = ${HOOKS_DIR}" echo " commit.template = ${GITMESSAGE}" @@ -88,10 +88,10 @@ case "$GLOBAL_MODE" in sync_template git config --global init.templateDir "$TEMPLATE_DIR" git config --global commit.template "$GITMESSAGE" - echo "git-logbook: 전역 init.templateDir/commit.template 설정 완료 (앞으로 만드는 새 저장소에 자동 적용)" + echo "git-trail: 전역 init.templateDir/commit.template 설정 완료 (앞으로 만드는 새 저장소에 자동 적용)" ;; no) - echo "git-logbook: 전역 설정은 건너뜁니다." + echo "git-trail: 전역 설정은 건너뜁니다." ;; ask) if [ -t 0 ]; then @@ -102,18 +102,18 @@ case "$GLOBAL_MODE" in sync_template git config --global init.templateDir "$TEMPLATE_DIR" git config --global commit.template "$GITMESSAGE" - echo "git-logbook: 전역 init.templateDir/commit.template 설정 완료." + echo "git-trail: 전역 init.templateDir/commit.template 설정 완료." ;; *) - echo "git-logbook: 전역 설정은 건너뜁니다. 나중에: ${SELF_DIR}/install.sh --global" + echo "git-trail: 전역 설정은 건너뜁니다. 나중에: ${SELF_DIR}/install.sh --global" ;; esac else - echo "git-logbook: 비대화형 환경이라 전역 설정은 건너뜁니다. --global로 자동 적용 가능." + echo "git-trail: 비대화형 환경이라 전역 설정은 건너뜁니다. --global로 자동 적용 가능." fi ;; esac -echo "git-logbook: 참고 - git push --no-verify는 로컬 훅으로 탐지할 수 없습니다." +echo "git-trail: 참고 - git push --no-verify는 로컬 훅으로 탐지할 수 없습니다." echo " push된 이력은 원격 설정이 지킵니다 - 대상 저장소의 원격(GitHub rulesets·병합 방식)을" -echo " 직접 구성하세요 (예시: git-logbook 저장소의 backlog doc-20)." +echo " 직접 구성하세요 (예시: git-trail 저장소의 backlog doc-20)." diff --git a/tests/test_end_to_end_commit.py b/tests/test_end_to_end_commit.py index 8688480..f49e3f2 100644 --- a/tests/test_end_to_end_commit.py +++ b/tests/test_end_to_end_commit.py @@ -26,7 +26,7 @@ def test_언어_도구가_거부할_파일도_커밋이_통과한다(self): """[GF-135] 언어별 검사가 제거돼 ruff가 거부할 파일도 커밋을 막지 않는다 GF-135 전에는 pyproject.toml이 있는 저장소의 `import os`(F401)가 - checks/python.py의 ruff에 걸려 커밋이 막혔다. 지금은 git-logbook이 언어 + checks/python.py의 ruff에 걸려 커밋이 막혔다. 지금은 git-trail이 언어 도구를 전혀 실행하지 않는다(decision-23) — 이 단언이 없으면 lint가 슬그머니 돌아와도 스위트가 똑같이 통과한다. """ @@ -63,7 +63,7 @@ def test_template_심볼릭_링크_설치에서도_훅이_자기_설정을_찾 new_repo = self.temp_dir() self.make_repo(path=new_repo, hooks_path=None, env=env) - # init.templateDir이 .git/hooks/*를 git-logbook 클론을 가리키는 심볼릭 링크로 + # init.templateDir이 .git/hooks/*를 git-trail 클론을 가리키는 심볼릭 링크로 # 채웠는지부터 확인한다 — 이게 아니면 GF-16 시나리오 자체가 재현 안 된다. linked_hook = new_repo / ".git" / "hooks" / "prepare-commit-msg" self.assertTrue( diff --git a/tests/test_install_script.py b/tests/test_install_script.py index e652708..1daf3b8 100644 --- a/tests/test_install_script.py +++ b/tests/test_install_script.py @@ -27,7 +27,7 @@ def setUp(self): self.run_cmd_ok(["git", "init", "-q"], cwd=self.repo) def fake_root(self): - """install.sh와 hooks/만 들어 있는 git-logbook 클론 사본. + """install.sh와 hooks/만 들어 있는 git-trail 클론 사본. 실제 hooks/ 파일을 지우는 테스트는 이 저장소 자신이 아니라 이 사본에서 한다. """ From 41b1818d9cde7167e89fdfaa82a09356ac3297d4 Mon Sep 17 00:00:00 2001 From: cpu-once <231006716+cpu-once@users.noreply.github.com> Date: Sat, 3 Oct 2026 12:46:56 +0900 Subject: [PATCH 3/6] =?UTF-8?q?[docs]=20=EA=B0=9C=EB=85=90=20=EC=A7=80?= =?UTF-8?q?=EB=8F=84=20doc-21=20=EC=B6=94=EA=B0=80=EC=99=80=20README=20?= =?UTF-8?q?=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-Id: GF-140 AI-Tool: claude-code AI-Tool-Version: 2.1.285 Co-Authored-By: Claude AI-Model: claude-opus-5-5 Tokens-Used: 1455775 Tool-Calls: 5 Hooks-Commit: 17469d5 Signed-off-by: cpu-once <231006716+cpu-once@users.noreply.github.com> --- README.md | 7 ++ ...4\353\217\274-\354\236\210\353\202\230.md" | 107 ++++++++++++++++++ 2 files changed, 114 insertions(+) create mode 100644 "backlog/docs/doc-21 - \352\260\234\353\205\220-\354\247\200\353\217\204-\342\200\224-trail\302\267provenance\302\267attestation\302\267audit\354\235\264-\354\226\264\353\224\224\354\227\220-\352\265\254\355\230\204\353\217\274-\354\236\210\353\202\230.md" diff --git a/README.md b/README.md index cee9462..6ea6e2c 100644 --- a/README.md +++ b/README.md @@ -104,6 +104,10 @@ git 이력을 작업 로그로 만드는 데 필요한 세 가지입니다(decis | **출처 기록** | 누가·어떤 도구와 모델로·얼마를 들여 만들었는지 자동으로 남김 | 위 트레일러 전체, `Task-Id` 브랜치 강제, `AI-Model` 게이트 | 측정 재작성(GF-129), 트레일러 집합 재정의(decision-19, `Signed-off-by`는 유지 — decision-25) | | **이력 불변** | 한 번 남긴 기록은 다시 쓰지 않음(append-only, decision-24) | 이 저장소의 GitHub 설정: 머지 커밋만 허용, main의 force push·삭제 금지(doc-20). 컨슈머는 doc-20을 참고해 직접 설정 | 로컬 훅 강제(DRAFT-18 append-only 훅 강제), CI 트레일러 검사(DRAFT-19) | +용어로 말하면 trail(감사 추적)이 세 기둥 전체이고, provenance(출처 기록)가 두 번째 기둥입니다. +attestation(서명된 증명)은 아직 없고, audit(사후 검사)은 커밋 시점 검사까지만 있습니다 — +자세한 구현 위치는 [개념 지도(doc-21)]()를 보세요. + 세 기둥은 서로를 필요로 합니다. 이력이 다시 쓰이면 트레일러가 자기 커밋에 대해 거짓이 되고, 기록할 내용이 정형화돼 있지 않으면 지킬 가치가 줄어듭니다. 로컬 훅은 `--no-verify`를 막지 못합니다. 그렇게 다시 쓴 이력이 main에 force push되는 것은 원격 @@ -170,6 +174,9 @@ cd ~/my-project ## 📚 더 자세한 내용이 궁금하시다면 +- **trail·provenance·attestation·audit이 각각 어느 훅·함수·설정에 구현돼 있고 어디가 + 비어 있는지**는 [개념 지도(doc-21)]()에 + 정리했습니다. - **설치 상세, 커밋 메시지 규칙 전문, AI 귀속 트레일러 표, 커스터마이즈, 저장소 구조, 주의점·한계**는 `backlog doc list`에서 확인하실 수 있습니다. - **설계 배경과 각 결정 이유**는 `backlog decision list`에서 diff --git "a/backlog/docs/doc-21 - \352\260\234\353\205\220-\354\247\200\353\217\204-\342\200\224-trail\302\267provenance\302\267attestation\302\267audit\354\235\264-\354\226\264\353\224\224\354\227\220-\352\265\254\355\230\204\353\217\274-\354\236\210\353\202\230.md" "b/backlog/docs/doc-21 - \352\260\234\353\205\220-\354\247\200\353\217\204-\342\200\224-trail\302\267provenance\302\267attestation\302\267audit\354\235\264-\354\226\264\353\224\224\354\227\220-\352\265\254\355\230\204\353\217\274-\354\236\210\353\202\230.md" new file mode 100644 index 0000000..d6c2cb7 --- /dev/null +++ "b/backlog/docs/doc-21 - \352\260\234\353\205\220-\354\247\200\353\217\204-\342\200\224-trail\302\267provenance\302\267attestation\302\267audit\354\235\264-\354\226\264\353\224\224\354\227\220-\352\265\254\355\230\204\353\217\274-\354\236\210\353\202\230.md" @@ -0,0 +1,107 @@ +--- +id: doc-21 +title: 개념 지도 — trail·provenance·attestation·audit이 어디에 구현돼 있나 +type: specification +created_date: '2026-10-03 03:46' +updated_date: '2026-10-03 03:46' +--- +git-trail이라는 이름(decision-26)과 설명에 쓰는 네 용어가 이 저장소에서 실제로 무엇을 가리키는지 +정리한다. 용어마다 **뜻 → 구현 위치 → 빈 곳** 순서다. 구현 위치는 함수 이름과 파일 경로로 적는다 +(줄 번호는 바뀌므로 적지 않는다). 훅이 도는 순서는 README "커밋 한 번에 훅이 도는 순서"를 본다. + +## 한눈에 + +| 용어 | 한 줄 뜻 | 주로 사는 곳 | 상태 | +| --- | --- | --- | --- | +| trail | 고칠 수 없는 작업 기록 | `hooks/post-commit`의 트레일러 삽입, GitHub 원격 설정 | 기록은 동작, 로컬 재작성 차단은 계획 | +| provenance | 누가·무엇으로·얼마를 들여 만들었나 | `hooks/post-commit`의 `trailer_*` 함수, `hooks/commit-msg`의 게이트 | 동작 (측정 일부 실험 단계) | +| attestation | 검증 가능한 증명 | 이 도구에는 없음, 사용자 git 서명 설정에 의존 | 미구현 | +| audit | 기록을 사후에 대조·검사 | `hooks/commit-msg`의 `validate_fixes_trailer`, doc-20 확인 명령 | 커밋 시점 검사만, 사후 검사는 계획 | + +## trail — 감사 추적 + +**뜻**: 누가·언제·무엇을 했는지 순서대로 남기고, 남긴 것은 고치지 않는 기록. git-trail에서는 +커밋 이력 자체가 trail이고, 트레일러가 그 기록의 항목이며, append-only 정책이 "고치지 않음"을 지킨다. + +**구현 위치** + +| 무엇 | 어디 | +| --- | --- | +| 트레일러를 커밋에 붙인다 | `hooks/post-commit` — 각 `trailer_*` 함수가 `queue_trailer()`로 쌓고, 파일 맨 아래에서 `git interpret-trailers` + `git commit --amend` 한 번으로 삽입 | +| 같은 트레일러를 두 번 붙이지 않는다 | `hooks/post-commit`의 `trailer_exists()` | +| 재생 커밋(merge·cherry-pick·rebase·revert)은 다시 기록하지 않는다 | `hooks/prepare-commit-msg`의 `is_replay_commit()` | +| 기록 형식의 기준값 | `hooks/gitformat.conf`의 `[gitformat "trailer"]` 섹션(키 이름) | +| push된 이력을 다시 쓰지 못하게 한다 | GitHub ruleset `main-protection`(`non_fast_forward`, `deletion`), 머지 커밋만 허용 — doc-20 | +| 정책 | decision-24(append-only), decision-25(이력 불변 기둥) | + +**빈 곳** +- 로컬에서 cherry-pick·`--amend`·squash를 막지 않는다. `is_replay_commit()`은 면제만 한다. 차단은 DRAFT-18 계획. +- 원격 보호는 이 저장소의 main 브랜치뿐이다. task 브랜치와 컨슈머 저장소는 보호되지 않는다(DRAFT-20). +- 웹 UI 커밋은 훅을 거치지 않아 트레일러가 하나도 없다(DRAFT-19). + +## provenance — 출처 기록 + +**뜻**: 결과물이 어디서, 무엇으로, 어떻게 만들어졌는지에 대한 기록. SLSA(공급망 보안 표준)의 용어다. +git-trail에서는 커밋마다 붙는 출처 트레일러가 provenance다. + +**구현 위치** — 모든 트레일러는 `hooks/post-commit`이 붙인다. + +| 트레일러 | 만드는 함수 | 값의 출처 | 관련 게이트(커밋을 막는 곳) | +| --- | --- | --- | --- | +| `Task-Id` | `trailer_task_id()` | 브랜치명의 `-<번호>` | `hooks/commit-msg`의 `enforce_task_id_branch()` — 패턴이 없으면 거부(decision-4) | +| `AI-Tool`, `AI-Tool-Version`, `Co-Authored-By` | `trailer_ai_tool()` | `AI_AGENT` 환경변수(Claude Code가 주입) | — | +| `AI-Model` | `trailer_ai_model()` → `read_transcript_model()`, `claude_transcript_path()` | Claude Code: 세션 트랜스크립트의 `message.model` / 그 외: `gitformat.aiModel` 설정 | `hooks/commit-msg`의 `enforce_ai_model_gate()` — Claude Code 외 도구는 `gitformat.knownModel` 목록에 있어야 함 | +| `Tokens-Used`, `Tool-Calls` | `trailer_tokens_used()` → `measure_claude_code_token_usage()`, `aggregate_usage()` | 트랜스크립트 `message.usage`를 응답 단위로 합산, 커서 `.git/.gitformat-token-cursor`(GF-139) | — | +| `Hooks-Commit` | `trailer_hooks_commit()` | 훅이 들어 있는 git-trail 클론의 `rev-parse --short HEAD` | — | +| `Signed-off-by` | `trailer_signed_off_by()` | 커밋 시점의 커미터(`%cn <%ce>`) | — (decision-25로 유지) | + +측정 방법과 한계의 상세는 doc-16, 트레일러별 레퍼런스는 doc-3, 에이전트 판정 신호는 doc-17. + +**빈 곳** +- Claude Code 외 도구의 `AI-Model`은 자가신고다(decision-15). 사용량 채널이 없어 `Tokens-Used`는 `unavailable (no-usage-channel)`. +- 트랜스크립트 경로는 Claude Code의 문서화되지 않은 규칙에 기댄다(`claude_transcript_path()` 주석). +- 서브에이전트의 토큰은 부모 트랜스크립트에 없어 집계되지 않는다. +- `Tokens-Used`는 "이 커밋에 쓴 양"이 아니라 "직전 커밋 이후 구간 전체"다. 귀속은 GF-129. + +## attestation — 증명 + +**뜻**: provenance를 **기계가 검증할 수 있는 형태**로 남긴 것. 보통 암호 서명이 붙는다(in-toto, +Sigstore, GitHub artifact attestations). "누가 그렇다고 주장했다"가 아니라 "그 주장이 위조되지 +않았음을 확인할 수 있다"가 핵심이다. + +**구현 위치 — git-trail 자체에는 없다.** + +| 무엇 | 어디 | 비고 | +| --- | --- | --- | +| 트레일러 | 커밋 메시지 안의 평문 | 서명되지 않은 주장. 누구나 손으로 쓸 수 있다 | +| 커밋 서명 | 사용자의 git 설정(`commit.gpgsign`, `gpg.format=ssh`) | git-trail이 설정하지 않는다. 켜져 있으면 `post-commit`의 amend로 만들어진 최종 커밋도 다시 서명되므로 트레일러까지 서명 범위에 들어간다 | +| 서명 강제 | GitHub ruleset `required_signatures` | 켜지 않았다(decision-24, DRAFT-20) | + +**빈 곳** +- 이 저장소에서 `git log --format=%G?`는 `N`으로 나온다. 서명이 없어서가 아니라 검증용 + `gpg.ssh.allowedSignersFile`이 설정돼 있지 않아서다. 검증 설정부터 정리해야 서명 강제를 검토할 수 있다. +- 트레일러 내용을 서명된 별도 증명(in-toto statement 등)으로 내보내는 기능은 없다. 계획도 아직 없다. + +## audit — 감사 + +**뜻**: 남은 기록을 사후에 대조하고 검사하는 일. trail이 "남기기"라면 audit는 "확인하기"다. + +**구현 위치** + +| 무엇 | 어디 | 시점 | +| --- | --- | --- | +| `Fixes:`가 가리키는 커밋이 실재하는지 | `hooks/commit-msg`의 `validate_fixes_trailer()` | 커밋 시점 | +| 제목·본문 형식 | `hooks/commit-msg`의 `validate_format()`, `validate_length()` | 커밋 시점 | +| 검사를 건너뛴 흔적 | `hooks/post-commit`의 `detect_verify_bypass()` + `hooks/prepare-commit-msg`의 `write_verified_marker()` → `Verify-Bypassed` | 커밋 직후. 사실상 도달 불가라 GF-128에서 제거 예정(decision-18·19) | +| 원격 설정이 그대로인지 | doc-20 "확인 방법"의 `gh api` 명령 | 수동 | +| 이력에서 트레일러 읽기 | `git log --format='%h %(trailers)'`, `git interpret-trailers --parse` | 수동 | + +**빈 곳** +- 커밋이 만들어진 뒤 트레일러가 참인지 자동으로 다시 확인하는 곳이 없다. CI 검사(DRAFT-19)가 + `Signed-off-by`와 실제 커미터, `Hooks-Commit`의 실재, `Task-Id`와 브랜치, 키 중복을 대조할 계획이다. +- 원격 설정이 되돌려지는 것을 감지하는 장치가 없다(DRAFT-20). + +## 관련 문서 + +decision-24(append-only) · decision-25(목적) · decision-26(이름) · doc-3(트레일러 레퍼런스) · +doc-16(토큰 측정) · doc-17(에이전트 판정 신호) · doc-19(이력 연산 판별) · doc-20(원격 설정) From 549d65c64ae121ecb45eb1db786d711c67440a5f Mon Sep 17 00:00:00 2001 From: cpu-once <231006716+cpu-once@users.noreply.github.com> Date: Sat, 3 Oct 2026 12:47:10 +0900 Subject: [PATCH 4/6] =?UTF-8?q?[docs][backlog]=20GF-140=20=EC=B0=A9?= =?UTF-8?q?=EC=88=98=20=EA=B8=B0=EB=A1=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-Id: GF-140 AI-Tool: claude-code AI-Tool-Version: 2.1.285 Co-Authored-By: Claude AI-Model: claude-opus-5-5 Tokens-Used: 0 Tool-Calls: 0 Hooks-Commit: abec7f3 Signed-off-by: cpu-once <231006716+cpu-once@users.noreply.github.com> --- ...274-\353\247\214\353\223\240\353\213\244.md" | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git "a/backlog/tasks/gf-140 - git-trail\353\241\234-\352\260\234\353\252\205\355\225\230\352\263\240-\352\260\234\353\205\220-\354\247\200\353\217\204-\353\254\270\354\204\234\353\245\274-\353\247\214\353\223\240\353\213\244.md" "b/backlog/tasks/gf-140 - git-trail\353\241\234-\352\260\234\353\252\205\355\225\230\352\263\240-\352\260\234\353\205\220-\354\247\200\353\217\204-\353\254\270\354\204\234\353\245\274-\353\247\214\353\223\240\353\213\244.md" index f82c740..346ec22 100644 --- "a/backlog/tasks/gf-140 - git-trail\353\241\234-\352\260\234\353\252\205\355\225\230\352\263\240-\352\260\234\353\205\220-\354\247\200\353\217\204-\353\254\270\354\204\234\353\245\274-\353\247\214\353\223\240\353\213\244.md" +++ "b/backlog/tasks/gf-140 - git-trail\353\241\234-\352\260\234\353\252\205\355\225\230\352\263\240-\352\260\234\353\205\220-\354\247\200\353\217\204-\353\254\270\354\204\234\353\245\274-\353\247\214\353\223\240\353\213\244.md" @@ -1,10 +1,11 @@ --- id: GF-140 title: git-trail로 개명하고 개념 지도 문서를 만든다 -status: To Do -assignee: [] +status: In Progress +assignee: + - '@claude' created_date: '2026-10-03 03:31' -updated_date: '2026-10-03 03:35' +updated_date: '2026-10-03 03:43' labels: - docs - naming @@ -49,3 +50,13 @@ GF-138과 같은 원칙으로 설정 키 gitformat.*(GF-133), 태스크 접두 - [ ] #3 git grep -i git-logbook -- ':!backlog' 결과에 의도한 예외만 남는다 - [ ] #4 rename 뒤 예전 URL 두 개(git-format, git-logbook)가 새 저장소로 리다이렉트되고 ruleset이 유지된다 + +## Implementation Plan + + +1. decision-26 기록 → 커밋 +2. 표시 이름 git-logbook → git-trail 치환(README·install.sh·훅·테스트·CI·.gitmessage·config·doc-1/5/6/20) → 테스트 → 커밋 +3. 개념 지도 doc 신규(trail·provenance·attestation·audit, 실제 함수·파일 기준) + README 링크 → 커밋 +4. 서브에이전트 독립 검토(함수명·경로 대조) +5. PR → 머지 커밋 → gh repo rename git-trail → origin 갱신 → description/topics → 리다이렉트 확인 + From 8f7982cacb863c6f65d25024f1b05e12815dae7c Mon Sep 17 00:00:00 2001 From: cpu-once <231006716+cpu-once@users.noreply.github.com> Date: Sat, 3 Oct 2026 16:14:39 +0900 Subject: [PATCH 5/6] =?UTF-8?q?[docs][backlog]=20=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EB=B0=98=EC=98=81:=20=EA=B0=9C=EB=85=90=20=EC=A7=80=EB=8F=84?= =?UTF-8?q?=20=EC=84=9C=EC=88=A0=20=EC=A0=95=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-Id: GF-140 AI-Tool: claude-code AI-Tool-Version: 2.1.285 Co-Authored-By: Claude AI-Model: claude-opus-5-5 Tokens-Used: 4159287 Tool-Calls: 13 Hooks-Commit: f677b47 Signed-off-by: cpu-once <231006716+cpu-once@users.noreply.github.com> --- ...4\353\217\274-\354\236\210\353\202\230.md" | 20 ++++++++++--------- ...0\352\262\214-\352\260\234\354\271\255.md" | 5 +++-- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git "a/backlog/docs/doc-21 - \352\260\234\353\205\220-\354\247\200\353\217\204-\342\200\224-trail\302\267provenance\302\267attestation\302\267audit\354\235\264-\354\226\264\353\224\224\354\227\220-\352\265\254\355\230\204\353\217\274-\354\236\210\353\202\230.md" "b/backlog/docs/doc-21 - \352\260\234\353\205\220-\354\247\200\353\217\204-\342\200\224-trail\302\267provenance\302\267attestation\302\267audit\354\235\264-\354\226\264\353\224\224\354\227\220-\352\265\254\355\230\204\353\217\274-\354\236\210\353\202\230.md" index d6c2cb7..fe9f052 100644 --- "a/backlog/docs/doc-21 - \352\260\234\353\205\220-\354\247\200\353\217\204-\342\200\224-trail\302\267provenance\302\267attestation\302\267audit\354\235\264-\354\226\264\353\224\224\354\227\220-\352\265\254\355\230\204\353\217\274-\354\236\210\353\202\230.md" +++ "b/backlog/docs/doc-21 - \352\260\234\353\205\220-\354\247\200\353\217\204-\342\200\224-trail\302\267provenance\302\267attestation\302\267audit\354\235\264-\354\226\264\353\224\224\354\227\220-\352\265\254\355\230\204\353\217\274-\354\236\210\353\202\230.md" @@ -3,7 +3,7 @@ id: doc-21 title: 개념 지도 — trail·provenance·attestation·audit이 어디에 구현돼 있나 type: specification created_date: '2026-10-03 03:46' -updated_date: '2026-10-03 03:46' +updated_date: '2026-10-03 07:14' --- git-trail이라는 이름(decision-26)과 설명에 쓰는 네 용어가 이 저장소에서 실제로 무엇을 가리키는지 정리한다. 용어마다 **뜻 → 구현 위치 → 빈 곳** 순서다. 구현 위치는 함수 이름과 파일 경로로 적는다 @@ -29,15 +29,16 @@ git-trail이라는 이름(decision-26)과 설명에 쓰는 네 용어가 이 저 | --- | --- | | 트레일러를 커밋에 붙인다 | `hooks/post-commit` — 각 `trailer_*` 함수가 `queue_trailer()`로 쌓고, 파일 맨 아래에서 `git interpret-trailers` + `git commit --amend` 한 번으로 삽입 | | 같은 트레일러를 두 번 붙이지 않는다 | `hooks/post-commit`의 `trailer_exists()` | -| 재생 커밋(merge·cherry-pick·rebase·revert)은 다시 기록하지 않는다 | `hooks/prepare-commit-msg`의 `is_replay_commit()` | +| 재생·병합 커밋은 커밋 시점 검사를 면제한다 | `hooks/prepare-commit-msg`의 `is_replay_commit()` — 이 훅의 검사만 건너뛴다. `post-commit`에는 면제가 없다(아래 빈 곳) | | 기록 형식의 기준값 | `hooks/gitformat.conf`의 `[gitformat "trailer"]` 섹션(키 이름) | | push된 이력을 다시 쓰지 못하게 한다 | GitHub ruleset `main-protection`(`non_fast_forward`, `deletion`), 머지 커밋만 허용 — doc-20 | | 정책 | decision-24(append-only), decision-25(이력 불변 기둥) | **빈 곳** - 로컬에서 cherry-pick·`--amend`·squash를 막지 않는다. `is_replay_commit()`은 면제만 한다. 차단은 DRAFT-18 계획. -- 원격 보호는 이 저장소의 main 브랜치뿐이다. task 브랜치와 컨슈머 저장소는 보호되지 않는다(DRAFT-20). -- 웹 UI 커밋은 훅을 거치지 않아 트레일러가 하나도 없다(DRAFT-19). +- 재생·병합 커밋의 기록이 의도와 다르다. revert에는 트레일러가 붙지만, cherry-pick·rebase 중에는 `post-commit`의 amend가 실패해(트레이스백) 붙지 않고, `git merge`는 `post-commit`을 아예 실행하지 않아 붙지 않는다. 의도(merge·revert는 기록, 재생은 면제)는 decision-24, DRAFT-18, GF-128에서 만든다. +- 원격 보호는 이 저장소의 main 브랜치뿐이다. task 브랜치와 태그는 보호되지 않고, main 직접 push도 원격이 막지 않는다(로컬 claude-rails 훅만 막는다, DRAFT-20). 컨슈머 저장소는 각자 설정해야 한다(README, doc-20). +- 웹 UI 커밋과 PR 머지 버튼으로 만든 머지 커밋은 훅을 거치지 않아 트레일러가 하나도 없다(DRAFT-19). ## provenance — 출처 기록 @@ -48,10 +49,10 @@ git-trail에서는 커밋마다 붙는 출처 트레일러가 provenance다. | 트레일러 | 만드는 함수 | 값의 출처 | 관련 게이트(커밋을 막는 곳) | | --- | --- | --- | --- | -| `Task-Id` | `trailer_task_id()` | 브랜치명의 `-<번호>` | `hooks/commit-msg`의 `enforce_task_id_branch()` — 패턴이 없으면 거부(decision-4) | +| `Task-Id` | `trailer_task_id()` | 브랜치명의 `-<번호>` | `hooks/commit-msg`의 `enforce_task_id_branch()` — 패턴이 없으면 거부. 예외 브랜치(main/master/develop/release/*)·detached HEAD·병합 중은 면제(decision-4) | | `AI-Tool`, `AI-Tool-Version`, `Co-Authored-By` | `trailer_ai_tool()` | `AI_AGENT` 환경변수(Claude Code가 주입) | — | | `AI-Model` | `trailer_ai_model()` → `read_transcript_model()`, `claude_transcript_path()` | Claude Code: 세션 트랜스크립트의 `message.model` / 그 외: `gitformat.aiModel` 설정 | `hooks/commit-msg`의 `enforce_ai_model_gate()` — Claude Code 외 도구는 `gitformat.knownModel` 목록에 있어야 함 | -| `Tokens-Used`, `Tool-Calls` | `trailer_tokens_used()` → `measure_claude_code_token_usage()`, `aggregate_usage()` | 트랜스크립트 `message.usage`를 응답 단위로 합산, 커서 `.git/.gitformat-token-cursor`(GF-139) | — | +| `Tokens-Used`, `Tool-Calls` | `trailer_tokens_used()` → `measure_claude_code_token_usage()`, `aggregate_usage()` | 트랜스크립트 `message.usage`를 응답 단위로 합산, 커서 `/.gitformat-token-cursor`(GF-139) | — | | `Hooks-Commit` | `trailer_hooks_commit()` | 훅이 들어 있는 git-trail 클론의 `rev-parse --short HEAD` | — | | `Signed-off-by` | `trailer_signed_off_by()` | 커밋 시점의 커미터(`%cn <%ce>`) | — (decision-25로 유지) | @@ -61,7 +62,8 @@ git-trail에서는 커밋마다 붙는 출처 트레일러가 provenance다. - Claude Code 외 도구의 `AI-Model`은 자가신고다(decision-15). 사용량 채널이 없어 `Tokens-Used`는 `unavailable (no-usage-channel)`. - 트랜스크립트 경로는 Claude Code의 문서화되지 않은 규칙에 기댄다(`claude_transcript_path()` 주석). - 서브에이전트의 토큰은 부모 트랜스크립트에 없어 집계되지 않는다. -- `Tokens-Used`는 "이 커밋에 쓴 양"이 아니라 "직전 커밋 이후 구간 전체"다. 귀속은 GF-129. +- **설계 의도는 커밋당 토큰량**(이 커밋에 스테이징된 파일을 실제로 건드린 응답의 토큰)이다. 지금 구현은 직전 커밋 이후 구간 전체(델타)를 세고 있어 의도와 다르다. GF-129에서 귀속 방식으로 바꾸고, 델타는 기록하지 않는다. +- commit-msg의 게이트와 검사(`enforce_task_id_branch()`, `enforce_ai_model_gate()`, `validate_*`)는 `--no-verify`로 건너뛸 수 있고, 지금은 그 흔적도 남지 않는다(`Verify-Bypassed`가 사실상 도달 불가). 검증을 prepare-commit-msg로 옮기는 GF-127에서 닫힌다. ## attestation — 증명 @@ -78,8 +80,8 @@ Sigstore, GitHub artifact attestations). "누가 그렇다고 주장했다"가 | 서명 강제 | GitHub ruleset `required_signatures` | 켜지 않았다(decision-24, DRAFT-20) | **빈 곳** -- 이 저장소에서 `git log --format=%G?`는 `N`으로 나온다. 서명이 없어서가 아니라 검증용 - `gpg.ssh.allowedSignersFile`이 설정돼 있지 않아서다. 검증 설정부터 정리해야 서명 강제를 검토할 수 있다. +- 이 저장소에서 `git log --format=%G?`는 로컬 커밋이 `N`, GitHub 머지 커밋이 `E`로 나온다. 로컬 커밋이 `N`인 건 + 서명이 없어서가 아니라 검증용 `gpg.ssh.allowedSignersFile`이 설정돼 있지 않아서다. 검증 설정부터 정리해야 서명 강제를 검토할 수 있다. - 트레일러 내용을 서명된 별도 증명(in-toto statement 등)으로 내보내는 기능은 없다. 계획도 아직 없다. ## audit — 감사 diff --git "a/backlog/tasks/gf-133 - \355\214\214\354\235\274\302\267\353\224\224\353\240\211\355\204\260\353\246\254-\354\235\264\353\246\204\354\235\204-\354\227\255\355\225\240\354\235\264-\353\223\234\353\237\254\353\202\230\352\262\214-\352\260\234\354\271\255.md" "b/backlog/tasks/gf-133 - \355\214\214\354\235\274\302\267\353\224\224\353\240\211\355\204\260\353\246\254-\354\235\264\353\246\204\354\235\204-\354\227\255\355\225\240\354\235\264-\353\223\234\353\237\254\353\202\230\352\262\214-\352\260\234\354\271\255.md" index 9536d99..0bd111f 100644 --- "a/backlog/tasks/gf-133 - \355\214\214\354\235\274\302\267\353\224\224\353\240\211\355\204\260\353\246\254-\354\235\264\353\246\204\354\235\204-\354\227\255\355\225\240\354\235\264-\353\223\234\353\237\254\353\202\230\352\262\214-\352\260\234\354\271\255.md" +++ "b/backlog/tasks/gf-133 - \355\214\214\354\235\274\302\267\353\224\224\353\240\211\355\204\260\353\246\254-\354\235\264\353\246\204\354\235\204-\354\227\255\355\225\240\354\235\264-\353\223\234\353\237\254\353\202\230\352\262\214-\352\260\234\354\271\255.md" @@ -4,7 +4,7 @@ title: 파일·디렉터리 이름을 역할이 드러나게 개칭 status: To Do assignee: [] created_date: '2026-09-25 19:34' -updated_date: '2026-10-03 03:04' +updated_date: '2026-10-03 07:14' labels: - hooks - naming @@ -13,6 +13,7 @@ dependencies: references: - decision-18 - decision-25 + - decision-26 documentation: - backlog/docs/doc-13 - git-format-재설계-계획-—-커밋-규칙을-prepare-commit-msg로-통합.md - backlog/docs/doc-18 - 재설계-작업-순서와-의존성.md @@ -50,7 +51,7 @@ c_cpp.py로 바꾸는 이유는 이 검사가 실제로 *.c까지 담당하기 - [ ] #7 git mv를 사용해 이력에서 이름 변경으로 추적된다 - [ ] #8 심볼릭 링크로 설치된 저장소에서도 새 경로가 해석된다 - [ ] #9 기존 gitformat.* 설정 키를 쓰던 컨슈머 저장소가 깨지지 않도록 읽기 호환 또는 이전 안내를 둔다 -- [ ] #10 [gitformat] 섹션의 모든 설정 키(컨슈머 오버라이드 taskPrefix·branchExempt·aiModel과 conf 내부 type·trailer.*·markerFile 등 전부), 마커·커서 파일명(.gitformat-verified, .gitformat-token-cursor), 환경변수 _GITFORMAT_AMEND_GUARD를 git-logbook 이름으로 옮긴다 — 프로젝트 개명(decision-25)의 후속 +- [ ] #10 [gitformat] 섹션의 모든 설정 키(컨슈머 오버라이드 taskPrefix·branchExempt·aiModel과 conf 내부 type·trailer.*·markerFile 등 전부), 마커·커서 파일명(.gitformat-verified, .gitformat-token-cursor), 환경변수 _GITFORMAT_AMEND_GUARD를 git-trail 이름으로 옮긴다 — 프로젝트 개명(decision-25, decision-26)의 후속 ## Definition of Done From 352814b771becf16fe3a4fafe4d651e957b59f75 Mon Sep 17 00:00:00 2001 From: cpu-once <231006716+cpu-once@users.noreply.github.com> Date: Sat, 3 Oct 2026 16:15:48 +0900 Subject: [PATCH 6/6] =?UTF-8?q?[docs][backlog]=20GF-140=20=EC=99=84?= =?UTF-8?q?=EB=A3=8C=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-Id: GF-140 AI-Tool: claude-code AI-Tool-Version: 2.1.285 Co-Authored-By: Claude AI-Model: claude-opus-5-5 Tokens-Used: 1032900 Tool-Calls: 3 Hooks-Commit: 4e29fad Signed-off-by: cpu-once <231006716+cpu-once@users.noreply.github.com> --- ...4-\353\247\214\353\223\240\353\213\244.md" | 32 +++++++++++++------ 1 file changed, 22 insertions(+), 10 deletions(-) diff --git "a/backlog/tasks/gf-140 - git-trail\353\241\234-\352\260\234\353\252\205\355\225\230\352\263\240-\352\260\234\353\205\220-\354\247\200\353\217\204-\353\254\270\354\204\234\353\245\274-\353\247\214\353\223\240\353\213\244.md" "b/backlog/tasks/gf-140 - git-trail\353\241\234-\352\260\234\353\252\205\355\225\230\352\263\240-\352\260\234\353\205\220-\354\247\200\353\217\204-\353\254\270\354\204\234\353\245\274-\353\247\214\353\223\240\353\213\244.md" index 346ec22..a8401cf 100644 --- "a/backlog/tasks/gf-140 - git-trail\353\241\234-\352\260\234\353\252\205\355\225\230\352\263\240-\352\260\234\353\205\220-\354\247\200\353\217\204-\353\254\270\354\204\234\353\245\274-\353\247\214\353\223\240\353\213\244.md" +++ "b/backlog/tasks/gf-140 - git-trail\353\241\234-\352\260\234\353\252\205\355\225\230\352\263\240-\352\260\234\353\205\220-\354\247\200\353\217\204-\353\254\270\354\204\234\353\245\274-\353\247\214\353\223\240\353\213\244.md" @@ -1,11 +1,11 @@ --- id: GF-140 title: git-trail로 개명하고 개념 지도 문서를 만든다 -status: In Progress +status: Done assignee: - '@claude' created_date: '2026-10-03 03:31' -updated_date: '2026-10-03 03:43' +updated_date: '2026-10-03 07:15' labels: - docs - naming @@ -34,20 +34,20 @@ GF-138과 같은 원칙으로 설정 키 gitformat.*(GF-133), 태스크 접두 ## Acceptance Criteria -- [ ] #1 decision-26으로 이름을 git-trail로 바꾼다고 기록하고, decision-25의 이름 조항만 대체한다고 명시한다 -- [ ] #2 README, install.sh, 훅 주석, .gitmessage, CI, 테스트, backlog config, doc-1·5·6·20의 표시 이름 git-logbook이 git-trail로 바뀐다 (설정 키·파일명 gitformat.*은 제외) -- [ ] #3 trail·provenance·attestation·audit 각각의 정의, 구현 위치(훅 함수·파일·설정·원격), 빈 곳을 적은 개념 지도 문서를 backlog doc으로 만든다 -- [ ] #4 개념 지도의 구현 위치는 실제 코드의 함수명·파일 경로와 일치한다 -- [ ] #5 README에서 개념 지도 문서로 안내한다 +- [x] #1 decision-26으로 이름을 git-trail로 바꾼다고 기록하고, decision-25의 이름 조항만 대체한다고 명시한다 +- [x] #2 README, install.sh, 훅 주석, .gitmessage, CI, 테스트, backlog config, doc-1·5·6·20의 표시 이름 git-logbook이 git-trail로 바뀐다 (설정 키·파일명 gitformat.*은 제외) +- [x] #3 trail·provenance·attestation·audit 각각의 정의, 구현 위치(훅 함수·파일·설정·원격), 빈 곳을 적은 개념 지도 문서를 backlog doc으로 만든다 +- [x] #4 개념 지도의 구현 위치는 실제 코드의 함수명·파일 경로와 일치한다 +- [x] #5 README에서 개념 지도 문서로 안내한다 - [ ] #6 PR을 머지 커밋으로 병합한 뒤 GitHub 저장소를 coralstay/git-trail로 rename하고 origin URL을 갱신한다 - [ ] #7 GitHub 저장소 description과 topics에 trail·provenance·attestation·audit을 반영한다 ## Definition of Done -- [ ] #1 python3 -m unittest discover -s tests 전체 통과 -- [ ] #2 ruff check 통과 -- [ ] #3 git grep -i git-logbook -- ':!backlog' 결과에 의도한 예외만 남는다 +- [x] #1 python3 -m unittest discover -s tests 전체 통과 +- [x] #2 ruff check 통과 +- [x] #3 git grep -i git-logbook -- ':!backlog' 결과에 의도한 예외만 남는다 - [ ] #4 rename 뒤 예전 URL 두 개(git-format, git-logbook)가 새 저장소로 리다이렉트되고 ruleset이 유지된다 @@ -60,3 +60,15 @@ GF-138과 같은 원칙으로 설정 키 gitformat.*(GF-133), 태스크 접두 4. 서브에이전트 독립 검토(함수명·경로 대조) 5. PR → 머지 커밋 → gh repo rename git-trail → origin 갱신 → description/topics → 리다이렉트 확인 + +## Implementation Notes + + +검증: unittest 98개·ruff·shellcheck 통과. git grep -i git-logbook -- ':!backlog'는 README 개명 이력 줄만 남음. doc-6에는 git-logbook 문자열이 없어 바꿀 것이 없었음(AC#2). 개념 지도 doc-21은 서브에이전트가 함수명·경로·GitHub 설정을 코드와 실측으로 대조 — is_replay_commit 행의 오류(post-commit에는 면제 없음), --no-verify 우회 누락, 원격 보호 빈 곳 누락, %G? 서술을 반영해 고침. 유저 지시로 Tokens-Used 서술을 '설계 의도는 커밋당 토큰량, 현재 구현은 델타라 의도와 다름(GF-129)'으로 정정. + + +## Final Summary + + +이름을 git-trail로 바꾸고(decision-26, decision-25의 이름 조항만 대체) README·install.sh·훅 주석·테스트·CI·backlog config·doc-1/5/20의 표시 이름을 맞췄다. trail·provenance·attestation·audit이 어느 훅 함수·파일·원격 설정에 구현돼 있고 어디가 비어 있는지 doc-21로 정리해 README에서 연결했다. 테스트 98개·ruff·shellcheck 통과, doc-21은 코드·실측과 대조 검증. GitHub rename과 topics(AC#6·7, DoD#4)는 PR 머지 후 처리한다. +