# task-2712 — FAILURE_CALLBACK_BEFORE_EXIT_GUARD (FINAL · IMPLEMENTATION_DISPATCH_AUTHORIZED · LOCKED)

★ **FINAL · IMPLEMENTATION_DISPATCH_AUTHORIZED · LOCKED** — 회장 verbatim "Codex OVERALL PWR + IMPLEMENTATION_DISPATCH_READINESS READY_WITH_RECOMMENDATIONS 인정 · task-2712 lock 인가 · task-2712 구현 dispatch 인가 · executor dev3 또는 dev4 중 clean (★ dev4 선택) · dev1 Hermes 제외" 정합.

- spec lineage 보존: draft/v1/v2/v3/v4/v5/v6 모두 보존 (★ §0 참조)
- 본 final = Codex round 6 remaining 2 (★ inline consolidation) + 구현 dispatch 영역

---

## 0. Header / Authorization Anchor

- **task_id**: `task-2712`
- **task_type**: `system_hook` (★ executable implementation · finish-task.sh + dispatch.py 최소 변경 + 신규 module + tests)
- **chair_authorization_id**: `CHAIR-AUTH-TASK-2712-FAILURE-CALLBACK-BEFORE-EXIT-GUARD-IMPLEMENTATION-260530` (★ 회장 verbatim 발급 · spec review 단계 chair_authorization_id 재사용 0 strict)
- **dispatch_status**: ★ **DISPATCH_AUTHORIZED · DISPATCH_PENDING**
- **executor_team**: `dev4-team` (비슈누 · Vishnu) (★ 회장 verbatim 결정 · dev1 Hermes 제외)
- **verifier_team_candidate**: `<CHAIR_TO_DECIDE_AFTER_COMPLETION>` (★ task-2712+1 분리 · 완료 보고 후 회장 결정)
- **anu_collector_key**: `c119085addb0f8b7`
- **callback_envelope_prompt_byte_limit**: UTF-8 ≤ 3900 bytes hard (★ wc -c)
- **spec_lineage**: draft sha `bae3a142` · v1 `18e400ee` · v2 `0f6850e6` · v3 `9bc815c9` · v4 `531a3363` · v5 `43694132` · v6 `7dfd5c25`

---

## 1. Problem Statement

task-2711 사례 박제: dev1 헤르메스 정상 작업 완료 (★ 15/15 산출물 + 18/18 pytest PASS + 1,220 bytes ANU envelope) 그러나 finish-task.sh L451 scope-guard FAIL → L1550 callback registration 미도달 → ANU key cron 0 → ANU silent-drop 4신호 단독 spawn 0 오판. 새 원칙 (회장 verbatim): **callback = mandatory lifecycle signal for ALL terminal states**.

---

## 2. Goal

봇이 9 terminal state (SUCCESS / FAILURE / BLOCKED / SCOPE_GUARD_FAIL / QC_FAIL / INFRA_DEFECT / PERMISSION_FAIL / API_FAIL / CRITICAL_ESCALATION) + CRASH_NO_EXIT_CODE 어떤 state 로 종료되든 exit 전에 disk marker 박제 + ANU session-bound polling 0 회수 가능 lifecycle.

---

## 3. Terminal State 10 + Signal Taxonomy

### 3.1 10 terminal state

SUCCESS / FAILURE / BLOCKED / SCOPE_GUARD_FAIL / QC_FAIL / INFRA_DEFECT (★ paths_null / shared branch contamination / residual_process subcase 포함) / PERMISSION_FAIL / API_FAIL (recoverable) / CRITICAL_ESCALATION (★ Critical 7) / CRASH_NO_EXIT_CODE (★ SIGKILL/OOM/kernel panic/etc).

### 3.1.1 Signal taxonomy 11

SIGKILL(-9) · SIGTERM(-15 user/parent) · SIGINT(-2 Ctrl-C) · SIGSEGV(-11) · SIGBUS(-7) · OOM-killer(-1) · container OOM-kill(-1 + container exit_reason) · kernel panic(-1 + post-boot journal) · `set -e` cascade(original terminal_state preserved) · bash `trap EXIT`(original preserved) · **docker stop SIGTERM(15)→SIGKILL(9) cascade**(multi-phase · -15 → -9 sentinel) · **nohup/detach**(RUNTIME_RESIDUAL_PROCESS = INFRA_DEFECT subcase)

### 3.1.2 finish-task.sh trap handlers (★ 3 lines minimal)

