16 KiB
3-플랫폼 동기화 Phase 2 (마지막) — hooks + 프로젝트 지침 + 정리 Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans. Steps use checkbox (
- [ ]) syntax.
Goal: Claude의 결정론 훅(wiki_structure_lint.py 구조 린트 + wiki_claim_gate.py claim 게이트)을 Codex CLI(.codex/hooks.json)에 연결하고, 세 플랫폼이 동일한 프로젝트 지침(CLAUDE.md)을 읽도록 codex AGENTS.md/config를 구성하며, Phase 0~1에서 생긴 문서 잔여를 정리해 동기화를 완결한다.
Architecture: 훅 스크립트는 이미 멀티-variant 입력 리더(tool_name / tool_call.name)를 갖췄지만 차단 규약과 출력 스키마가 플랫폼마다 다르다(Claude: exit 2+stderr / Antigravity: {decision:deny} JSON / Codex: 경험적 확인 필요). 따라서 Phase 2는 (1) 이식성 버그 선수정(claim_gate 하드코딩 ROOT) → (2) codex 훅 payload·차단 규약 경험적 캡처(codex 설치됨) → (3) .codex/hooks.json 배선 + 필요 시 codex-variant 어댑트 → (4) 프로젝트 지침 DRY 구성 → (5) CLAUDE.md 잔여 정리 순으로 간다. 훅 스크립트 SSOT 는 .claude/hooks/ 1벌을 유지하고 플랫폼별 config 가 그것을 가리킨다(스크립트 중복 금지).
Tech Stack: Python 3.12, codex-cli 0.136.0, agy(Antigravity), pytest(venv).
배경 / 현재 훅 지형 (실측)
| 훅 | 위치 | root 해석 | 입력 | 차단 규약 |
|---|---|---|---|---|
wiki_structure_lint.py |
.claude/hooks/ |
DEFAULT_ROOT = SCRIPT.parents[2] (동적·이식 OK) |
--hook(stdin JSON→file_path, C2 non-blocking) / --file / --all |
non-blocking(경고) |
wiki_claim_gate.py |
.claude/hooks/ |
ROOT=Path("/home/donghyeon/Documents/LLM Wiki") 하드코딩 — 이 repo 경로 아님(버그) |
stdin JSON (tool_name/tool_call.name/hook_event_name) |
Claude: exit 2 + stderr |
wiki_hard_gate.py |
~/.gemini/antigravity-cli/hooks/ (global) |
— | antigravity protobuf (tool_call.{name,input}) |
Antigravity: stdout {decision:"deny"} |
- Claude
.claude/settings.json: PreToolUse(claim_gate, matcher*) + PostToolUse(structure_lint--hook, matcherWrite|Edit|MultiEdit) + SubagentStart/Stop(claim_gate). 명령은python3 "$CLAUDE_PROJECT_DIR"/.claude/hooks/<script>. - Antigravity
.agents/hooks.json:wiki-hard-gatePreToolUse(matcher*) → globalwiki_hard_gate.py. 이미 claim/self-grep 게이트 역할 수행. - Codex hooks(공식,
developers.openai.com/codex/hooks):<repo>/.codex/hooks.json또는config.toml [hooks]. 이벤트 PreToolUse/PostToolUse/Stop/SessionStart/SubagentStart/SubagentStop 등. matcher(regex)+command. payload·차단 규약은 본 plan Task 2 에서 경험적 캡처. - Codex 프로젝트 지침:
AGENTS.md(root-down 연결). configproject_doc_fallback_filenames로 다른 파일명을 지침으로 인정 가능 → CLAUDE.md 를 codex 지침으로 재사용(DRY) 가능.
File Structure
- Modify:
.claude/hooks/wiki_claim_gate.py— 하드코딩 ROOT → 동적(SCRIPT.parents[2]또는$CLAUDE_PROJECT_DIR/cwd). 모든 플랫폼 공용 버그픽스. - Create:
.codex/hooks.json— structure_lint(PostToolUse) + claim_gate(PreToolUse) 배선. - Create:
.codex/config.toml—project_doc_fallback_filenames = ["CLAUDE.md"](codex 가 CLAUDE.md 를 지침으로 읽도록, DRY). - Create (조건부):
AGENTS.md(root) — config fallback 이 동작 안 하면 thin pointer 대안. - Create:
docs/superpowers/notes/2026-06-04-phase2-codex-hook-schema.md— codex 훅 payload·차단 규약 경험적 기록. - Modify (정리):
CLAUDE.md§2 표(line 48.claude/commands/행에 codex/antigravity 등가 1줄) + 자동화 섹션 잔여 "7개" 카운트/구식 서술.
훅 스크립트 는
.claude/hooks/1벌만 SSOT. codex.codex/hooks.json은 절대경로/$CODEX_PROJECT_DIR로 그 스크립트를 가리키며 복제하지 않는다.
Task 1: claim_gate 하드코딩 ROOT 버그픽스 (공용 선수정)
Files:
-
Modify:
.claude/hooks/wiki_claim_gate.py -
Test:
.claude/hooks/기존 테스트가 있으면 거기, 없으면scripts/test_sync_automation.py밖의 별도 확인. -
Step 1: 현재 ROOT 사용처 확인
cd /home/donghyeon/dev/llm-wiki-private
grep -nE "ROOT" .claude/hooks/wiki_claim_gate.py
사용처: L20 정의, L81 ROOT / p(상대→절대), L149 relative_to(ROOT).
- Step 2: 동적 root 로 교체
structure_lint.py 와 동일 패턴 적용. L20 을:
ROOT = Path(__file__).resolve().parents[2] # .claude/hooks/<this> -> repo root
로 교체(환경변수 우선이 필요하면 Path(os.environ.get("CLAUDE_PROJECT_DIR") or Path(__file__).resolve().parents[2]) — 단 codex 는 CODEX_PROJECT_DIR, antigravity 는 다른 변수일 수 있으므로 파일 위치 기반이 가장 이식적. 파일 위치 기반으로 간다).
- Step 3: 동작 확인 (allow 경로 + relative_to 안전)
cd /home/donghyeon/dev/llm-wiki-private
# 무해한 read 툴 이벤트는 allow(exit 0)
echo '{"hook_event_name":"PreToolUse","tool_name":"Read","tool_input":{"file_path":"README.md"}}' | python3 .claude/hooks/wiki_claim_gate.py; echo "exit=$?"
# repo 내부 경로가 relative_to 에서 깨지지 않음
echo '{"hook_event_name":"PreToolUse","tool_name":"Write","tool_input":{"file_path":"raw/branch-notes/x.md","content":"---\ntitle: t\n---\n"}}' | python3 .claude/hooks/wiki_claim_gate.py; echo "exit=$?"
Expected: 첫 명령 exit=0. 둘째는 게이트 규칙에 따라 0 또는 2 — ValueError/traceback 이 없어야 함(하드코딩 ROOT 였으면 relative_to 에서 깨졌음).
- Step 4: Commit
git add .claude/hooks/wiki_claim_gate.py
git commit -m "fix(hooks): claim_gate uses dynamic repo root (was hardcoded stale path) — portable across platforms"
Task 2: codex 훅 payload·차단 규약 경험적 캡처
Files:
- Create:
docs/superpowers/notes/2026-06-04-phase2-codex-hook-schema.md
codex 훅이 PreToolUse 에 넘기는 JSON 구조와 차단 방법(exit code? stdout JSON
decision?)을 실제로 캡처해야.codex/hooks.json을 정확히 쓸 수 있다.
- Step 1: codex 훅 문서/도움말 확인
cd /home/donghyeon/dev/llm-wiki-private
codex --help 2>&1 | grep -iE "hook" || echo "no hook in top help"
codex exec --help 2>&1 | grep -iE "hook" || echo "no hook flag in exec"
- Step 2: 캡처용 probe 훅으로 실제 payload 덤프
.codex/hooks.json 에 임시 probe(stdin 을 파일로 덤프 후 allow)를 걸고 codex 로 1개 툴을 실행시켜 payload 를 캡처:
cd /home/donghyeon/dev/llm-wiki-private
mkdir -p .codex
cat > /tmp/codex-hook-probe.py <<'PY'
import sys, json, pathlib
data = sys.stdin.read()
pathlib.Path("/tmp/codex-hook-payload.json").write_text(data)
sys.exit(0) # allow
PY
cat > .codex/hooks.json <<'JSON'
{ "hooks": { "PreToolUse": [ { "matcher": "*",
"hooks": [ { "type": "command", "command": "python3 /tmp/codex-hook-probe.py", "timeout": 30 } ] } ] } }
JSON
# codex 로 안전한 read-only 작업 한 번 실행 (sandbox read-only, 승인 never)
codex exec -s read-only -C /home/donghyeon/dev/llm-wiki-private "list the files in the repo root with ls" 2>&1 | tail -5 || true
echo "--- captured payload ---"; cat /tmp/codex-hook-payload.json 2>/dev/null || echo "no payload captured"
캡처된 JSON 의 필드(tool_name? tool_call.name? hook_event_name?)를 노트에 기록.
- Step 3: 차단 규약 확인
probe 를 sys.exit(2) + stderr 로 바꿔 codex 가 툴을 차단하는지, 아니면 stdout JSON({"decision":"deny"})을 요구하는지 확인(공식 hooks 문서 + 실측). 결과를 노트에 기록.
- Step 4: 정리 + 노트 커밋
cd /home/donghyeon/dev/llm-wiki-private
rm -f .codex/hooks.json # probe 제거 (Task 3 에서 정식 작성)
git add docs/superpowers/notes/2026-06-04-phase2-codex-hook-schema.md
git commit -m "docs(phase2): capture codex hook payload + block convention (empirical)"
Task 3: .codex/hooks.json 배선 (+ 필요 시 claim_gate codex-variant)
Files:
-
Create:
.codex/hooks.json -
Modify (조건부):
.claude/hooks/wiki_claim_gate.py(codex payload·차단 variant — Task 2 결과가 Claude 규약과 다를 때만) -
Step 1:
.codex/hooks.json작성
Task 2 에서 확인한 이벤트명/matcher 로:
{
"hooks": {
"PreToolUse": [
{ "matcher": "Edit|Write|MultiEdit",
"hooks": [ { "type": "command",
"command": "python3 .codex/../.claude/hooks/wiki_claim_gate.py",
"timeout": 30 } ] } ],
"PostToolUse": [
{ "matcher": "Edit|Write|MultiEdit",
"hooks": [ { "type": "command",
"command": "python3 .codex/../.claude/hooks/wiki_structure_lint.py --hook",
"timeout": 30 } ] } ]
}
}
command 경로는 Task 2 에서 codex 훅의 cwd 가 repo root 인지 확인 후 결정: repo-root 이면
python3 .claude/hooks/...(상대), 아니면 절대경로python3 /home/donghyeon/dev/llm-wiki-private/.claude/hooks/.... 하드코딩 절대경로는 이식성 해치므로 codex 가 제공하는 project-dir 변수($CODEX_PROJECT_DIR등, Task 2 확인)를 우선 사용. matcher 정규식은 codex 의 실제 tool 이름(Task 2)으로 맞춤 — codex tool 이름이shell/apply_patch등이면 그에 맞게 조정.
- Step 2: claim_gate codex-variant (조건부)
Task 2 가 "codex 차단 = exit 2 / 입력 = tool_call.name" 으로 Claude 와 호환이면 스크립트 수정 불필요(이미 멀티-variant 리더). 다르면(예: codex 가 stdout JSON 요구) wiki_claim_gate.py 에 codex 분기 추가:
-
입력:
hook_event_name/tool_name없으면 codex 필드에서 추출. -
출력: codex 차단 규약에 맞춰 emit. 기존 Claude/antigravity 경로 회귀 없이 분기. TDD: codex payload 샘플(Task 2 캡처)을 fixture 로 한 단위 테스트 추가.
-
Step 3: 실호출 검증
cd /home/donghyeon/dev/llm-wiki-private
# codex 가 구조 위반 파일 저장을 차단/경고하는지 (read-only 가 아닌 workspace-write 로 한 번)
codex exec -s workspace-write -C /home/donghyeon/dev/llm-wiki-private "create a file raw/branch-notes/zzz-hook-test.md with body 'no frontmatter'" 2>&1 | tail -8 || true
ls raw/branch-notes/zzz-hook-test.md 2>&1 # claim_gate 가 frontmatter 없는 raw 를 막았는지
rm -f raw/branch-notes/zzz-hook-test.md
기대: claim_gate 규칙에 따라 차단되거나 경고. 결과를 노트에 기록.
- Step 4: Commit
git add .codex/hooks.json .claude/hooks/wiki_claim_gate.py docs/superpowers/notes/
git commit -m "feat(codex): wire .codex/hooks.json (claim gate + structure lint) reusing .claude/hooks scripts"
Task 4: 프로젝트 지침 DRY 구성 (codex AGENTS.md / config)
Files:
-
Create:
.codex/config.toml -
Create (조건부):
AGENTS.md -
Step 1: config fallback 시도 (DRY 우선)
cd /home/donghyeon/dev/llm-wiki-private
cat > .codex/config.toml <<'TOML'
# codex 가 CLAUDE.md 를 프로젝트 지침으로 읽게 함 (SSOT 중복 방지)
project_doc_fallback_filenames = ["CLAUDE.md"]
TOML
# 확인: codex 가 CLAUDE.md 를 지침으로 로드하는지
codex exec -s read-only -C /home/donghyeon/dev/llm-wiki-private "이 저장소의 운영 규칙 문서 이름과 첫 섹션 제목을 말해줘" 2>&1 | tail -8 || true
codex 가 CLAUDE.md 내용을 인지하면 AGENTS.md 불필요(DRY 달성). 노트 기록.
- Step 2: fallback 미동작 시 thin AGENTS.md (대안)
config fallback 이 안 먹으면 root 에 thin pointer 작성(중복 본문 금지):
# AGENTS.md
이 저장소의 운영 규칙 SSOT 는 `CLAUDE.md` 다. 모든 agent/명령은 `CLAUDE.md` 와 `rules/` 를 정독한다.
(이 파일은 codex/antigravity 가 AGENTS.md 를 우선 탐색할 때의 포인터일 뿐, 규칙 본문을 중복하지 않는다.)
- Step 3: Commit
git add .codex/config.toml AGENTS.md 2>/dev/null; git add .codex/config.toml
git commit -m "feat(codex): read CLAUDE.md as project instructions via config fallback (DRY, no duplication)"
Task 5: antigravity hooks 커버리지 점검 (보강 여부 결정)
Files:
-
Modify (조건부):
.agents/hooks.json -
Step 1: hard_gate 가 structure-lint/claim-gate 를 이미 커버하는지 판정
cd /home/donghyeon/dev/llm-wiki-private
grep -nE "frontmatter|Parent|Claim|structure|template|raw/branch|decision" ~/.gemini/antigravity-cli/hooks/wiki_hard_gate.py | head
wiki_hard_gate.py 가 이미 claim/구조 게이트를 antigravity 스키마로 수행 중이면(현재 그렇게 보임) 추가 배선 불필요 — 그 사실을 노트에 명시하고 종료. 빠진 검사(예: structure_lint 의 C2)가 있고 antigravity 에서 필요하면 PreToolUse 항목 추가(단 antigravity Stop 은 응답 텍스트 못 봄 → content gate 는 PreToolUse 만).
- Step 2: (보강 시)
.agents/hooks.json갱신 + Commit. (불필요 시) 노트만 커밋.
cd /home/donghyeon/dev/llm-wiki-private
git add docs/superpowers/notes/ .agents/hooks.json 2>/dev/null
git commit -m "docs(phase2): antigravity hook coverage assessment (hard_gate already covers claim/structure)" || echo "nothing to commit"
Task 6: CLAUDE.md / 문서 잔여 정리
Files:
-
Modify:
CLAUDE.md -
Step 1: §2 표 + 잔여 카운트 정리
-
§2 디렉터리 역할 표(line 48 부근
.claude/commands/)에 "codex.agents/skills/· antigravity.agents/workflows/로 동기화(scripts/sync_automation.py commands)" 1줄. -
자동화 섹션의 "동일 7개 agent" → "9개 agent" (branch-depth-auditor·coverage-auditor 추가 반영). antigravity bullet 도 동일.
-
(Phase 0~1 에서 이미 codex bullet·sync 경로는 갱신됨 — 누락분만.)
-
Step 2: 최종 동기화 sanity
cd /home/donghyeon/dev/llm-wiki-private
python3 scripts/sync_automation.py agents --check && python3 scripts/sync_automation.py commands --check && echo "ALL DRIFT-0"
.venv/bin/python -m pytest scripts/test_sync_automation.py -q | tail -2
grep -rn "convert-wiki-agents" . --include=*.md 2>/dev/null | grep -v docs/superpowers || echo "no dead convert-script refs"
Expected: ALL DRIFT-0, 테스트 green, dead ref 없음.
- Step 3: Commit
git add CLAUDE.md
git commit -m "docs: CLAUDE.md final cleanup — 9 agents, commands cross-platform mapping (Phase 2)"
Phase 2 완료 기준 (Definition of Done)
- claim_gate 하드코딩 ROOT 버그 수정(이식성).
- codex 훅 payload·차단 규약 경험적 캡처 +
.codex/hooks.json배선 동작(실호출 검증). - codex 가 CLAUDE.md 를 프로젝트 지침으로 읽음(config fallback) 또는 thin AGENTS.md.
- antigravity 훅 커버리지 판정(보강 또는 "이미 커버" 명시).
- CLAUDE.md 잔여(카운트·매핑) 정리, dead ref 0.
agents/commands모두 drift-0, 테스트 green.
이로써 3-플랫폼 동기화 전체 완료: agents(9)·commands(13)·hooks·project-instructions 가 Codex·Antigravity 에 Claude 와 동등하게 구성됨. 이후: 브랜치 merge/PR (finishing-a-development-branch).
Self-Review (작성자 체크)
- Spec coverage: 설계 §4 Phase 2 의 모든 항목 — codex hooks.json, AGENTS.md/지침, antigravity hooks 보강, CLAUDE.md 정리 — Task 1~6 에 매핑. 추가로 발견된 claim_gate 하드코딩 ROOT 버그를 Task 1 로 선수정(공용 이득).
- Placeholder scan: codex 훅 payload/차단 규약은 미지(unknown) 라 Task 2 에서 경험적 캡처 후 Task 3 가 그 결과에 분기하도록 구성(추측 금지). config fallback 동작 여부도 Task 4 에서 실측 후 분기.
- Type/일관성: 훅 스크립트 SSOT 1벌(
.claude/hooks/) 원칙 유지, 플랫폼 config 가 가리키기만 함. codex command/agent 생성기(Phase 0~1)와 독립. - 알려진 한계: codex 훅의 정확한 payload/차단 규약이 docs 로 확정 안 됨 → Task 2 경험적 캡처에 의존. codex 가 설치돼 있어 실측 가능(미설치였으면 BLOCKED). antigravity 훅은 이미 hard_gate 로 커버될 가능성이 높아 Task 5 는 "판정 우선, 보강은 조건부".