# task-2493 — task-2481R-preflight: GitHub App bot-authored PR 권한 read-only audit

- 작업 유형: read-only preflight audit / 권한 검증
- 작업 레벨: Lv.1 (read-only, 코드/PR 변경 0)
- 팀: dev3-team (다그다)
- 일시: 2026-05-08

## SCQA

### Situation
task-2481 recovery bundle (R+1, R+2) 발행을 앞두고, GitHub App `jeon-jonghyuk-taskctl-bot[bot]`이 bot-authored PR을 실제로 생성/머지할 수 있는 권한을 보유했는지 사전 검증 필요. task-2483에서 BOT 토큰 갱신 사이클이 LIVE 검증됐지만, 권한 자체에 대한 정식 read-only audit은 미수행.

### Complication
1차 감사(루)는 `x-accepted-github-permissions` 헤더와 `/installation/repositories` 의 `permissions` 필드를 근거로 NO-GO 판정. 그러나 이 둘은 각각 (a) 엔드포인트 요구 권한, (b) collaborator 관점 권한이며 *App installation 보유 권한*과 다르다. 잘못된 해석에 기반한 NO-GO는 R+1 발행을 부당하게 차단할 수 있음.

### Question
GitHub App bot이 R+1 발행에 필요한 권한을 실제로 보유하는가? `pull_requests:write` / `contents:write` / `checks:read`를 정확히 어떤 evidence로 검증해야 하는가?

### Answer
2차 정정 감사로 다음 결론 도출:
- **pull_requests:write — PASS (실증)**: bot authored PR #33/34/35 (user.type=Bot, login=`jeon-jonghyuk-taskctl-bot[bot]`, merged=True) 존재가 결정적 증거
- **contents:write — INDETERMINATE**: bot authored PR들의 head commits가 전부 사람(JonghyukJeon) authored — bot push 직접 증거 없음. 단 1차 감사의 `push:false` 근거는 collaborator field 오해석으로 무효
- **checks — UNVERIFIED**: GET /check-suites/check-runs 404 (read 권한 미부여 가능)

**최종 판정: CONDITIONAL GO** — PR creation은 GO, branch push는 실제 시도로 검증 필요.

## 작업 결과

### Step 1~7 read-only 검증 결과

| Step | 항목 | 판정 | 핵심 evidence |
|------|------|------|--------------|
| 1 | BOT_GITHUB_TOKEN 유효성 | PASS | `/rate_limit` 200, core remaining 4996, `/installation/repositories` 200 |
| 2 | viewer.login | PASS | graphql viewer = `jeon-jonghyuk-taskctl-bot[bot]` |
| 3 | App permissions | PARTIAL-PASS | pull_requests:write 실증 / contents:write 미실증 / checks 미부여 추정 |
| 4 | gh auth status | PASS | `✓ Logged in to github.com account jeon-jonghyuk-taskctl-bot[bot] (GH_TOKEN)` |
| 5 | branch push 권한 | INDETERMINATE | `push:false`는 collaborator field 오해석 무효. 실제 push 증거/반증 모두 없음 |
| 6 | PR creation 가능성 | PASS | PR #33/34/35 (bot authored, merged=True) 존재 = POST /pulls 실증 |
| 7 | 부족 권한 | 1건 확정(checks) + 1건 불확실(contents:write) | 아래 표 참조 |

### Step 7 — 권한 격차 정리

| 권한 | 현재 | R+1 필요성 | 해소 방법 |
|------|------|-----------|-----------|
| pull_requests:write | 보유 (실증) | 필수 | — |
| contents:write | 불확실 | 필수 | 실제 bot push 시도로 확인 또는 GitHub App settings에서 확인 |
| checks:read | 미부여 추정 | 선택 (CI gate에 따라 필수일 수 있음) | App Settings → Repository permissions → Checks: Read |

### 부수 발견사항
- 토큰 만료 로깅 갭: `bot-token-refresh.jsonl` 마지막 기록 expires_at = 2026-05-07T17:09:33Z이나 현재(2026-05-08) 토큰 응답 정상. systemd 갱신은 동작 중이나 jsonl 기록이 일부 누락된 것으로 보임. task-2481R/R+1과는 별개의 운영 갭으로 별도 task 권장.
- `refresh_bot_token.py`는 토큰 발급 시 `permissions` 파라미터를 빈 본문으로 호출 → App 전체 권한 상속. 권한 제한 없음.

### R+1 발행 GO/NO-GO 최종 판정

**CONDITIONAL GO**