```bash
trap '_emit_failure_envelope CRASH_NO_EXIT_CODE "$TASK_ID" $? "trap_EXIT"' EXIT
trap '_emit_failure_envelope CRASH_NO_EXIT_CODE "$TASK_ID" -2 "SIGINT"' INT
trap '_emit_failure_envelope CRASH_NO_EXIT_CODE "$TASK_ID" -15 "SIGTERM"' TERM
```

### 3.1.3 RUNTIME_RESIDUAL_PROCESS subcase (★ R4)

`INFRA_DEFECT.residual_process` · detection: parent exit 후 child alive (ps aux + pgrep) · handoff: `<task_id>.failure-handoff-marker.json` + residual_pid · **`.done` 강제 0**.

### 3.2 .done 정책 + Exactly One Terminal Marker Rule

#### 3.2.1 6 marker paths · classification

| marker type | path pattern | classification | role |
|---|---|---|---|
| `.done` | `<task_id>.done` | TERMINAL_MARKER | SUCCESS only |
| failure envelope | `<task_id>.failure-envelope.json` | TERMINAL_MARKER | 8 failure state |
| handoff marker | `<task_id>.failure-handoff-marker.json` | TERMINAL_MARKER | BLOCKED/INFRA/API |
| supervisor crash | `<task_id>.supervisor-crash-marker.json` | TERMINAL_MARKER | CRASH_NO_EXIT_CODE |
| stderr emit log | `<task_id>.stderr-emit.log` | **FALLBACK_EVIDENCE_ONLY** | envelope-write fail · NOT terminal |
| syslog journal | systemd tag `failure_callback_2712` | **FALLBACK_EVIDENCE_ONLY** | last-resort · NOT terminal |

#### 3.2.2~3.2.3 Marker definition + scanner owner L1~L4

- marker = task_id 매칭 + valid JSON 또는 stderr log line + recorded_at + terminal_state field
- L1: in-bot self-check after hook · L2: dispatch.py post-child-exit scan · L3: ANU session start + 5 min interval + on-demand · L4: session-watchdog.sh every 10 min (chair-gated)

#### 3.2.4 Marker count verification

```python
def verify_exactly_one_terminal_marker(task_id: str) -> dict:
    """
    return contract 4 enum:
      OK / ZERO_FIRE / ZERO_FIRE_BUT_FALLBACK_RECOVERABLE / MULTI_FIRE_VIOLATION
    """
    terminal_markers = [
        path_exists(f"memory/events/{task_id}.done"),
        path_exists(f"memory/events/{task_id}.failure-envelope.json"),
        path_exists(f"memory/events/{task_id}.failure-handoff-marker.json"),
        path_exists(f"memory/events/{task_id}.supervisor-crash-marker.json"),
    ]
    terminal_count = sum(terminal_markers)
    fallback_evidence_present = (
        path_exists(f"memory/events/{task_id}.stderr-emit.log") or
        syslog_journal_has_entry(task_id, tag="failure_callback_2712")
    )
    if terminal_count == 0:
        if fallback_evidence_present:
            return {"status": "ZERO_FIRE_BUT_FALLBACK_RECOVERABLE", "fallback_evidence": True}
        return {"status": "ZERO_FIRE", "fallback_evidence": False}
    if terminal_count >= 2:
        return {"status": "MULTI_FIRE_VIOLATION", "fallback_evidence": fallback_evidence_present}
    return {"status": "OK", "fallback_evidence": fallback_evidence_present}
```

---

## 4. Failure Callback Envelope Schema

### 4.1 11 mandatory field

`task_id` · `team` · `bot` · `session` · `phase` · `exit_code`(int · crash sentinel -1/-9/-15) · `failure_kind` · `artifact_paths`(array) · `critical7_match`(bool) · `terminal_state`(enum 10) · `chair_authorization_id` + `collector_role=ANU` + `collector_key=ANU_KEY` + `self_key_used=false`(strict).

### 4.2 byte limit

UTF-8 ≤ 3900 bytes hard (wc -c). 초과 시 artifact_paths 만 유지 + summary 축소.

### 4.3 fallback rules

| 시나리오 | rule |
|---|---|
| envelope OK + cron OK | normal · ANU callback 정상 |
| envelope OK + cron FAIL | §6 4-step fallback → disk handoff marker |
| envelope-write FAIL (disk full/IO) | stderr escape-safe JSON 1-line emit + supervisor capture + cron skip |
| envelope-write FAIL + stderr FAIL | UNCLASSIFIED_TERMINAL_STATE + MARKER_FIRE_ZERO |
| envelope OK + handoff FAIL | stderr 1-line emit |
| both fail | stderr 1-line obligation + supervisor handoff |
| no supervisor | syslog journal last-resort |

