# task-2556 — executor 자동 entry point 구현 (보고서)

**상태**: ESCALATED — Phase B/C PASS, Phase D Gemini 자동 도착 baseline 초과(3h 13m > 2h 43m).
**분류**: `GEMINI_REVIEW_TRIGGER_REQUIRED` (Critical 7 외 / operational nudge / 회장 수동 1회 /gemini review 필요).
**일자**: 2026-05-12 KST
**팀**: dev5 마르둑
**lineage**: task-2554+2 (PR #106 cd1aeba418 main) → **task-2556**
**worktree**: `.worktrees/task-2556-dev5` (branch `task/task-2556-dev5`)
**baseline**: `origin/main` @ `cd1aeba418727511ab07afde0c15071327a5acf4`

---

## 1. 본질 1:1 박제

회장 §명시 (2026-05-12 KST):
> PR #106 → OWNER_TRIGGER_ONLY_CAPABILITY runner 는 main 반영. 단 PR #107 이 실증한 갭 =
> **idle PR 을 자동 감지해 runner 를 호출하는 daemon/scheduler entry point 부재**.
> 본 task = 자동 entry point (executor scheduler) 구현 완성.

---

## 2. 구현 산출물 (effective diff)

### code (5)
| # | path | status | lines (대략) |
|---|---|---|---|
| 1 | `anu_v2/executor_scheduler.py` | new | 462 |
| 2 | `anu_v2/idle_pr_diagnoser.py` | new | 309 |
| 3 | `anu_v2/polling_policy.py` | new | 197 |
| 4 | `anu_v2/owner_trigger_only.py` | patch (+~100) | 백워드 호환 |
| 5 | `anu_v2/merge_queue_executor.py` | patch (+~60) | 백워드 호환 |

### tests (7 = 6 §명시 + 1 supporting)
| # | path | tests | result |
|---|---|---|---|
| 6 | `test_executor_scheduler_pr107_pilot.py` | 3 | PASS |
| 7 | `test_executor_first_gemini_trigger_missing.py` | 6 | PASS |
| 8 | `test_executor_gemini_stale_on_head.py` | 5 | PASS |
| 9 | `test_executor_duplicate_trigger_dedupe.py` | 6 | PASS |
| 10 | `test_executor_head_mismatch_fail_closed.py` | 3 | PASS |
| 11 | `test_executor_token_unavailable.py` | 16 | PASS |
| 12 | `test_polling_policy_long_polling_gate.py` (supporting) | 14 | PASS |

총 53 신규 tests PASS. anu_v2 회귀 262 → 315 (모두 PASS).

### fixtures (6)
| # | path |
|---|---|
| 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)
| # | path |
|---|---|
| 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` |

---

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

| § | 요구 | 구현 위치 |
|---|---|---|
| 1 | OPEN PR idle scan | `ExecutorScheduler.run_one_cycle` + `snapshot_provider` DI |
| 2 | task_id/head_sha/CI/Gemini 진단 | `IdlePRDiagnoser.diagnose` (8 states) |
| 3 | FIRST_GEMINI_TRIGGER_MISSING 감지 (30min) | `idle_pr_diagnoser.py` line ~243 |
| 4 | GEMINI_STALE_ON_HEAD 감지 | `idle_pr_diagnoser.py` line ~260 |
| 5 | OWNER_TRIGGER_REQUIRED decision write | `emit_owner_trigger_decision` (capability 재사용) |
| 6 | runner 자동 호출 | `invoke_from_scheduler` adapter (`owner_trigger_only.py`) |
| 7 | decision/audit/markers | `record_owner_trigger_outcome` + `_append_audit` |
| 8 | scheduled/event-driven recheck | `run_one_cycle` 단일 cycle, `chat_notifications=0` |
| 9 | long polling 금지 | `polling_policy.py` (15min / 30min / 1 recheck / exit) |
| 10 | duplicate same-head dedupe | `_has_active_trigger` (POSTED+PENDING) + `_scheduler_lock` (fcntl) |
| 11 | token boundary | `assert_scheduler_token_boundary` + 5 forbidden token names |
| 12 | bot session 종료 후 재진입 | audit JSONL + markers 영구 박제, 외부 cron 재진입 |

---

## 4. 금지 16건 어셀션 (모두 PASS)

회장 §명시 5건:
1. ✅ markdown only X — 5 code + 6 fixtures + 7 tests 생성.
2. ✅ memory marker only X — 실제 코드 변경.
3. ✅ 수동 /gemini review fallback X — capability 만 사용.
4. ✅ close/reopen X.
5. ✅ force/rebase/admin X.

doctrine 11건:
6. ✅ bot /gemini review 댓글 X — endpoint hard-block.
7. ✅ same-PR push after fresh X.
8. ✅ 자동 +1 발행 X.
9. ✅ long polling X — `polling_policy.py` 코드 게이트.
10. ✅ worktree 격리 위반 X — `.worktrees/task-2556-dev5`.
11. ✅ owner PAT live X.
12. ✅ GH_TOKEN fallback X — `assert_scheduler_token_boundary`.
13. ✅ 다른 PR 혼입 X — branch 작업 0.
14. ✅ empty commit X.
15. ✅ scheduler chat 노출 X — `chat_notifications=0` 어셀션.
16. ✅ OWNER token 외 용도 X.

---

## 5. 검증 결과

### Verification (Phase C)
- `pytest anu_v2/tests/ -x` → **315 passed in 1.61s** (262 baseline + 53 신규).
- PR #98~#107 branch HEAD 변경 0.
- forbidden_paths 0.
- chat_notifications 0 (audit JSONL 모든 record).
- long polling violations 0 (code gate + 14 test assertions).

### Capability 회귀 verification (회장 §14 ESCALATED 방지)
- `test_owner_trigger_only_2554.py` 13 PASS
- `test_owner_trigger_concurrency_2554plus1.py` PASS
- `test_owner_trigger_race_fix_2554plus1.py` 22 PASS
- `test_owner_trigger_fresh_medium_3_2554plus1.py` 9 PASS
- `test_executor_posted_but_no_fresh_evidence.py` PASS

---

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

```
[cron 15min] → wrapper.py
              → gh pr list --state open --json ... → snapshots
              → OwnerTriggerOnly(http_post, token_provider=lambda: os.environ["OWNER_GEMINI_TRIGGER_TOKEN"])
              → MergeQueueExecutor(...)
              → ExecutorScheduler(...).run_one_cycle(env=os.environ)
              → SchedulerCycleResult (chat_notifications=0)
              → process exit
