# task-3017 — 쓰기 게이트 복구 + 계보 검사 신설 (오늘 밤 한정 · 순서 엄수)

**레벨**: Lv.2 (거버넌스) · **팀**: dev4-team (비슈누) · **회장 승인 완료** (2026-08-25 21:10)

## 배경 — ANU 가 오늘 실제 사고를 냈다

2026-08-25 ANU(오케스트레이터)가 판단 오류 7건을 냈고, 그중 **1건은 실제 회귀 사고**다.

```
[C] 상태 오염 (실제 사고)
  ANU 가 worktree(base=997fa5d) 의 파일을 934168e 브랜치에 통째로 PUT
  → t3014 변경분을 덮어씀 (codex 29→17건, cafe 8→1건, server/main.py -161줄)
  → CI 2건 실패로 발견, origin/main merge 로 수습
  근인: "내 작업 base" 와 "쓰기 대상 브랜치 HEAD" 가 다른 것을 모르고 덮어씀

[B] 토큰 가드 우회 (2건)
  ANU 가 gh 호출에 GH_TOKEN="$BOT_GITHUB_TOKEN" 을 빠뜨려 회장 개인 PAT 로 인증됨
  → InsuRo #251, InsuWiki #8 의 merged_by = JonghyukJeon (감사기록 오염)
  ★★ 그런데 ~/.claude/hooks/gh-token-guard.sh 가 이미 존재한다.
     쓰기형 gh 명령에 GH_TOKEN 이 없으면 deny 하도록 만들어져 있는데 2건이 통과했다.
```

**Codex 판단(2026-08-25 4회 조율): 규칙이 부족한 게 아니라 "적용 경로 신뢰가 붕괴"됐다.
게이트를 늘리기 전에 기존 것부터 복구하라.**

---

## ★ 순서를 지켜라 — ①을 끝내기 전에 ②를 시작하지 마라

### ① `gh-token-guard.sh` 복구·검증 (먼저)
```
위치: /home/jay/.claude/hooks/gh-token-guard.sh
설계 의도: 쓰기형 gh 명령(pr create/merge/edit/review, api -X PUT/POST/PATCH/DELETE)에
          GH_TOKEN= 또는 GITHUB_TOKEN= 이 없으면 deny. 읽기 전용은 통과.
```
**할 일**
1. **왜 2건이 통과했는지 규명하라.** 추측하지 말고 재현하라.
   - 훅이 실제로 등록·발화하는가? (`settings.json` 매처 확인)
   - `gh api -X PUT .../merge` 형태를 패턴이 잡는가?
   - 여러 줄 명령·환경변수 선행 대입·서브셸 등에서 우회되는가?
2. 구멍을 막되 **읽기 전용 gh 호출은 계속 통과**해야 한다(오탐 금지)
3. ★ **회장 본인의 PAT 사용을 막지 마라.** 막을 대상은 **"의도치 않은 폴백"** 이다

### ② 계보 검사 게이트 신설 (①이 끝난 뒤)
쓰기 직전 **"대상 브랜치 HEAD · 작업 base · worktree"** 가 같은 계보인지 검사하고,
불일치면 **중단**한다.

**적용 대상 (최소한 이것들)**
- `gh api -X PUT .../contents/...` (파일 통째 PUT) ← **오늘 사고가 난 경로**
- `gh api -X PUT .../pulls/N/merge`

**동작**
- 불일치 감지 시 **deny + 사유 제시**: "작업 base 는 X, 대상 브랜치 HEAD 는 Y. merge/rebase 후 재시도"
- ★ **정상 작업을 막지 마라.** 신규 파일 생성·같은 계보 갱신은 통과해야 한다
- ★ **판단이 어려우면 통과시키지 말고 사유를 붙여 deny 하라**(fail-closed). 다만 그 사유가 사람이 읽고
  바로 조치할 수 있어야 한다

---

## ★★ 금지 (오늘 밤 레드라인 — Codex 4회 조율 합의)
- **범위 확장 금지.** 이 태스크는 ①② 뿐이다. 다른 훅·다른 규칙을 함께 손대지 마라
- **검증 안 된 새 안전장치 남발 금지.** 게이트를 여러 개 만들지 말고 **①②만** 확실히
- **기존 훅 4개를 건드리지 마라**: `anu-unreviewed-reports.sh` · `anu-block-new-dispatch.sh` ·
  `ops-rules-inject.sh` · (그리고 ①의 대상인 `gh-token-guard.sh` 외)