### 4.3.1 stderr emit operational

- format: `FAILURE_CALLBACK_2712 task_id=<id> terminal_state=<enum> exit_code=<int> failure_kind=<str> phase=<str>`
- stream: fd=2 · dispatch.py 가 child stderr → `<task_id>.stderr-emit.log`
- retention: 비어있지 않으면 보존 · 비어있으면 삭제
- L3 scan: `*.stderr-emit.log` pattern 매칭 + `FAILURE_CALLBACK_2712` prefix

### 4.3.2 syslog journal operational

- utility: `logger -t failure_callback_2712 -p user.warning`
- syslog retention 부재/불완전 시 **disk marker 기준 failure recovery 우선** (★ disk markers + stderr-emit.log = L1 source · syslog = last-resort)

---

## 5. Terminal Failure Path Matrix

### 5.1 finish-task.sh inner-script hook 6 path

| exit point | hook call | terminal_state |
|---|---|---|
| **L451 scope-guard FAIL** | `_emit_failure_envelope SCOPE_GUARD_FAIL "$task_id" $exit_code "$scope_violation_count"` | SCOPE_GUARD_FAIL |
| qc HIGH/CRITICAL FAIL | `_emit_failure_envelope QC_FAIL ...` | QC_FAIL |
| capability snapshot null | `_emit_failure_envelope INFRA_DEFECT ...` | INFRA_DEFECT |
| chair_auth/forbidden/allowed_resources 위반 | `_emit_failure_envelope PERMISSION_FAIL ...` | PERMISSION_FAIL |
| forbidden_files write 시도 | `_emit_failure_envelope CRITICAL_ESCALATION ...` | CRITICAL_ESCALATION |
| API 500 timeout | `_emit_failure_envelope API_FAIL ...` | API_FAIL (recoverable) |

### 5.2 dispatch.py inner instrumentation 5 path + spawn verification

| entry | hook + verification |
|---|---|
| bot collision detect | `_dispatch_emit_handoff BLOCKED bot_collision` |
| **DISPATCH_FALSE_OK detect** | `_dispatch_emit_handoff INFRA_DEFECT dispatch_false_ok` + `_verify_bot_spawn()` 결과 DISPATCH_FALSE_OK 시 강제 |
| capability snapshot null | `_dispatch_emit_handoff INFRA_DEFECT capability_null` |
| task md missing | `_dispatch_emit_handoff PERMISSION_FAIL task_md_missing` |
| cron registration failure | `_dispatch_emit_handoff INFRA_DEFECT cron_registration_failure` |

#### 5.2.1 Spawn verification

```python
import os, time, glob, psutil

def _verify_bot_spawn(task_id, expected_bot_id, child_pid=None):
    """return: SPAWNED / DISPATCH_FALSE_OK / TIMEOUT_BOT_ALIVE_BUT_NO_MARKER"""
    DEFAULT_TIMEOUT_SEC = 15
    timeout_sec = int(os.environ.get("FAILURE_CALLBACK_2712_SPAWN_TIMEOUT_SEC", DEFAULT_TIMEOUT_SEC))
    polling = 1
    elapsed = 0
    while elapsed < timeout_sec:
        if glob.glob(f"memory/events/{task_id}.spawn-confirmed-*.json"):
            if child_pid is None or psutil.pid_exists(child_pid):
                return "SPAWNED"
        time.sleep(polling)
        elapsed += polling
    child_alive = (child_pid is not None) and psutil.pid_exists(child_pid)
    if child_alive:
        return "TIMEOUT_BOT_ALIVE_BUT_NO_MARKER"
    return "DISPATCH_FALSE_OK"
```

### 5.3 runtime path

Critical 7 매칭 → CRITICAL_ESCALATION + 즉시 cron · forbidden write → CRITICAL_ESCALATION · infra defect → INFRA_DEFECT handoff · runtime crash (SIGKILL/OOM) → supervisor post-mortem · trap (SIGINT/SIGTERM/EXIT) → §3.1.2 hook

---

## 6. Fallback chain + bypass defense

### 6.1 4-step fallback

1. failure envelope JSON disk 박제
2. ANU cron 발사 시도
3. cron 실패 시 disk handoff marker 박제
4. exit (exit_code 유지)

### 6.2 ANU 회수 path

disk scan · session-bound polling 0 · 우선순위: cron callback > disk envelope > disk handoff marker > stderr capture log > syslog journal > supervisor handoff

