# task-2556 — executor 자동 entry point 구현 (계획서)

**lineage**: task-2554+2 → task-2555 (지점간 통신) → **task-2556** (entry point)
**선행**: PR #106 (cd1aeba418) main 반영, OWNER_TRIGGER_ONLY_CAPABILITY 사용 가능
**baseline branch**: origin/main @ cd1aeba418
**workspace**: .worktrees/task-2556-dev5

## 1. 본질 (회장 §명시 2026-05-12 KST)

PR #106 의 capability runner 는 코드에 박제됐지만, idle PR 을 **자동 감지해 runner 를
호출하는 daemon/scheduler entry point** 가 없다. 매번 task 봇이 직접 `OwnerTriggerOnly`
인스턴스를 만들어 호출해야 했고, 봇 session 이 종료되면 `FIRST_GEMINI_TRIGGER_MISSING` /
`GEMINI_STALE_ON_HEAD` 자동 처리가 끊겼다 (PR #107 가 실증).

본 task = **자동 entry point 구현 완성**. capability 자동 활성화.

## 2. 아키텍처

```
[ external cron / webhook ]
            │
            ▼
   ExecutorScheduler.run_one_cycle()   ← anu_v2/executor_scheduler.py (NEW)
            │
            ├─ snapshot_provider()  ← injected (gh pr list 정규화)
            ├─ IdlePRDiagnoser.diagnose_all()   ← anu_v2/idle_pr_diagnoser.py (NEW)
            │       └─ 8 states (WITHIN_GRACE / FIRST_GEMINI_TRIGGER_MISSING /
            │                    GEMINI_STALE_ON_HEAD / GEMINI_FRESH_ON_HEAD /
            │                    CI_FAILED / MISSING_TASK_ID / UNKNOWN / OWNER_TRIGGER_REQ)
            │
            ├─ MergeQueueExecutor.emit_owner_trigger_decision()  ← decision.json
            ├─ invoke_from_scheduler(OwnerTriggerOnly, ...)  ← /gemini review POST
            ├─ MergeQueueExecutor.record_owner_trigger_outcome()  ← marker
            └─ MergeQueueExecutor.auto_resume_after_fresh_evidence()  ← minimal patch (NEW)

   PollingPolicy   ← anu_v2/polling_policy.py (NEW)
       (long polling 금지 코드 게이트: 15min / 30min / 1 recheck / exit)
```

## 3. 모듈별 책임

### 신규 3
- `anu_v2/polling_policy.py` — long polling 코드 게이트 (회장 §9).
- `anu_v2/idle_pr_diagnoser.py` — 8-state state machine 분류 (회장 §2~§4).
- `anu_v2/executor_scheduler.py` — entry point + dispatch + audit (회장 §1/§5~§8/§10/§12).

### minimal patch 2
- `anu_v2/owner_trigger_only.py` — `invoke_from_scheduler` adapter + `assert_scheduler_token_boundary`.
- `anu_v2/merge_queue_executor.py` — `auto_resume_after_fresh_evidence` 단일 진입점.

## 4. 회장 12 필수 1:1 매핑

| § | 요구 | 구현 위치 |
|---|---|---|
| 1 | OPEN PR idle scan | ExecutorScheduler.run_one_cycle / snapshot_provider DI |
| 2 | task_id/head_sha/CI/Gemini evidence 진단 | IdlePRDiagnoser.diagnose |
| 3 | FIRST_GEMINI_TRIGGER_MISSING 감지 (30min) | IdlePRDiagnoser §3 분기 |
| 4 | GEMINI_STALE_ON_HEAD 감지 | IdlePRDiagnoser §4 분기 |
| 5 | OWNER_TRIGGER_REQUIRED decision write | emit_owner_trigger_decision (기존 capability 활용) |
| 6 | runner 자동 호출 | invoke_from_scheduler adapter |
| 7 | decision.json + audit.jsonl + markers | record_owner_trigger_outcome + scheduler audit |
| 8 | scheduled/event-driven recheck (chat 노출 0) | run_one_cycle 단일 cycle, chat_notifications=0 |
| 9 | long polling 금지 | polling_policy.py 코드 게이트 (15min/30min/1 recheck) |
| 10 | duplicate same-head dedupe | audit `_has_active_trigger` + scheduler `_scheduler_lock` (fcntl) |
| 11 | token boundary | assert_scheduler_token_boundary |
| 12 | bot session 종료 후 재진입 | audit JSONL + marker 영구 박제, 외부 cron 재호출 |

## 5. 금지 16건 어셀션

회장 § 5건 + doctrine 11건 모두 코드 게이트로 강제:
- markdown only X → 5 코드 + 6 fixture + 7 test.
- memory marker only X → audit/marker + 실제 코드 변경.
- 수동 /gemini review X → 본 task 자체 PR 도 capability 로 자동 dispatch 가능.
- close/reopen X → owner_trigger_only.close/reopen 가 raise.
- force/rebase/admin X → forbidden_paths assert.
- bot /gemini review X → endpoint hard-block.
- same-PR push X → audit `_has_active_trigger` dedupe.
- chain +1 자동 발행 X → 본 plan 에 없음.
- long polling X → polling_policy 가 raise.
- worktree 위반 X → .worktrees/task-2556-dev5 격리.
- owner PAT live X → BOT_GITHUB_TOKEN 만 사용 (merge path), OWNER_GEMINI_TRIGGER_TOKEN 만 사용 (trigger path).
- GH_TOKEN fallback X → assert_scheduler_token_boundary.
- 다른 PR 혼입 X → expected_files 1:1.
- empty commit X → 신규 5 + patch 2 = 변경 있음.
- scheduler chat 노출 X → chat_notifications=0 어셀션.
- OWNER token 외 용도 X → owner_trigger 만 OWNER token 사용.

## 6. expected_files (1:1)

### code (5)
1. `anu_v2/executor_scheduler.py` (신규)
2. `anu_v2/idle_pr_diagnoser.py` (신규)
3. `anu_v2/merge_queue_executor.py` (minimal patch — `auto_resume_after_fresh_evidence` 추가)
4. `anu_v2/owner_trigger_only.py` (minimal patch — `invoke_from_scheduler` + `assert_scheduler_token_boundary` 추가)
5. `anu_v2/polling_policy.py` (신규)

### tests (6 + 1 supporting)
6. `anu_v2/tests/test_executor_scheduler_pr107_pilot.py`
7. `anu_v2/tests/test_executor_first_gemini_trigger_missing.py`
8. `anu_v2/tests/test_executor_gemini_stale_on_head.py`
9. `anu_v2/tests/test_executor_duplicate_trigger_dedupe.py`
10. `anu_v2/tests/test_executor_head_mismatch_fail_closed.py`
11. `anu_v2/tests/test_executor_token_unavailable.py`
12. `anu_v2/tests/test_polling_policy_long_polling_gate.py` (supporting)

### fixtures (6)
13. `anu_v2/fixtures/pr107_first_gemini_missing.json`
14. `anu_v2/fixtures/gemini_stale_on_head_2556.json`
15. `anu_v2/fixtures/executor_duplicate_dedupe.json`
16. `anu_v2/fixtures/executor_head_mismatch.json`
17. `anu_v2/fixtures/executor_token_unavailable.json`
18. `anu_v2/fixtures/executor_bot_session_exit_then_scheduler_resume.json`

### supporting (5)
19. `memory/reports/task-2556.md`
20. `memory/events/task-2556.dispatch-decision.json`
21. `memory/plans/tasks/task-2556/plan.md` (본 문서)
22. `memory/plans/tasks/task-2556/context-notes.md`
23. `memory/plans/tasks/task-2556/checklist.md`

## 7. 검증 결과

- pytest 신규 39 + supporting 14 = 53 PASS.
- pytest 전체 anu_v2 회귀 315 PASS (262 baseline + 53 신규).
- PR #98~#107 head unchanged (변경 0).
- forbidden paths 0.
- chat_notifications == 0 (어셀션).
- long polling violations 0 (코드 게이트).
