---
task_id: task-2483
type: context-notes
scope: task
status: completed
created: 2026-05-07
updated: 2026-05-07
---

# 맥락노트: task-2483

## 차단 evidence (시작 시점)
- `git -C /home/jay/workspace log --all -- scripts/refresh_bot_token.py` → 0건. 즉 git history에 한 번도 commit된 적 없음. 회장 박제 메모리 §3에 따라 anu-direct에서 작성됐던 파일이 workspace 외부에 머무르다 유실 (회장 명시 "이전 anu-direct 작성, 유실").
- `journalctl --user -u refresh-bot-token.service -n 20` → "Errno 2: No such file or directory" 실패 21회 이상 반복 (5분 간격 RestartSec 누적). systemd unit 자체는 active(elapsed) + ExecStart 경로 정상이지만 타깃 파일 부재로 status=2 INVALIDARGUMENT 종료.
- `.env.keys` BOT_GITHUB_TOKEN=ghs_T0jj... 존재하나 graphql 401 (task-2481 layer 5 차단 사유로 박제됨).

## 핵심 결정 + 근거

### 결정 1: 재작성 (복원 X)
- **근거**: git log에 0건 → 복원 불가. 회장 박제 메모리(`feedback_github_app_key_location_260507.md`)에 동작 명세가 4단계로 박제됨. 명세를 그대로 따라 작성.
- **대안**: anu-direct 세션 디렉토리 검색. → 시간 낭비 위험. 명세가 박제돼 있으므로 재작성 비용 < 검색 비용.

### 결정 2: PyJWT + cryptography
- **근거**: 시스템에 이미 설치 (PyJWT 2.11.0, cryptography 46.0.5). pip install 불필요.
- **대안**: `python-jose` → 추가 의존. 거부.

### 결정 3: `.env.keys` in-place 갱신 (line replace)
- **근거**: systemd unit `ReadWritePaths=/home/jay/workspace/.env.keys`로 이미 권한 부여. 기존 다른 키 보존 필요(BOT_GITHUB_APP_ID 등) → 파일 전체 재작성 X, BOT_GITHUB_TOKEN 라인만 in-place.
- **구현**: 파일 line-by-line 읽고 `BOT_GITHUB_TOKEN=` prefix 라인 교체. 미존재 시 EOF append. atomic write (`tmpfile + os.replace`)로 부분 쓰기 위험 차단.

### 결정 4: PEM 메인 부재 → 백업 자동 fallback
- 메인: `/home/jay/.secrets/jeon-jonghyuk-taskctl-bot.2026-05-05.private-key.pem`
- 백업: `/home/jay/workspace/.secrets/jeon-jonghyuk-taskctl-bot.2026-05-05.private-key.pem`
- **근거**: 회장 박제 메모리 §"영구 보존 위치 (이중 백업)" 명시.
- env `BOT_GITHUB_PRIVATE_KEY_PATH` 우선 사용 → 미존재 시 백업 fallback → 둘 다 부재면 fail-closed.

### 결정 5: audit log = sha256 hash + expires_at + status (토큰 원문 절대 X)
- **근거**: forbidden_actions에 `token_secret_logging` 명시. 회장 박제 메모리 §"절대 금지" 동일.
- **구현**: hashlib.sha256(token).hexdigest()[:16] + expires_at + status(`refreshed`/`refresh_rejected`/`pem_missing`/`api_error`) jsonl append-only.

### 결정 6: fail-closed 정책
- API 401/timeout/JSON parse 실패 → 구 토큰 보존 (.env.keys 미수정), audit `refresh_rejected` 기록, exit 1.
- **근거**: 회장 명시 "API 401 시 fail-closed".

## 3 Step Why 자문

1. **Why this design?** → BOT_GITHUB_TOKEN은 1시간 만료. 자동 머지 체인(bot-authored PR + Gemini auto-review + no-admin merge)이 토큰 의존. 50분 systemd timer가 만료 직전 자동 갱신 필요.
2. **Why GitHub App JWT 방식이 최선인가?** → 회장 명시 forbidden: 개인 PAT 영구 대체 X. GitHub App만이 (a) bot-authored PR identity 제공 (b) installation token 머신 자동 갱신 (c) 권한 최소화 가능. PAT는 personal scope 노출 + 만료 추적 불가.
3. **Why systemd timer 50분 vs 다른 대안?** → cron보다 (a) `OnBootSec` 부팅 자동 시작 (b) `Persistent=true` missed run catch-up (c) `--user` 권한 격리 (d) journal 통합. dispatch.py 호출 시점 발급은 race + 토큰 1시간 내 다중 갱신 비용 + dispatch 실패 시 후속 차단.

A-B-C 일관: **자동 머지 체인 자동화 ⇐ 봇 토큰 자동 갱신 ⇐ systemd timer + GitHub App**. 일관성 OK.

## 참조 문서
- `~/.claude/.../system_bot_orchestration_blueprint_260506.md` §11 merge_policy
- `~/.claude/.../feedback_github_app_key_location_260507.md` (영구 명세)
- `~/.claude/.../feedback_dogfooding_pending_classification_260507.md` (task-2481 conditions)
- `memory/events/task-2481.dogfooding-pending.conditions` (조건 1 = 본 task)

## 현재 git 상태 (작업 시작 시점)
- main HEAD: `1f96ddcd Merge pull request #36 from Jeon-Jonghyuk/task/task-2471-dev2`
- worktree: `/home/jay/workspace/.worktrees/task-2483-dev3` branch `task/task-2483-dev3`
- baseline: `7d176191 [task-2479] 헤르메스: CI failure visibility 보고서` (dev1 진행중 위에서 분기)

## sanitize 게이트
- 외부 AI(Codex/Gemini) 호출 전 `utils.sanitize_gate.sanitize_text` 적용 필수.
- 본 task의 affected_files에는 PEM 키, 토큰 값이 직접 등장하지 X (env path / hash만 처리). 코드/문서 자체는 PII 없음.