### 6.3 SELF_COLLECTOR_FORBIDDEN

#### 6.3.1 In-flight enforcement (5 checkpoint)

```python
def _validate_collector_strict(envelope: dict) -> None:
    ANU_KEY = "c119085addb0f8b7"
    if envelope.get("collector_role") != "ANU":
        raise CollectorViolation("SELF_COLLECTOR_FORBIDDEN: collector_role != ANU")
    if envelope.get("collector_key") != ANU_KEY:
        raise CollectorViolation(f"SELF_COLLECTOR_FORBIDDEN: collector_key != {ANU_KEY}")
    if envelope.get("owner_key") != ANU_KEY:
        raise CollectorViolation(f"SELF_COLLECTOR_FORBIDDEN: owner_key != {ANU_KEY}")
    if envelope.get("self_key_used") is not False:
        raise CollectorViolation("SELF_COLLECTOR_FORBIDDEN: self_key_used must be explicit false")

def _fire_callback_with_enforcement(envelope: dict, cron_fn) -> None:
    _validate_collector_strict(envelope)
    cron_fn(envelope)
```

#### 6.3.2 D1~D5 bypass defense

| layer | rule |
|---|---|
| D1 single entry point | 모든 cron 발사 = `_fire_callback_with_enforcement()` wrapper 만 |
| D2 lint/static | CI/pre-commit grep `cokacdir --cron` direct → reject |
| D3 post-fire verification | `cron-list --key ANU_key` + collector field 매칭 |
| D4 runtime audit log | `cron-fire-audit-<ts>.json` (caller stack + sha256) |
| D5 absence-of-state | cron-fire marker count ↔ audit log count window 매칭 mismatch detect |

#### 6.3.3.A Prose semantic spec

| marker class | count target |
|---|---|
| `*.failure-envelope.json` | failure_marker_count |
| `*.failure-handoff-marker.json` | failure_marker_count |
| `*.supervisor-crash-marker.json` | failure_marker_count |
| `*.done` | success_marker_count |

- ✓ OK 1: success=1 + failure=0 (SUCCESS) · ✓ OK 2: success=0 + failure=1 (failure path)
- VIOLATION 1: success≥1 + failure≥1 (concurrent)
- VIOLATION 2: terminal marker class 2종 이상 동시 증가
- VIOLATION 3: failure terminal state 에서 .done 증가

#### 6.3.3.B Pseudo-code (★ §6.3.3.A 1:1 mirror)

```python
def detect_bypass_via_count_mismatch(window_start, window_end) -> List[dict]:
    success_marker_count = 0
    failure_marker_count = 0
    fire_markers = []
    class_counts = {"failure_envelope": 0, "failure_handoff_marker": 0, "supervisor_crash_marker": 0, "done": 0}

    for path in glob("memory/events/*.failure-envelope.json"):
        if window_start <= os.path.getmtime(path) <= window_end:
            envelope = json.load(open(path))
            if envelope.get("registration_mode") in ("normal_callback", "failure_callback_before_exit_guard"):
                failure_marker_count += 1
                class_counts["failure_envelope"] += 1
                fire_markers.append((path, "failure_envelope", envelope.get("terminal_state")))
    for path in glob("memory/events/*.failure-handoff-marker.json"):
        if window_start <= os.path.getmtime(path) <= window_end:
            failure_marker_count += 1
            class_counts["failure_handoff_marker"] += 1
            fire_markers.append((path, "failure_handoff_marker", None))
    for path in glob("memory/events/*.supervisor-crash-marker.json"):
        if window_start <= os.path.getmtime(path) <= window_end:
            failure_marker_count += 1
            class_counts["supervisor_crash_marker"] += 1
            fire_markers.append((path, "supervisor_crash_marker", None))
    for path in glob("memory/events/*.done"):
        if window_start <= os.path.getmtime(path) <= window_end:
            success_marker_count += 1
            class_counts["done"] += 1
            fire_markers.append((path, "done", "SUCCESS"))

    audit_entries = [p for p in glob("memory/events/cron-fire-audit-*.json")
                     if window_start <= os.path.getmtime(p) <= window_end]

    violations = []
    if success_marker_count >= 1 and failure_marker_count >= 1:
        violations.append({"violation": "EXACTLY_ONE_TERMINAL_VIOLATION_FAILURE_AND_DONE_CONCURRENT", ...})
    classes_with_fires = [c for c, n in class_counts.items() if n >= 1]
    if len(classes_with_fires) >= 2:
        violations.append({"violation": "EXACTLY_ONE_TERMINAL_VIOLATION_MULTI_CLASS_CONCURRENT", ...})
    failure_envelope_non_success = any(cls == "failure_envelope" and ts and ts != "SUCCESS"
                                        for (_, cls, ts) in fire_markers)
    if failure_envelope_non_success and class_counts["done"] >= 1:
        violations.append({"violation": "EXACTLY_ONE_TERMINAL_VIOLATION_FAILURE_STATE_WITH_DONE", ...})
    total_fires = success_marker_count + failure_marker_count
    if total_fires > len(audit_entries):
        violations.append({"violation": "AUDIT_LOG_MISSING_FOR_CRON_FIRE", ...})
    return violations
```