- 사유:
  - PR 생성 기능은 실증된 `pull_requests:write`로 GO
  - branch push (`contents:write`)는 직접 evidence 없음. R+1 실행 단계에서 bot이 실제 push를 시도해야 확정 검증
  - 1차 감사의 NO-GO 핵심 근거 2개(헤더 오해석 + collaborator field 오해석) 모두 무효화됨
- 권장 조치:
  1. R+1을 즉시 차단할 사유는 없음 — 진행 가능
  2. R+1 첫 단계에서 bot push 결과(성공/403)를 확인하고, 403이면 즉시 GitHub App settings → Contents: Read and write로 상향 후 재시도
  3. checks:read는 R+1 실행 중 CI gate가 요구하면 동시 상향

## 합격 조건 점검

| 조건 | 결과 |
|------|------|
| 7개 항목 read-only 검증 결과 보고 | ✅ |
| 부족 권한 명확히 명시 | ✅ (checks 확정 + contents:write 불확실) |
| 코드 변경 0 | ✅ (audit-result.md, audit-evidence.md, 본 보고서만 작성) |
| PR 생성 0 | ✅ |
| admin override 0 | ✅ |
| 토큰 원문 노출 0 | ✅ (sha256 prefix 12자만 기록) |
| R+1 발행 GO/NO-GO 최종 판정 | ✅ CONDITIONAL GO |

## 산출물 / affected_files

본 task는 read-only audit이므로 코드 변경 0건. 산출 문서 4건 (모두 신규 작성):

- `/home/jay/workspace/memory/plans/tasks/task-2493/plan.md`
- `/home/jay/workspace/memory/plans/tasks/task-2493/context-notes.md`
- `/home/jay/workspace/memory/plans/tasks/task-2493/checklist.md`
- `/home/jay/workspace/memory/plans/tasks/task-2493/audit-result.md` (1차 + 정정 결과)
- `/home/jay/workspace/memory/plans/tasks/task-2493/audit-evidence.md` (재검증 raw evidence)
- `/home/jay/workspace/memory/reports/task-2493.md` (본 보고서)

## 수정 파일별 검증 상태

| 파일 | 종류 | 검증 |
|------|------|------|
| memory/plans/tasks/task-2493/plan.md | 신규 (3문서) | 작성 완료, status=completed |
| memory/plans/tasks/task-2493/context-notes.md | 신규 (3문서) | 작성 완료 |
| memory/plans/tasks/task-2493/checklist.md | 신규 (3문서) | 7단계 + 금지사항 + 산출물 모두 체크 |
| memory/plans/tasks/task-2493/audit-result.md | 신규 (1차+정정) | 정정 이력 박제, CONDITIONAL GO 판정 |
| memory/plans/tasks/task-2493/audit-evidence.md | 신규 (raw evidence) | 8 commands raw response 박제 |
| memory/reports/task-2493.md | 신규 (보고서) | SCQA 4섹션 + 7단계 결과 + L1 + 셀프QC |

코드 파일(.py/.yml/.sh/.github/) 수정 0건.

## L1 스모크테스트 결과

- 서버 재시작: 해당없음 (read-only API 검증 task — 서버 변경 0)
- API 응답 확인: 해당 — `gh api /rate_limit` 200, `/installation/repositories` 200, `/repos/.../pulls?state=open` 200 (raw evidence는 audit-evidence.md 참조)
- 스크린샷: 해당없음 (UI 변경 0)

## forbidden_actions 위반

0건
- any_code_modification: 0
- any_pr_creation: 0
- real_bot_pr_attempt: 0
- real_merge_attempt: 0
- real_branch_push: 0
- admin_override: 0
- audit_jsonl_real_write: 0 (read만)
- token_value_logging: 0 (sha256 prefix만)

## 모델 사용 기록

| 팀원 | 작업 | 모델 | 정당성 |
|------|------|------|--------|
| 루(Lugh) 1차 | 7단계 GitHub API read-only 검증 | sonnet | API 호출 + JSON 분석 — 코딩 task로 sonnet 적정 |
| 루(Lugh) 2차 | 헤더 오해석 정정 + 추가 evidence 수집 | sonnet | 동일 |

팀장(다그다, Opus) 직접 코딩 0건. 위임/검토/통합만 수행.

## 셀프 QC

- [x] 코드 변경 0 (read-only audit)
- [x] 보고서 SCQA 4섹션 포함
- [x] 토큰 원문 노출 0 (sha256 prefix만)
- [x] 7단계 결과 PASS/FAIL/N/A 판정 명시
- [x] R+1 발행 GO/NO-GO 최종 판정 명시
- [x] forbidden_actions 위반 0
- [x] 부수 발견사항(jsonl logging 갭) 별도 항목으로 분리 — 본 task 범위 외
- [x] 1차 감사 NO-GO 오류 정정 이력 audit-result.md에 박제
