# task-2563 — OWNER_TRIGGER_ONLY_CAPABILITY hardening plan

회장 §명시 2026-05-13 / dev6 페룬 / Lv.2~3 capability-hardening / 3건 통합 PR.

## 목표

OWNER_TRIGGER_ONLY_CAPABILITY 실전 활용 7회 경험 기반 소형 안정화 3건을 하나의 PR로 통합 hardening 한다. 기존 인터페이스 / signature / fail-closed 속성은 변경 0.

## 결정 박제 (회장 §명시 1:1)

| 항목 | 결정 |
|---|---|
| task ID | task-2563 |
| 통합 hardening 수 | 3건 (FIRST_TRIGGER_PENDING 분리 + signature 회귀 + FUC-3 logger.exception) |
| owner_trigger_fast_path | default `false` (PENDING 상태 dispatch 차단) |
| FIRST_TRIGGER_PENDING_WINDOW_SECONDS | 300s (5분, "PR open 직후 짧은 시간") |
| FIRST_TIMEOUT_SECONDS 1:1 | 1800s (polling_policy 와 정확 일치) |
| 팀 | dev6 페룬 |
| 본질 | capability 활용 7회 학습 → 통합 hardening |

## 구현 단계

1. `anu_v2/idle_pr_diagnoser.py` 수정 — `STATE_FIRST_TRIGGER_PENDING` + `FIRST_TRIGGER_PENDING_WINDOW_SECONDS` (default 300s) 추가, 9-state 머신으로 확장.
2. `anu_v2/executor_scheduler.py` 수정 — `ACTION_FIRST_TRIGGER_PENDING_SKIP` + `_load_fast_path_flag` helper, PENDING 상태 분기 추가.
3. `anu_v2/owner_trigger_only.py` 수정 — `import logging` + `logger` + `_redact_diagnostics` + `_collect_http_diagnostics` 추가, http_post 예외 경로에 `logger.exception` 호출 추가. signature 변경 0.
4. `anu_v2/tests/test_owner_trigger_invocation_hardening_2563.py` 신규 — 20 회귀 테스트 (3 hardening 1:1 커버).
5. `anu_v2/fixtures/*.json` 신규 3종.
6. 3문서 + report + dispatch-decision.json.

## state machine 분리 1:1

| elapsed | state | scheduler action |
|---|---|---|
| 0 ~ 300s | WITHIN_GRACE_PERIOD | WITHIN_GRACE_PERIOD_SKIP |
| 300 ~ 1800s + reviews 0 | FIRST_TRIGGER_PENDING | FIRST_TRIGGER_PENDING_SKIP (fast_path=false) / OWNER_TRIGGER_DISPATCHED (fast_path=true) |
| ≥ 1800s + reviews 0 | FIRST_GEMINI_TRIGGER_MISSING | OWNER_TRIGGER_DISPATCHED |

## signature 회귀 1:1

`OwnerTriggerOnly._http_post: Callable[[str, str, dict, dict], dict]` — 3 caller path 모두 동일 positional 4-arg invoke.

1. direct: `OwnerTriggerOnly.trigger_gemini_review(...)`
2. scheduler: `invoke_from_scheduler(runner, ...)`
3. wrapper/DI mock: custom callable wrapping http_post

## logger.exception 보강 1:1

```python
try:
    self._http_post("POST", path, body_payload, headers)
except Exception as exc:
    # task-2563 FUC-3
    http_diag = _collect_http_diagnostics(exc)
    redacted_diag = _redact_diagnostics(http_diag)
    logger.exception("owner_trigger http_post FAILED ... diagnostic=%s", ..., redacted_diag)
    txn.record({..., "result": RESULT_FAILED, "token_value_logged": False, ...})
    raise exc
```

## 회귀 어셀션

- `python3 -m pytest anu_v2/tests/test_owner_trigger_invocation_hardening_2563.py -v` — 20 PASS.
- `python3 -m pytest anu_v2/tests/test_owner_trigger_only_2554.py anu_v2/tests/test_executor_first_gemini_trigger_missing.py anu_v2/tests/test_executor_scheduler_per_pr_isolation.py` — 28 PASS (semantic 변경 반영 후).
- `python3 -m py_compile anu_v2/owner_trigger_only.py anu_v2/idle_pr_diagnoser.py anu_v2/executor_scheduler.py` — syntax OK.

## 완료 조건

dispatch-decision.json `completion_conditions_11` 1:1.
