# task-2560 — FUC-4 per-PR exception isolation 계획서

## 1. 본질 (회장 §명시 2026-05-12 Track A 1순위)

`ExecutorScheduler._handle_single_diagnosis` per-PR try/except isolation.
- 한 PR diagnosis 의 예외가 cycle 전체를 중단시키지 않게 한다.
- 실패 PR 만 FAILED 또는 critical ESCALATED marker 박제.
- 다음 PR diagnosis 는 정상 진행.
- exception summary 를 decision/audit 양쪽 기록.
- non-critical exception 발생 시 scheduler cycle 유지.

## 2. 설계

### 2.1 새 상수 / dataclass 필드

`anu_v2/executor_scheduler.py`:
- `ACTION_PR_EXCEPTION_ISOLATED` (non-critical 분류)
- `ACTION_PR_EXCEPTION_CRITICAL_ESCALATED` (critical 7 분류)
- `PR_EXCEPTION_AUDIT_SCHEMA = "anu_v2.executor_scheduler.pr_exception.v1"`
- `_CRITICAL_EXCEPTIONS` tuple — 7개 분류:
  1. TokenBoundaryViolation
  2. PermissionError
  3. OSError
  4. MemoryError
  5. NotImplementedError
  6. TypeError
  7. AttributeError
- `SchedulerCycleResult` 신규 필드:
  - `pr_exceptions_isolated: int = 0`
  - `pr_exceptions_critical_escalated: int = 0`
  - `cycle_crashed: bool = False`

### 2.2 핵심 메서드

- `ExecutorScheduler._safe_handle_single_diagnosis()`
  - `_handle_single_diagnosis()` 를 try/except 로 감싸는 wrapper.
  - `BotSessionExitRequired` 는 re-raise (cycle 종료 신호).
  - `Exception` 계열은 catch → critical 여부 분류 → marker/summary 박제.
- `ExecutorScheduler._record_pr_exception_marker()`
  - `{task_id}.pr-{pr}.exception.json` summary (schema + ts + traceback origin).
  - `{task_id}.pr-{pr}.{failed|critical-escalated}` 0-byte marker.
- helper:
  - `_is_critical_exception(exc) -> bool`
  - `_summarize_exception(exc) -> dict` (type / module / message[:512] / origin frame).

### 2.3 호출 흐름 변경

`run_one_cycle` 내부:
```
for diag in diagnoses:
    ...
    action = self._safe_handle_single_diagnosis(
        diag=diag, snapshot=snap, cycle_started=cycle_started
    )
    actions.append(action)
    self._append_audit(cycle_started=cycle_started, action=action)
```
- `_handle_single_diagnosis` 직접 호출 → wrapper 호출로 1줄 교체.
- 이후 isolated/critical 카운트 누적.

## 3. 테스트 (anu_v2/tests/test_executor_scheduler_per_pr_isolation.py)

회장 §명시 필수 6 + 부수 어셀션 3:
1. PR A exception (RuntimeError) 이면서 PR B 는 정상 → 둘 다 cycle 내 처리.
2. PR C critical exception (PermissionError) → ESCALATED marker.
3. 3 PR 동시 (isolated/clean/critical) → cycle_crashed=False.
4. long polling 0 — sleep loop 0 검증.
5. BotSessionExitRequired 는 isolation 으로 삼키지 않고 전파.
6. Critical 7 분류 표 정확히 7개.
7. `_summarize_exception` traceback origin 박제 + PII 0.

## 4. forbidden_paths (회장 §명시 7 금지)

- `anu_v2/owner_trigger_only.py` 수정 0
- `anu_v2/auto_gemini_triage.py` 수정 0
- task-2558 expected_files 재수정 0
- task-2561/task-2562 같은 PR 섞기 0
- 다른 anu_v2 modules 변경 0
- dashboard/ 변경 0
- PR #98~#111 branch 변경 0

## 5. expected_files (8건 strict)

1. `anu_v2/executor_scheduler.py` (수정)
2. `anu_v2/tests/test_executor_scheduler_per_pr_isolation.py` (신규)
3. `anu_v2/fixtures/executor_per_pr_exception_isolation.json` (신규)
4. `memory/reports/task-2560.md` (신규)
5. `memory/events/task-2560.dispatch-decision.json` (신규)
6. `memory/plans/tasks/task-2560/plan.md` (본 파일)
7. `memory/plans/tasks/task-2560/context-notes.md`
8. `memory/plans/tasks/task-2560/checklist.md`

## 6. 14단계 standard

1. cokacdir → workspace
2. worktree `task-2560-dev5` from origin/main
3. 코드 수정 + 테스트 작성 + 회귀 PASS
4. 3문서 + dispatch-decision.json + 보고서
5. BOT identity commit
6. PR 생성 (BOT)
7. Gemini fresh (owner_trigger_only 또는 ExecutorScheduler 자동, capability 활용 가능)
8. unresolved 0
9. CI 11/11
10. CLEAN
11. BOT squash merge
12. admin override 0
13. post-merge smoke + reconcile + lifecycle markers
14. .done
