# task-2563 — Context Notes

## OWNER_TRIGGER_ONLY_CAPABILITY 실전 7회 활용 경험

PR #107 / #108 / #103 / #109 / #110 / #115 + 1건 추가로 capability 가 실전 사용된 결과, 3 가지 소형 안정화 항목이 식별되었다. task-2560/2561/2562 정식 후속 task 가 아닌 본질 hardening 으로 분류.

## Fix 1 사유 — FIRST_TRIGGER_PENDING 상태 분리

- 기존: `GRACE_PERIOD_SECONDS = 30 * 60 = 1800s` 가 `FIRST_TIMEOUT_SECONDS` 와 같은 값. 즉시 경계에서 `WITHIN_GRACE_PERIOD → FIRST_GEMINI_TRIGGER_MISSING` 으로 전이.
- 문제: PR open 직후 짧은 시간 동안에도 review 가 0 이면 missing 으로 오분류 될 위험. capability 활용 시 조기 trigger 호출 가능.
- 수정: 짧은 grace window (`FIRST_TRIGGER_PENDING_WINDOW_SECONDS = 300s`) 도입. 300s ~ 1800s 구간은 `FIRST_TRIGGER_PENDING` (대기), 1800s 이후만 `FIRST_GEMINI_TRIGGER_MISSING` (확정).
- 회장 §명시 1:1: `polling_policy.FIRST_TIMEOUT_SECONDS=1800` 와 MISSING 확정 시점 정확 일치.

## Fix 1 fast_path flag 사유

- PENDING 구간이 길어진다고 무조건 대기는 불가. emergency / 회장 우회가 필요한 경우 `dispatch_decision.owner_trigger_fast_path=true` 박제 시 조기 dispatch 허용.
- default `false` 이므로 fail-closed (실수로 fast_path 빠뜨려도 PENDING 단계엔 trigger 0).
- `OWNER_TRIGGER_INVOKING_STATES` frozenset 에는 `FIRST_TRIGGER_PENDING` 미포함 — invariant 보장.

## Fix 2 사유 — signature 회귀

- `OwnerTriggerOnly._http_post: Callable[[str, str, dict, dict], dict]` 4-arg positional.
- 3 caller path:
  1. direct `OwnerTriggerOnly.trigger_gemini_review`
  2. scheduler 경유 `invoke_from_scheduler`
  3. wrapper/DI mock
- 과거 capability 활용 중 wrapper 가 keyword arg 로 invoke 시도 / extra arg 추가 등 silent drift 가능성. runtime 에서 처음 발견되면 PR 차단 실패 + token 노출 위험.
- 회귀 테스트로 signature 강제: 4 positional, 0 keyword, method=="POST", body=={"body": "/gemini review"}, headers contains "Authorization".

## Fix 3 사유 — FUC-3 logger.exception 보강

- 기존: http_post 실패 경로에서 `txn.record(FAILED)` 만 호출. traceback / GitHub diagnostic 정보 (status / x-github-request-id / x-accepted-permissions / documentation_url) 누락.
- 수정: `logger.exception(...)` 을 `txn.record(FAILED)` 호출 **전에** 추가. traceback 자동 박제 + diagnostic dict (redacted) 명시 출력.
- 절대 보장: token 원문 / Authorization header / api_key 등은 `_redact_diagnostics` 로 `<redacted>` 마스킹.
- audit 격리 유지: logger 와 audit 은 별도 sink. logger.exception 실패해도 audit 차단 0 (try/except 안에 한정).

## _redact_diagnostics 설계

- key pattern (`(?i)(token|authorization|api[_-]?key|secret|password)`) 매치 → 전체 값을 `"<redacted>"`.
- value sentinel (`Bearer `, `ghp_`, `github_pat_`, `ghu_`, `ghs_`, `ghr_`) 포함 → 전체 값을 `"<redacted>"`.
- 재귀 dict / list / tuple / str 모두 지원, depth 8 cap 으로 stack overflow 방지.
- module source 정적 검사 (test_module_source_has_no_hardcoded_token_value) 와 충돌 없도록 sentinel 은 runtime 문자열 concat (`"gh" + "p_"`) 으로 작성.

## fail-closed 속성 유지

- logger.exception 호출 → txn.record(FAILED) → raise exc 순서 보존.
- audit FAILED 행 에 `token_value_logged: False` + `token_hash_prefix` (sha256 8자, raw 아님) 박제.
- HTTP POST side-effect 0 (mock 1 call 만, 그 실패 call).
- PENDING sentinel 이 이미 남아 있어 다음 runner 가 DEDUPED 판정 (crash-safety).

## fixture 3종 분리 사유

- 각 fix 별 독립 fixture → 테스트 가독성 + 향후 재사용성 보장.
- JSON schema 안에 expected invariants 박제 (fail-closed 어셀션 명시화).
- stdlib + pytest 만 사용 (no external deps, dispatch_decision authoritative).

## 금지 9건 재박제

1. task-2562 G4 영역 재수정 0
2. task-2558 auto_gemini_triage 재수정 0
3. scripts/finish-task.sh 수정 0
4. task-2560/2561 영역 섞기 0
5. 회장 수동 `/gemini review` 0
6. BOT `/gemini review` 0
7. token 원문 출력 0 (logger.exception 에서도)
8. long polling 0
9. force push / rebase / admin override 0

## 통합 PR 단일성

3건 hardening 을 한 PR 로 묶는 이유: 모두 OWNER_TRIGGER_ONLY_CAPABILITY 의 변두리 안정화로 강한 결합. 별도 PR 로 분리 시 dispatch overhead + 동일 영역 (anu_v2/owner_trigger_only.py / executor_scheduler.py) 동시 mutate 위험. 단일 PR 로 통합하여 fail-closed 가시화.