[다음 cron tick] → 동일 흐름 (state markers 로 재진입)
```

---

## 7. Phase D 진행 상태 (2026-05-12 11:29 KST 기준)

- **PR**: https://github.com/Jeon-Jonghyuk/dev_workspace/pull/108
- **branch**: `task/task-2556-dev5`
- **head SHA**: `edc928a4`
- **CI 11 checks**: 9 SUCCESS + 2 FAILURE (gemini-review-gate / phase3-merge-gate — Gemini fresh review 미도착으로 인한 예상 FAILURE; 도착 후 자동 재평가).
- **Gemini reviews**: 0 (자동 도착 대기 중; PR #106 은 2h 43m, PR #107 은 1h+ 대기).
- **scheduler 자체 dogfood 불가**: 본 PR 의 ExecutorScheduler 는 main 미반영이라 PR #108 자체에 적용 불가. PR #106 capability 도 OWNER_GEMINI_TRIGGER_TOKEN 부재로 봇이 직접 호출 불가. 회장 §16-special 자체 안내 (자동 도착 대기 또는 회장 수동 개입).

## 8. Phase D 후속 처리 자동화

- **1차 cron (12:14:57 KST)**: `1E86B1CF` — Gemini 미도착(1h 9m 경과)으로 silent 재스케줄.
- **2차 cron (13:55:00 KST)**: `F322C19C` — Gemini 미도착 시 ESCALATED 보고 예정 (누적 ~2h 49m, PR #106 의 2h 43m 초과 기준).
- 재진입 시 수행:
  1. Gemini review 도착 여부 확인.
  2. unresolved code-changing 0 어셀션 (도착 시).
  3. CI all SUCCESS + mergeStateStatus CLEAN 확인.
  4. BOT_GITHUB_TOKEN squash merge.
  5. smoke + reconcile evidence + lifecycle markers + 보고서 갱신.

### 8.1. 1차 cron 결과 (2026-05-12 12:15 KST)

- **PR 상태 시점**: createdAt `2026-05-12T02:06:17Z` → 1h 9m 경과.
- **reviews**: `[]` (Gemini 미도착).
- **comments**: `[]`.
- **CI**: 9 SUCCESS + 2 FAILURE (gemini-review-gate / phase3-merge-gate — Gemini 대기로 인한 예상 실패; 도착 시 자동 재평가 대상).
- **mergeStateStatus**: `BLOCKED` (reviews 부재).
- **레퍼런스 PR 동시 상태**:
  - PR #106 (task-2554): MERGED ✅ (Gemini 2h 43m 후 review).
  - PR #107 (task-2555): OPEN / BLOCKED / Gemini 미도착 (1h 53m 경과, 본 PR 보다 44m 선행).
- **결정**: silent 재스케줄. 본 시점에서 ESCALATE 시 PR #106 비교상 premature (2h 43m 미달). 회장 chat 노출 0.

회장 chat 노출 0 (cron 은 silent 실행 — task-2556 자신의 §8 정책 준수).

### 8.2. 2차 cron 결과 (2026-05-12 15:30 KST) — ESCALATED

- **PR HEAD 시점**: `a51e0812` push @ `2026-05-12T12:16:43+09:00`.
- **시점 확인**: `2026-05-12T15:30:11+09:00` → **누적 3h 13m 경과**.
- **baseline 비교**: PR #106 (task-2554+2) Gemini 도착 = 2h 43m. **30m 초과**.
- **reviews / issue comments / review comments**: `0 / 0 / 0` (REST + GraphQL 양쪽 교차 확인).
- **CI**: 9 SUCCESS + 2 FAILURE (gemini-review-gate / phase3-merge-gate — 변동 없음).
- **mergeStateStatus**: `BLOCKED` / **mergeable**: `MERGEABLE` (Gemini review 없어 BLOCKED).
- **다른 PR 비교**:
  - PR #106 (task-2554+2): MERGED (2h 43m 후 Gemini 도착).
  - PR #107 (task-2555): 별도 진행 (다른 봇).
- **결정**: **ESCALATED** — baseline 초과로 silent 재스케줄 부적절.

### 8.3. ESCALATED 사유

본 PR 은 봇이 연 PR (jeon-jonghyuk-taskctl-bot[bot]). Gemini App 의 auto-trigger
는 봇이 연 PR 에 대해 발화하지 않는 알려진 갭 (`GEMINI_EXTERNAL_TRIGGER_GAP`).
PR #104 (task-2554) 와 동일 패턴. 봇은 `OWNER_TRIGGER_ONLY_CAPABILITY` doctrine
에 의해 자체 `/gemini review` 코멘트 발화 금지 (endpoint hard-block). 또한 봇은
`OWNER_GEMINI_TRIGGER_TOKEN` 미보유로 PR #106 capability runner 자체 호출 불가.

본 PR 의 `ExecutorScheduler` 가 이 갭을 메우도록 설계되었으나, 본 PR 이 main 에
미반영 상태라 자기 자신에는 적용 불가 (self-dogfood 한계 — §7 명시).

### 8.4. 회장 수동 처리 권한 (bootstrap exception)

1. PR #108 에 회장 계정으로 `/gemini review` 댓글 1회 작성 (1-shot bootstrap, PR #104 와 동일 절차).
2. Gemini fresh review 도착 대기 (~2h 43m 기준).
3. unresolved code-changing 0 확인 (재발 시 `OWNER_DECISION_REQUIRED` / 자동 `+1` 발행 X).
4. CI re-run (gemini-review-gate / phase3-merge-gate).
5. mergeStateStatus CLEAN 확인.
6. BOT_GITHUB_TOKEN squash merge → smoke + reconcile + lifecycle markers.

### 8.5. 보존 어셀션 (silent retry 동안 변경 없음)

- PR #98~#107 branch HEAD 변경 0.
- main 변경 0.
- `.env` / CI workflows / other anu_v2 모듈 변경 0.
- 봇 `/gemini review` 코멘트 0 (endpoint hard-block 준수).
- same-PR push after fresh 없음 (fresh review 자체 미도착).
- long polling 위반 0.
- chat notifications during silent retry: 0 (self-doctrine §8 준수).

## 9. 결론

**capability 자동화 갭 폐쇄 완료 (코드 레벨)**. PR #106 의 `OwnerTriggerOnly` runner 를 본 task 의
`ExecutorScheduler` 가 자동 호출함으로써, PR #107 이 실증한 idle PR 자동 감지 + 자동
trigger 가 가능해졌다. 봇 session 종료 후에도 외부 cron 이 `run_one_cycle` 을 재호출하면
audit/markers 기반으로 상태 복원하여 재진입 가능.

**Phase D 분기**: 본 PR 의 Gemini 자동 도착이 PR #106 baseline 을 30m 초과해
**ESCALATED** 분기. `GEMINI_REVIEW_TRIGGER_REQUIRED` 1회 bootstrap 회장 수동 트리거가
필요하다. 본 task 의 코드는 main 반영 후 task-2557+ 부터 자기-자동 처리 가능.

**최종 보고**: **ESCALATED** (회장 수동 `/gemini review` 1회 요청).
