# BAT-007 Design - Status: confirmed - Batch: BAT-007 ## Architecture Summary - REQ-022: daemon CLI의 `--deactivate` 분기에 신규 service 메서드(`revoke_tokens_for_daemon`) 호출을 추가한다. 해당 메서드는 repository의 `revoke_tokens_by_daemon_id`를 통해 `UPDATE auth_tokens SET revoked_at=NOW() WHERE daemon_id=? AND revoked_at IS NULL`을 실행한다. - REQ-023: service_tokens CLI에 `migrate_null_daemon` 서브커맨드를 추가한다. `--dry-run`만 허용하며 실제 변경 없이 inventory + 매핑 후보를 출력한다. - 두 변경 모두 신규 repository 메서드 → service 헬퍼 → CLI 연결 패턴으로 일관된다. ## Changed Areas - Docs: - 없음 (baseline 문서 갱신은 REQ-019~021/024 별도 배치) - Modules: - `app/repositories/portfolio_repository.py` — `revoke_tokens_by_daemon_id(daemon_id: str) -> int` 추상 메서드 (REQ-022); `list_service_tokens_with_null_daemon() -> list[AuthToken]` 추상 메서드 (REQ-023) - `app/repositories/postgres.py` — 두 메서드 구현 - `app/services/auth_service.py` — `revoke_tokens_for_daemon(daemon_id: str) -> int` (REQ-022); `list_null_daemon_service_tokens() -> list[AuthToken]`, `find_candidate_daemons_for_user(user_id: str) -> list[Daemon]` (REQ-023) - `app/cli/commands/daemon.py` — `update_daemon` --deactivate 분기에 revoke 호출 추가 (REQ-022) - `app/cli/commands/service_tokens.py` — `migrate_null_daemon` 서브커맨드 추가 (REQ-023) - Tests: - `tests/test_daemon_registry.py` — REQ-022 AC-1~4 회귀 추가 (`_DaemonRepo`에 `revoke_tokens_by_daemon_id` 추가) - `tests/test_null_daemon_migration.py` — 신규, FakeRepo 기반 REQ-023 AC-1~5 ## Key Decisions 1. **REQ-022 CLI 호출 패턴**: daemon.py `_with_service`는 callback에 `(repository, app_context)`를 전달한다. `repo.update_daemon(...)` 후 `repo.revoke_tokens_by_daemon_id(daemon_id)`를 순차 호출하여 deactivate + revoke를 처리. `AuthService.revoke_tokens_for_daemon`은 서비스 계층 테스트 목적으로 추가. 2. **REQ-022 --force 옵션 추가**: `update_daemon` 명령에 `force: bool = typer.Option(False, "--force")` 추가. `--deactivate`와 함께 사용 시 `--force` 없으면 확인 프롬프트 표시. 자동 revoke는 `--force` 여부와 무관하게 항상 실행. 3. **REQ-022 회피 옵션 없음**: `--keep-tokens` 옵션 미노출 (dcy-004 OQ-3 결정 — 정책 일관성). deactivation이면 항상 revoke. 4. **REQ-022 재활성화 시 자동 복구 없음**: `--activate` 분기에서는 `revoke_tokens_by_daemon_id` 호출 없음. 이미 revoke된 token은 운영자가 신규 발급해야 함. 5. **REQ-023 dry-run 전용 설계**: `--dry-run` 없이 실행 시 `typer.Exit(code=1)` + 수동 rebind/revoke 안내 메시지. 자동 변경 코드 없음. 6. **REQ-023 매핑 후보 조회**: service_tokens.py `_with_service`는 callback에 `(service, app_context)` 전달. `service.list_null_daemon_service_tokens()`와 `service.find_candidate_daemons_for_user(user_id)`를 사용해 출력 구성. 7. **revoke_tokens_by_daemon_id 반환값**: revoke된 token 수(`int`) 반환 — AC-3(no-op, 0 반환) 검증에 사용. ## Edge Cases - REQ-022: daemon에 바인딩된 token이 0건인 경우 → `revoke_tokens_by_daemon_id` 0 반환, 정상 종료 (AC-3) - REQ-022: `--activate` 재활성화 시 token revoke 호출 안 함 (분기 조건: `deactivate=True`일 때만) - REQ-023: NULL daemon active token이 0건인 경우 → "no NULL daemon tokens" 메시지 + exit 0 (AC-2) - REQ-023: 매핑 후보 daemon이 0건인 token → dry-run 출력에 별도 경고 표시 ## Architecture Impact - none (runtime-flows, project-structure 변경 없음 — REQ-019 배치에서 docs 반영) ## Reference Doc Update Plan - project-structure.md: none (이 배치에서 변경 없음) - runtime-flows.md: none (이 배치에서 변경 없음)