# task-1176.1 완료 보고서

**작업**: 사용량 API 토큰 만료 문제 수정
**팀**: dev1-team (헤르메스)
**담당**: 불칸(백엔드), 아르고스(테스트)

---

## SCQA

**S**: task-1175.1에서 구현한 `GET /api/usage-status` API가 `~/.claude/.credentials-*.json` 백업 파일의 accessToken으로 OAuth Usage API를 호출하여 계정별 사용량을 표시한다. 활성 `.credentials.json`은 Claude CLI가 자동 갱신하므로 항상 유효하다.

**C**: 백업 credential 파일(drumband, jonghyuk)의 accessToken이 만료되어 Usage API가 401 Unauthorized를 반환한다. 또한 활성 `.credentials.json`이 결과에 포함되지 않아 현재 로그인된 계정의 사용량도 확인할 수 없다.

**Q**: 만료된 백업 토큰을 자동 갱신하고, 활성 계정도 사용량 표시에 포함할 수 있는가?

**A**: OAuth refresh token flow 구현으로 해결. `POST https://console.anthropic.com/v1/oauth/token` 엔드포인트에 `grant_type=refresh_token`으로 새 accessToken을 발급받고 credential 파일을 자동 업데이트. 활성 `.credentials.json`도 결과에 포함하여 `is_active: true`로 마킹. 추가 발견: Usage API의 `utilization` 값이 0-1 소수에서 0-100 퍼센트로 변경되어 `* 100` 변환 제거. pytest 19/19 통과, pyright 0 errors.

---

## 구현 내역

### 1. server.py — OAuth 토큰 자동 갱신 (불칸)

**모듈 레벨 상수 추가** (line 44-45):
- `_OAUTH_TOKEN_URL`: Anthropic OAuth 토큰 엔드포인트
- `_OAUTH_CLIENT_ID`: Claude Code PKCE OAuth client ID

**`_refresh_oauth_token()` 정적 메서드 추가** (line 3471-3493):
- refreshToken으로 새 accessToken 발급
- JSON POST 요청, 15초 타임아웃
- 실패 시 None 반환 (graceful)

**`_get_usage_status()` 전면 수정** (line 3495-3612):
- 활성 `.credentials.json` 포함 (`is_active: true`)
- `expiresAt` 기반 토큰 만료 판단 (5분 여유)
- 만료 시 refreshToken으로 자동 갱신 → credential 파일 업데이트
- 갱신 실패 시 `"error": "token_expired"` + 안내 메시지
- `utilization` 값 `* 100` 변환 제거 (API 형식 변경 대응)

### 2. test_usage_status.py — 테스트 확장 (아르고스)

기존 6개 테스트 수정 + 신규 6개 추가 = 총 12개:

**기존 수정**:
- `Path.exists` mock 추가 (활성 credential 파일 제어)
- `expiresAt` 명시적 설정 (미래 시각)
- mock utilization 값을 0-100 형식으로 변경

**신규 추가**:
- `test_active_credentials_included`: 활성 계정 결과 포함 확인
- `test_expired_token_auto_refresh_success`: 만료 토큰 자동 갱신 성공
- `test_expired_token_refresh_failure`: 갱신 실패 시 token_expired 에러
- `test_expired_token_no_refresh_token`: refreshToken 없을 때 처리
- `test_refresh_oauth_token_success`: 정적 메서드 단위 테스트
- `test_refresh_oauth_token_http_error`: HTTP 에러 시 None 반환

---

## 발견 이슈 및 해결

### 자체 해결 (4건)

1. **OAuth refresh 엔드포인트 미문서화** — Claude CLI 바이너리 분석으로 `platform.claude.com/v1/oauth/token` 엔드포인트와 `client_id=9d1c250a-e61b-44d9-88ed-5944d1962f5e` 파라미터 확인. JSON body + `grant_type=refresh_token` 형식.

2. **utilization 단위 변경** — task-1175.1 구현 당시 0-1 소수로 가정하여 `* 100` 적용했으나, 현재 API가 0-100 퍼센트를 직접 반환. `* 100` 제거로 수정. (server.py:3591-3596, 테스트 mock 데이터도 동기화)

3. **활성 계정 중복 표시** — jonghyuk이 활성 계정이면서 `.credentials-jonghyuk.json` 백업도 존재. 활성 계정은 `active` 키로, 백업은 `jonghyuk` 키로 별도 표시. 백업 토큰은 이미 회전되어 갱신 불가 → `token_expired` 표시. 기능적 영향 없음 (활성 계정의 사용량은 정상 표시).

4. **HTTPError hdrs 타입 경고** — Pyright가 `HTTPError(hdrs=None)` 타입 불일치 보고. `email.message.Message()` + `io.BytesIO(b"")` 사용으로 수정.

### 범위 외 미해결 (1건)

1. **refresh token 회전으로 인한 백업 무효화** — 활성 계정과 동일 계정의 백업 파일은 CLI가 토큰을 갱신할 때마다 이전 refresh token이 무효화됨. 근본 해결은 CLI가 백업 파일도 동시 갱신하는 로직이나, 이는 Claude CLI 내부 로직으로 범위 외.

---

## 산출물

- `/home/jay/workspace/dashboard/server.py` (수정: 상수 추가 + _refresh_oauth_token 메서드 + _get_usage_status 전면 수정 + utilization 단위 수정)
- `/home/jay/workspace/dashboard/tests/test_usage_status.py` (수정: 기존 6개 테스트 보강 + 신규 6개 테스트 추가)
- `/home/jay/.claude/.credentials-drumband.json` (갱신: 리프레시 토큰으로 새 accessToken 발급)
- `/home/jay/workspace/memory/reports/task-1176.1.md` (본 보고서)

---

## 테스트 결과

- pytest test_usage_status.py: 12/12 통과 (0.40s)
- pytest test_server.py: 7/7 통과 (0.22s) — 회귀 없음
- 전체: 19/19 통과 (0.63s)
- pyright server.py: 0 errors, 0 warnings
- 실제 API 호출 검증: active 51%/31%, drumband 0%/100%, jonghyuk token_expired

---

## QC 자동 검증 결과

- test_runner: PASS (pytest 7 passed in 0.22s)
- pyright_check: PASS (0 errors, 0 warnings)
- file_check: PASS (server.py 203,417B, test_usage_status.py 15,097B)
- data_integrity: PASS
- spec_compliance: PASS
- style_check: WARN (black/isort 포맷팅 — 기능 무관)
- api_health/tdd_check/schema_contract/scope_check: SKIP
