# task-2556 — Context Notes

## 1. 왜 본 task 가 필요한가

- **PR #106** 은 OWNER_TRIGGER_ONLY_CAPABILITY 의 **runner** (실행 로직) 를 main 에 반영했다.
  파일들: `owner_trigger_only.py`, `owner_trigger_audit.py`, `owner_trigger_decision.py`.
  이들은 "어떻게 `/gemini review` 댓글을 안전하게 1 회 작성하는가" 를 정의한다.

- **PR #107** 은 실제 운영 중 다음 갭을 실증했다:
  - PR open 후 30 분이 지나도 Gemini 1st review 가 안 도착하면 → 누가 owner_trigger 를
    호출하는가?
  - PR head 가 follow-up commit 후 stale 상태가 되면 → 누가 감지해 trigger 하는가?
  - 봇 session 이 종료되면 위 작업이 **완전 정지**.

- **결론**: capability 만으로는 부족하다. **자동 entry point** (scheduler) 가 필요하다.
  본 task = scheduler 구현.

## 2. 본 task 와 다른 PR 의 분리

- PR #106 (merged) — capability runner. 변경 0. 본 task 의 dependency.
- PR #107 (open) — 본 task 와 무관한 별개 PR. branch 변경 0. forbidden.
- 본 task — scheduler entry point. 5 신규 + 2 minimal patch. expected_files 1:1.

## 3. 핵심 설계 결정

### 결정 1: scheduler 는 단일 cycle 만 실행
회장 §9 "long polling 금지" 를 코드 게이트로 강제했다. `run_one_cycle` 안에 while True 0,
sleep loop 0. 다음 cycle 은 외부 cron / webhook 에서 본 메서드를 재호출.
→ 봇 session 종료 후에도 외부 트리거로 재진입 가능 (회장 §12).

### 결정 2: chat_notifications=0 강제
회장 §8 / 금지 #15 "scheduler 가 회장 chat 에 알림 노출 0".
- `SchedulerCycleResult.chat_notifications` 필드를 0 으로 hard-code.
- audit JSONL 의 모든 행에 `chat_notifications: 0` 강제 기록.
- test_pr107_pilot 테스트가 이 값을 어셀션.

### 결정 3: token boundary 2 단 검증
- `assert_scheduler_token_boundary(env)` — env dict 에서 forbidden 5 token 차단.
- runner 자체의 `assert_token_boundary` 가 호출 시점에서 한 번 더 검증.
- 빈 문자열 token, None, 누락 모두 fail-closed.

### 결정 4: same-head dedupe 는 audit 기반
- 별도 lockfile DB 만들지 않고 owner_trigger_audit 의 bounded reverse scan 재활용.
- `_has_active_trigger(pr, head)` 가 POSTED OR PENDING 을 보면 dedupe.
- scheduler 의 `_scheduler_lock` 은 별도 sidecar lock — concurrent scheduler instance 간
  serialization (POSIX advisory).

### 결정 5: minimal patch 원칙
- `owner_trigger_only.py` 에는 `invoke_from_scheduler` adapter + token guard 2 개만 추가.
- `merge_queue_executor.py` 에는 `auto_resume_after_fresh_evidence` 단일 진입점 1 개만 추가.
- 기존 코드 변경 0 — PR #106 의 capability 는 변형 없이 재사용.

## 4. 회장 §1~§12 1:1 trace

각 § 요구가 어느 코드/test 에 매핑되는지는 `plan.md` §4 참조.

## 5. 운영 시 사용 흐름 (post-merge)

1. 외부 cron (예: 15 분마다) 이 `python -m anu_v2.executor_scheduler` 형태의 스크립트
   호출 (본 task 는 그 진입점이 될 wrapper 를 제공하지 않는다 — 다른 task 책임).
2. wrapper 가 `gh pr list --state open --json ...` 호출 + `OwnerTriggerOnly` 인스턴스화
   + `MergeQueueExecutor` 인스턴스화.
3. wrapper 가 `ExecutorScheduler(...).run_one_cycle(env=...)` 호출.
4. 결과 SchedulerCycleResult 를 보고 wrapper 가 exit.
5. 다음 cron tick 에서 동일 호출 반복 (state persisted markers 로 재진입).

## 6. 회귀 보장

- 기존 anu_v2 262 tests 변경 0 PASS.
- 신규 39 + supporting 14 = 53 신규 tests 추가 PASS.
- 전체 315 PASS.

## 7. 의도된 한계

- 본 task 는 wrapper / cron 자체 설정은 만들지 않는다. cron / webhook 설정은 task-2557
  또는 별도 ops task 로 분리됨 (chain 정책 — 자동 +1 발행 X).
- `auto_resume_after_fresh_evidence` 는 fresh marker 만 생성. 실제 merge 까지 진행은
  task 봇의 책임 (capability scope 분리).