- `projects/**` 제품 코드 수정 금지
- **진행 중 작업 방해 금지** — dev3 가 t3016(머지 통제 조사) 진행 중이고,
  ANU 가 곧 PR #256 머지·배포를 해야 한다. **정상 경로를 막으면 안 된다**

## allowed_resources

```yaml
allowed_resources:
  paths:
    - "/home/jay/.claude/hooks/gh-token-guard.sh"
    - "/home/jay/.claude/hooks/git-lineage-guard.sh"
    - "/home/jay/.claude/settings.json"
    - "scripts/__tests__/**"
    - "memory/reports/task-3017.md"
  forbidden_paths:
    - "/home/jay/.claude/hooks/anu-unreviewed-reports.sh"
    - "/home/jay/.claude/hooks/anu-block-new-dispatch.sh"
    - "/home/jay/.claude/hooks/ops-rules-inject.sh"
    - "projects/**"
    - "memory/plans/tasks/**"
  commands:
    - "bash"
    - "python3"
    - "gh"
    - "jq"
  merge_policy: "tiered"
  ttl_hours: 12
```

## 검증 (실측 강제 — 이것이 이 태스크의 전부다)
1. **① 우회 재현** — 현재 가드로 회장 PAT 폴백이 실제로 통과하는지 재현해 기록하라.
   재현 못 하면 **"재현 실패"라고 정직히** 적고, 그렇다면 2건이 어떻게 통과했는지 다른 가설을 대라
2. **① 수정 후 차단 실증** — 같은 명령이 deny 되는지 확인
3. **① 오탐 없음** — 읽기 전용 `gh api` 호출이 그대로 통과하는지 확인
4. **② 계보 불일치 차단 실증** — 일부러 base 가 다른 상태를 만들어 PUT 을 시도했을 때 deny 되는지
5. **② 정상 통과 실증** — 같은 계보에서의 PUT·신규 파일 생성이 통과하는지
6. ★ **훅이 실제로 발화하는지 확인하라.** 파일만 만들고 `settings.json` 등록을 빠뜨리면 무용지물이다.
   **"만들었다"가 아니라 "실제로 막혔다"를 증거로 제시하라**

## 운영 계약
- ★ `gh` 호출 시 `GH_TOKEN="$BOT_GITHUB_TOKEN"` 주입 필수.
  **이 태스크가 그 규칙을 복구하면서 스스로 어기면 자기모순이다.**
- 워크플로우 `/home/jay/workspace/prompts/DIRECT-WORKFLOW.md` · QC `/home/jay/workspace/teams/shared/QC-RULES.md`
- `WORKSPACE_ROOT=/home/jay/workspace` · `CHAT_ID=6937032012` · 수집자 key `ANU_KEY=c119085addb0f8b7`
- 완료 경로는 `finish-task.sh` 실행이 유일하다. 수동 `.done` 금지.
- **명세와 실측이 다르면 임의 판단하지 말고 ANU 에 보고하라.**

★ **세션이 길어지면 중간 결과를 보고서에 먼저 적어라.** 오늘 4개 세션이 `status=ok` 로 끝나면서
보고서·PR 을 못 남겼다. 조사 결과를 수시로 기록하라.

## 완료 조건
① 우회 재현(또는 재현실패 명시) → 수정 → 차단 실증 → 오탐 없음 확인
② 계보 불일치 차단 실증 + 정상 통과 실증
**둘 다 "훅이 실제 발화했다"는 증거 포함**

## 보고
**PR 생성까지가 범위다. 머지는 ANU 가 한다.**
★ 훅 파일은 `~/.claude/` 아래라 git 저장소 밖일 수 있다. 그 경우 **PR 대신 변경 내역과
검증 로그를 보고서에 상세히 남기고 ANU 에 보고**하라.
`memory/reports/task-3017.md` 작성 후 표준 완료 콜백 등록. 콜백 프롬프트 **UTF-8 3900 bytes 이하**.
★ 봉투 첫 줄에 **"① 차단 실증 여부 / ② 차단 실증 여부"** 를 각각 담아라.