★ §6.3.3.C ANU self-review section 제거 (★ 회장 verbatim doctrine 1 정합)

---

## 7. Required Test Fixtures — 14 required

| # | fixture | terminal_state | failure_kind | expected marker | .done 허용 |
|---|---|---|---|---|---|
| F-1 | task-2711 scope-guard FAIL | SCOPE_GUARD_FAIL | scope_violation_count_61 | failure-envelope.json | NO |
| F-2 | paths_null | INFRA_DEFECT | capability_paths_null | failure-handoff-marker.json | NO |
| F-3 | API 500 mid-postprocessing | API_FAIL (recoverable) | api_error_500 | failure-handoff-marker.json | NO |
| F-4 | bot collision / DISPATCH_FALSE_OK | BLOCKED + INFRA_DEFECT | bot_collision + dispatch_false_ok | failure-handoff-marker.json | NO |
| F-5 | cron registration failure | (any) | cron_fallback | failure-envelope.json + handoff fallback | NO |
| F-6 | crash no exit_code | CRASH_NO_EXIT_CODE | sigkill_or_oom | supervisor-crash-marker.json | NO |
| F-7 | envelope-write fail + stderr | UNCLASSIFIED_TERMINAL_STATE | disk_full_or_io_error | stderr-emit.log | NO |
| F-8 | SELF_COLLECTOR_FORBIDDEN violation | CRITICAL_ESCALATION | self_key_used_true | failure-envelope.json (CRITICAL) | NO |
| F-9 | syslog retention unavailable | INFRA_DEFECT | syslog_retention_unavailable_or_incomplete | failure-envelope.json + stderr-emit.log | NO |
| F-10 | docker/nohup residual process | INFRA_DEFECT.residual_process | docker_stop_or_nohup_residual | failure-handoff-marker.json + residual_pid | NO |
| F-11 | callback attempted no fire + fallback exists | any FAILURE state | callback_registration_attempted_no_cron_fire_fallback_recoverable | failure-envelope.json OR handoff-marker.json (terminal marker 1개) + cron 미발사 흔적 | NO |
| F-12 | SIGTERM/Ctrl-C trap | CRASH_NO_EXIT_CODE | trap_SIGINT_or_SIGTERM | supervisor-crash-marker.json | NO |
| F-13 | bypass attempt cron direct | CRITICAL_ESCALATION | wrapper_bypass_direct_cron | failure-envelope.json (CRITICAL) + audit missing | NO |
| F-14 | D5 audit-missing bypass | CRITICAL_ESCALATION | audit_log_missing_for_cron_fire | failure-envelope.json (CRITICAL) + count mismatch | NO |

---

## 8. Architecture

### 8.1 4 신규 module

| module | path |
|---|---|
| failure_envelope_writer | `scripts/harness/v36/failure_envelope_writer.py` |
| failure_callback_dispatcher | `scripts/harness/v36/failure_callback_dispatcher.py` |
| terminal_state_classifier | `scripts/harness/v36/terminal_state_classifier.py` |
| before_exit_guard_hook | `scripts/harness/v36/before_exit_guard_hook.sh` |

### 8.2 finish-task.sh 최소 변경 (★ ~6 hook lines + 3 trap lines = ~9 lines total)
### 8.3 dispatch.py 최소 변경 (★ ~10-15 lines + spawn verification)

---

## 9. Affected Files — 23 expected_files

1. `scripts/harness/v36/failure_envelope_writer.py` (★ 신규)
2. `scripts/harness/v36/failure_callback_dispatcher.py` (★ 신규)
3. `scripts/harness/v36/terminal_state_classifier.py` (★ 신규)
4. `scripts/harness/v36/before_exit_guard_hook.sh` (★ 신규)
5. `tests/test_failure_callback_before_exit_guard_2712.py` (★ 신규)
6~19. `tests/fixtures/failure_callback_2712/F-1~F-14.json` (★ 14 fixture)
20. `schemas/failure_envelope_schema.json` (★ 11 field)
21. `memory/reports/task-2712.md` (★ 보고서)
22. `memory/events/task-2712.formalization-commit-260530.json` (★ formalization marker)
23. `memory/events/task-2712.callback-envelope.json` (★ ANU envelope ≤3900 bytes)

### 9.1 allowed_existing_file_edits (★ 회장 verbatim 영역)

- `scripts/finish-task.sh` (★ 6 inner hook + 3 trap · 최소 변경)
- `dispatch.py` (★ 5 inner instrumentation + spawn verification · 최소 변경)

---

## 10. Allowed / Forbidden Files

### 10.1 forbidden_files (★ sha256 동일 강제)

- `bot_settings.json` / `memory/bot_settings.json`
- `.claude/settings.json` / `/home/jay/.claude/settings.json`
- `scripts/session-watchdog.sh` / `scripts/task-scope-guard.sh`
- `qc_verify.py` / `utils/merge_queue_executor.py` / `utils/real_merge_hooks.py`
- `anu_v3/` / `memory/capabilities/**`
- task-2706~2709+1 모든 산출물 (★ immutable historical)
- `memory/tasks/task-2710*.md` / task-2710 모든 marker
- `memory/tasks/task-2711*.md` / task-2711 모든 marker / 산출물 (★ scripts/anu/codex_review_loop_decider.py 등)
- `memory/tasks/task-2712.md` 외 task-2712 spec lineage v1~v6 보존

### 10.2 forbidden_actions (★ 회장 verbatim 9)

1. dev1 오염 브랜치 재사용 0
2. task-2711 재dispatch 0
3. task-2711 `.done` 강제 생성 0
4. task-2703 오염 파일 수정/삭제 0
5. task-2710 변경 0
6. task-2706~2709+1 evidence 변경 0
7. bot_settings/settings 변경 0
8. PR / push / merge / GitHub write 0
9. pilot dispatch 0

### 10.3 Capability Scope YAML

```yaml
allowed_resources:
  paths:
    - scripts/harness/v36/failure_envelope_writer.py
    - scripts/harness/v36/failure_callback_dispatcher.py
    - scripts/harness/v36/terminal_state_classifier.py
    - scripts/harness/v36/before_exit_guard_hook.sh
    - tests/test_failure_callback_before_exit_guard_2712.py
    - tests/fixtures/failure_callback_2712/F-1_task_2711_scope_guard_fail.json
    - tests/fixtures/failure_callback_2712/F-2_paths_null.json
    - tests/fixtures/failure_callback_2712/F-3_api_500_recoverable.json
    - tests/fixtures/failure_callback_2712/F-4_bot_collision_dispatch_false_ok.json
    - tests/fixtures/failure_callback_2712/F-5_cron_registration_failure_handoff.json
    - tests/fixtures/failure_callback_2712/F-6_crash_no_exit_code.json
    - tests/fixtures/failure_callback_2712/F-7_envelope_write_fail_stderr_emit.json
    - tests/fixtures/failure_callback_2712/F-8_self_collector_forbidden_violation.json
    - tests/fixtures/failure_callback_2712/F-9_syslog_retention_unavailable.json
    - tests/fixtures/failure_callback_2712/F-10_docker_nohup_residual_process.json
    - tests/fixtures/failure_callback_2712/F-11_callback_attempted_no_fire_fallback_exists.json
    - tests/fixtures/failure_callback_2712/F-12_sigterm_ctrlc_trap.json
    - tests/fixtures/failure_callback_2712/F-13_bypass_attempt_cron_direct.json
    - tests/fixtures/failure_callback_2712/F-14_d5_audit_missing_bypass.json
    - schemas/failure_envelope_schema.json
    - memory/reports/task-2712.md
    - memory/events/task-2712.formalization-commit-260530.json
    - memory/events/task-2712.callback-envelope.json
  allowed_existing_file_edits:
    - scripts/finish-task.sh
    - dispatch.py
  expected_files:
    - scripts/harness/v36/failure_envelope_writer.py
    - scripts/harness/v36/failure_callback_dispatcher.py
    - scripts/harness/v36/terminal_state_classifier.py
    - scripts/harness/v36/before_exit_guard_hook.sh
    - tests/test_failure_callback_before_exit_guard_2712.py
    - tests/fixtures/failure_callback_2712/F-1_task_2711_scope_guard_fail.json
    - tests/fixtures/failure_callback_2712/F-2_paths_null.json
    - tests/fixtures/failure_callback_2712/F-3_api_500_recoverable.json
    - tests/fixtures/failure_callback_2712/F-4_bot_collision_dispatch_false_ok.json
    - tests/fixtures/failure_callback_2712/F-5_cron_registration_failure_handoff.json
    - tests/fixtures/failure_callback_2712/F-6_crash_no_exit_code.json
    - tests/fixtures/failure_callback_2712/F-7_envelope_write_fail_stderr_emit.json
    - tests/fixtures/failure_callback_2712/F-8_self_collector_forbidden_violation.json
    - tests/fixtures/failure_callback_2712/F-9_syslog_retention_unavailable.json
    - tests/fixtures/failure_callback_2712/F-10_docker_nohup_residual_process.json
    - tests/fixtures/failure_callback_2712/F-11_callback_attempted_no_fire_fallback_exists.json
    - tests/fixtures/failure_callback_2712/F-12_sigterm_ctrlc_trap.json
    - tests/fixtures/failure_callback_2712/F-13_bypass_attempt_cron_direct.json
    - tests/fixtures/failure_callback_2712/F-14_d5_audit_missing_bypass.json
    - schemas/failure_envelope_schema.json
    - memory/reports/task-2712.md
    - memory/events/task-2712.formalization-commit-260530.json
    - memory/events/task-2712.callback-envelope.json
  forbidden_paths:
    - bot_settings.json
    - memory/bot_settings.json
    - .claude/settings.json
    - /home/jay/.claude/settings.json
    - scripts/session-watchdog.sh
    - scripts/task-scope-guard.sh
    - qc_verify.py
    - utils/merge_queue_executor.py
    - utils/real_merge_hooks.py
    - anu_v3/
    - memory/capabilities/**
    - memory/tasks/task-2710.md
    - memory/tasks/task-2710_v2.md
    - memory/tasks/task-2710_v3.md
    - memory/tasks/task-2710_v4.md
    - memory/tasks/task-2710_v5.md
    - memory/tasks/task-2710_v6.md
    - memory/tasks/task-2710_v7.md
    - memory/tasks/task-2710_v8.md
    - memory/tasks/task-2711.md
    - memory/tasks/task-2711_draft.md
    - scripts/anu/codex_review_loop_decider.py
    - tests/test_codex_review_loop_decider_2711.py
    - memory/tasks/task-2712_v2.md
    - memory/tasks/task-2712_v3.md
    - memory/tasks/task-2712_v4.md
    - memory/tasks/task-2712_v5.md
    - memory/tasks/task-2712_v6.md
    - memory/tasks/task-2712_draft.md
```

---

## 11. Implementation Principles 10 (★ 회장 verbatim 1:1)

1. SUCCESS .done 경로 + FAILURE callback/handoff 경로 분리
2. .done = SUCCESS 또는 정상 closeout 만 허용
3. failure terminal state = .done 없이 failure envelope 또는 handoff marker
4. scope-guard FAIL / QC_FAIL / permission fail / API fail / infra defect / forbidden violation 모두 exit 전 marker
5. callback 등록 실패 시 disk handoff marker
6. SELF_COLLECTOR_FORBIDDEN 위반 0
7. collector_role=ANU + collector_key=ANU_KEY + self_key_used=false 강제
8. envelope UTF-8 ≤ 3900 bytes 강제
9. exactly-one-terminal-marker rule 적용
10. task-2711 같은 finish-task scope-guard FAIL 사례에서 ANU session-bound polling 0 회수 가능

---

## 12. Preflight 6 (★ 회장 verbatim · dev4 구현 시작 전)

1. clean worktree 확인 (★ `.worktrees/task-2712-dev4` 신규 생성 · 기존 dev1/task-2703/task-2711 오염 worktree 미사용)
2. **origin/main 기준 fresh branch/worktree 생성** (★ shared branch contamination 회피)
3. dev1/task-2703/task-2711 오염 파일 0 확인 (★ git status 검증 · main..HEAD 0)
4. expected_files 확정 (★ §9 23 file)
5. forbidden path baseline sha256 저장 (★ §10.3 forbidden_paths 모두 baseline)
6. task-2710/task-2711/task-2706~2709+1 evidence no-touch 확인 (★ §10.1 immutable lineage)

---

## 13. Completion Conditions 8 (★ 회장 verbatim)

1. regression PASS
2. F-1 task-2711 scope-guard FAIL fixture PASS
3. failure envelope byte limit PASS (★ UTF-8 ≤ 3900 bytes)
4. SELF_COLLECTOR_FORBIDDEN regression PASS (★ F-8)
5. exactly-one-terminal-marker regression PASS
6. finish-task.sh success path regression PASS
7. dispatch.py DISPATCH_FALSE_OK / bot collision failure handoff regression PASS (★ F-4)
8. report (★ memory/reports/task-2712.md) + callback envelope 작성

---

## 14. dev4 Vishnu 실행 지침

### 14.1 단계

1. §12 preflight 6 모두 PASS 박제
2. §8.1 4 module 구현
3. §3.1.2 trap 3 line finish-task.sh 추가 + §5.1 6 hook
4. §5.2 5 entry + §5.2.1 spawn verification dispatch.py 추가
5. §7 14 fixture 박제
6. §13 8 completion conditions 모두 PASS
7. `memory/reports/task-2712.md` SCQA report (★ 14 fixture 결과 + completion 8 verbatim 박제)
8. `memory/events/task-2712.formalization-commit-260530.json` + `memory/events/task-2712.callback-envelope.json` (★ ANU key · UTF-8 ≤ 3900 bytes · collector_role=ANU · self_key_used=false)
9. finish-task.sh 정상 종료 (★ scope-guard PASS + callback registration)

### 14.2 callback envelope mandatory

- `collector_role=ANU` strict
- `collector_key=c119085addb0f8b7` (★ ANU key) strict
- `owner_key=c119085addb0f8b7` strict
- `self_key_used=false` strict
- `registration_mode="normal_callback_via_finish_task_sh_launcher_gate"`
- UTF-8 ≤ 3900 bytes hard (wc -c)

### 14.3 완료 후 흐름

dev4 → ANU normal callback → ANU 회장 보고 → task-2712+1 verifier 별도 결정

---

## 15. ANU Doctrine Compliance (★ 본 dispatch round)

- ★ ANU 자체 코드 구현 0 (★ dev4 위임)
- ★ ANU 자체 chair_authorization_id 발급 0 (★ 회장 verbatim 발급)
- ★ task-2710 / task-2711 / task-2706~2709+1 evidence 변경 0
- ★ ANU 자체 새 forbidden target 추가 0
- ★ ANU 자체 새 chair_authorization scope 확장 0
- ★ ANU 자체 OVERALL PASS / IMPLEMENTATION_DISPATCH_READY / FULL_ACCEPT 선언 0
- ★ ANU self-review PASS claim 0
- ★ Codex round 6 verdict + 회장 verbatim 인가 = 본 dispatch 인가 근거

---

## 16. Sentinel

★ task-2712 = FINAL · IMPLEMENTATION_DISPATCH_AUTHORIZED · LOCKED · chair_authorization_id CHAIR-AUTH-TASK-2712-FAILURE-CALLBACK-BEFORE-EXIT-GUARD-IMPLEMENTATION-260530 · executor dev4 Vishnu (★ dev1 제외) · 10 terminal state + 11 signal taxonomy + §3.1.2 trap 3 line + §3.1.3 RUNTIME_RESIDUAL_PROCESS · §3.2 6 marker (4 terminal + 2 fallback) + §3.2.4 4 status enum · §4 11 mandatory field + 3900 bytes hard + §4.3 7-case fallback + stderr/syslog operational · §5.1 6 inner hook + §5.2 5 entry + §5.2.1 spawn verification (N=15s) · §6.3 5 checkpoint + D1-D5 + §6.3.3 VIOLATION 1/2/3 + AUDIT_LOG_MISSING · §7 14 required fixture (F-1~F-14) · §8 4 module + finish-task.sh ~9 line + dispatch.py ~10-15 line · §9 23 expected_files + §9.1 finish-task.sh + dispatch.py allowed_existing_file_edits · §10 forbidden 9 · §11 implementation principle 10 · §12 preflight 6 · §13 completion condition 8 · §14 dev4 실행 지침 + callback envelope ANU key · finish-task.sh 변경 ≤9 line · dispatch.py 변경 ≤15 line · bot_settings/settings 변경 0 · PR/push/merge/GitHub write 0 · pilot dispatch 0 · task-2710/task-2711/task-2706~2709+1 evidence 변경 0 · dev1 오염 브랜치 재사용 0 · task-2711 재dispatch 0 · task-2711 .done 강제 0 · task-2703 오염 수정/삭제 0 · 완료 후 task-2712+1 verifier 별도. 끝