# task-2708 v2 — P2-A callback-before-failfast pre-registration layer (DRAFT v2)

★ **DRAFT v2** — 회장 verbatim "verdict B NEEDS_DRAFT_REVISION 확정 · task-2708 v2 초안 작성 · G1~G8 모두 초안 수준에서 해소" 정합. v1 (`memory/tasks/task-2708.md` sha `4ec809a668b2acc9305a3d5010f920a40c929d14e62d2431389e890cebea22f9`) 박제 보존 · v2 신규 파일.

★ dispatch 0 · 코드 수정 0 · finish-task.sh 수정 0 · helper 수정 0 · cron 수동 등록 0 · `.done` 생성 0 · PR/push/merge/GitHub write 0.

---

## 0. Header / Authorization Anchor

- **task_id**: `task-2708`
- **task_type**: `system_hook`
- **chair_authorization_id**: `CHAIR-AUTH-TASK-2708-P2A-CALLBACK-BEFORE-FAILFAST-PREREGISTRATION-260529`
- **draft_version**: `v2`
- **draft_v1_lineage**: `memory/tasks/task-2708.md` sha256 `4ec809a668b2acc9305a3d5010f920a40c929d14e62d2431389e890cebea22f9` (★ 보존 · revert 0)
- **dispatch_status**: **NOT_DISPATCHED** (★ 회장 verbatim '구현 dispatch 금지 · READY 나오기 전까지')
- **executor_team_candidate**: `dev1-team` (헤르메스 · Hermes) (★ 회장 결정)
- **verifier_team_candidate**: `dev2-team` (오딘 · Odin) (★ task-2708+1 별도)
- **anu_collector_key**: `c119085addb0f8b7`

---

## 1. Problem Statement

callback registration 이 fail-fast 보다 늦은 위치(L1532)에 있어, **29 fail_fast exit 1 path** 가 모두 callback 등록 0 으로 끝나는 구조. task-2707 production evidence = IMPLEMENTATION_PASS_ROUTING_FAIL (★ 회장 verbatim 분류 고정).

---

## 2. Goal

★ finish-task.sh 진입 직후 callback **pre-registration** 1회 결정성 통과 + Layer 1 validator + Layer 2 registrar fallback 으로 결과 회수 100% 보장.

---

## 3. R6~R14 확정 decision (★ v1 정합 유지)

| ID | 결정 | v2 구체화 위치 |
|---|---|---|
| R6 | MODIFY | §6 cancellation/BLOCKED/ESCALATED 3 sub-case 분리 |
| R7 | ACCEPT | §10.4 python helper sys.exit() 8건 제외 |
| R8 | ACCEPT | §4 Layer 1/2 분리 |
| R9 | MODIFY | §6 conditional cell + §7 task_type aware |
| R10 | ACCEPT | §5 recursion lock |
| R11 | ACCEPT | §8 "결정성 1회 통과" 3축 정의 |
| R12 | MODIFY | §7 OPTION A default + 5 exception 명시 |
| R13 | ACCEPT | §10.1/10.6 in_scope / out_of_scope |
| R14 | ACCEPT | §9 callback / escalation marker 동시 발행 |

---

## 4. G1 보강 — finish-task.sh 정확한 수정 범위

### 4.1 수정 후보 위치 3 hunk (★ verbatim line citations)

**Hunk A — Pre-registration layer 삽입** (★ 신규 약 50 라인)

- 위치: `scripts/finish-task.sh` L17~L20 영역 (★ TASK_ID/WORKSPACE/EVENTS_DIR 초기화 직후 · L20 정확 위치 회장 결정)
- 구체화: TASK_ID 사전 결정 후 callback envelope 파일 존재 확인 → pre-registration cron 1회 등록 (★ idempotent skip)
- before snippet (L17-L20 추정):
  ```bash
  TASK_ID="$1"
  WORKSPACE="${WORKSPACE:-/home/jay/workspace}"
  EVENTS_DIR="$WORKSPACE/memory/events"
  ```
- after snippet 추가 (★ ≈ 50 라인):
  ```bash
  # 5.0. [task-2708 P2-A] callback pre-registration layer (회장 verbatim · fail-fast 이전)
  # idempotent · R3/R4 정합 · re-entrant guard 적용
  if [ -z "${P2A_CALLBACK_PREREG_DONE:-}" ]; then
      export P2A_CALLBACK_PREREG_DONE=1
      CALLBACK_ENVELOPE_PATH_PREREG="${CALLBACK_ENVELOPE_PATH:-$EVENTS_DIR/anu_callback/${TASK_ID}-normal-completion.json}"
      PREREG_LOCK_FILE="$EVENTS_DIR/${TASK_ID}.callback-prereg-lock"
      if [ ! -f "$PREREG_LOCK_FILE" ] && [ -f "$CALLBACK_ENVELOPE_PATH_PREREG" ]; then
          # 1회 한정 helper launch · safe-fail
          ( cd "$WORKSPACE" && PYTHONPATH="$WORKSPACE" python3 -m dispatch.normal_fallback_callback_helper launch \
              --task-id "$TASK_ID" --kind normal \
              --owner-key "c119085addb0f8b7" --chat-id "6937032012" \
              --envelope-path "$CALLBACK_ENVELOPE_PATH_PREREG" \
              --output "$EVENTS_DIR/${TASK_ID}.callback-prereg-launch.json" 2>/dev/null \
              && touch "$PREREG_LOCK_FILE" \
              && echo "[P2A] callback pre-registration: ANU-owned PASS" \
              || echo "[P2A] callback pre-registration: fail-closed/non-blocking" )
      fi
  fi
  ```

**Hunk B — Layer 1 validator fallback trigger** (★ L1132-1149 영역 수정 · 약 8 라인)

- 위치: 현재 L1131-1149 (NORMAL-CALLBACK-ENFORCE FAIL 분기)
- 의도: validator FAIL 시 `exit 1` 대신 Layer 2 registrar fallback 트리거 1회 호출 후 marker 박제
- before (현재):
  ```bash
  if [ "$CALLBACK_ENFORCE_EXIT" -ne 0 ]; then
      MARKER_FILE="$EVENTS_DIR/${TASK_ID}.normal-callback-not-registered.json"
      ...
      exit 1
  fi
  ```
- after (★ recursion lock + fallback):
  ```bash
  if [ "$CALLBACK_ENFORCE_EXIT" -ne 0 ]; then
      MARKER_FILE="$EVENTS_DIR/${TASK_ID}.normal-callback-not-registered.json"
      ...
      # P2-A: Layer 2 fallback 1회 호출 (re-entrant guard)
      if [ -z "${P2A_LAYER2_FALLBACK_DONE:-}" ]; then
          export P2A_LAYER2_FALLBACK_DONE=1
          # Layer 2 registrar fallback inline trigger (★ L1532 logic 호출 또는 helper launch 직접)
          ( cd "$WORKSPACE" && PYTHONPATH="$WORKSPACE" python3 -m dispatch.normal_fallback_callback_helper launch \
              --task-id "$TASK_ID" --kind fallback \
              --owner-key "c119085addb0f8b7" --chat-id "6937032012" \
              --envelope-path "$CALLBACK_ENVELOPE_PATH" \
              --output "$EVENTS_DIR/${TASK_ID}.callback-fallback-launch.json" 2>/dev/null || true )
      fi
      exit 1  # Layer 1 validator FAIL 자체는 .done 차단 유지 (★ 기존 doctrine)
  fi
  ```

**Hunk C — Layer 2 registrar idempotent skip** (★ L1550 수정 · 약 5 라인)

- 위치: 현재 L1550 (callback helper launch)
- 의도: Pre-registration 이 이미 완료된 경우 idempotent skip · duplicate 방지
- before (현재):
  ```bash
  ( cd "$WORKSPACE" && PYTHONPATH="$WORKSPACE" python3 -m dispatch.normal_fallback_callback_helper launch ...)
  ```
- after (★ idempotent skip):
  ```bash
  PREREG_LOCK_FILE="$EVENTS_DIR/${TASK_ID}.callback-prereg-lock"
  if [ -f "$PREREG_LOCK_FILE" ]; then
      echo "[task-2708 P2-A] Layer 2 registrar SKIP (pre-registration already complete)"
  else
      ( cd "$WORKSPACE" && PYTHONPATH="$WORKSPACE" python3 -m dispatch.normal_fallback_callback_helper launch ...)
  fi
  ```

### 4.2 변경 라인 hard limit

- **Hunk A**: ≤ 50 라인 추가
- **Hunk B**: ≤ 15 라인 수정 (★ +12 / -3)
- **Hunk C**: ≤ 10 라인 수정 (★ +7 / -1)
- **TOTAL ≤ 75 라인 수정 (★ hard limit)**

### 4.3 Layer 1 validator 위치

- **유지** (★ L1106-1160 현재 위치)
- 변경 사항: Hunk B 의 fallback trigger (★ Layer 1 self-fail 시 Layer 2 호출)

### 4.4 Layer 2 registrar 위치

- **유지** (★ L1532-1558 현재 위치)
- 변경 사항: Hunk C 의 idempotent skip (★ pre-registration 이미 완료 시 skip)

### 4.5 callback registration 흐름

```
finish-task.sh 진입
  ↓
[Hunk A] Pre-registration (★ envelope 존재 시 helper launch · idempotent)
  ↓
기존 fail-fast 17+ path 중 일부 또는 정상 path
  ↓
[L1106-1160] Layer 1 validator (★ Hunk B 분기 · fail 시 fallback)
  ↓
[L1532-1558] Layer 2 registrar (★ Hunk C 분기 · pre-reg 완료 시 skip)
  ↓
finish-task.sh 종료
```

### 4.6 정상 success path 깨지지 않는 근거

- Hunk A 는 envelope 존재 시에만 진입 + idempotent (lock file)
- Hunk B 는 validator FAIL 분기에서만 동작 + re-entrant guard
- Hunk C 는 lock file 존재 시에만 skip (★ duplicate 방지) · 기존 logic 분기 변경 0
- 기존 fail-fast logic (L282/L321/L359/L377/L451 등 28+) **변경 0**
- 기존 scope-guard / QC / taskctl logic **변경 0**

---

## 5. G3 보강 — Layer 1 ↔ Layer 2 recursion lock

### 5.1 validator failure 시 registrar fallback 호출 — **YES**

- §4.1 Hunk B 의 `P2A_LAYER2_FALLBACK_DONE` 환경변수로 1회 한정
- 호출 시점: Layer 1 validator FAIL 직후 (★ L1149 exit 1 직전)
- argument: `--kind fallback` (★ §6 normal vs fallback 분리)

### 5.2 registrar failure marker 처리

- `{task_id}.callback-fallback-not-registered.json` 발행 + safe-fail (★ §9.2 marker schema)
- 추가 exit 0 (★ marker 박제 후 진행 가능)

### 5.3 re-entrant guard

- **환경변수 방식**: `P2A_CALLBACK_PREREG_DONE` (Hunk A) + `P2A_LAYER2_FALLBACK_DONE` (Hunk B)
- subprocess 안에서 환경변수 상속 → 동일 task 내 재진입 차단

### 5.4 recursion lock key

- `task_id::callback_kind` (★ task-2626 baseline 정합)
- per-process lock file: `events/{task_id}.callback-prereg-lock`

### 5.5 retry count

- pre-registration: **1회 한정** (★ env var + lock file)
- fallback: **1회 한정** (★ env var)
- total per task: **최대 2회 helper launch** (pre-reg + fallback) · normal Layer 2 는 skip

### 5.6 Layer 1 self-fail 시 callback storm 방지

- §5.3 env var + §5.4 lock file 2축 가드
- Layer 1 자체가 Layer 2 를 호출할 때는 Layer 1 재진입 차단 (★ Hunk B `P2A_LAYER2_FALLBACK_DONE=1` 후 후속 Layer 1 호출 자체 0)

---

## 6. G2 보강 — R6 3 sub-case + R9 conditional cell + R12 N exception

### 6.1 R6 — cancellation / BLOCKED / ESCALATED 3 sub-case 분리

| sub-case | trigger | callback_kind | 처리 |
|---|---|---|---|
| **cancellation** | `.cancelled` marker 존재 (L67/L80/L470) | `skip` | callback 등록 0 (★ intentional skip · task 자체 취소) |
| **BLOCKED** | `qc_result=BLOCKED` + L510 timer end | `escalate` | callback 등록 YES · escalation marker 동시 발행 |
| **ESCALATED** | `qc_result=ESCALATED` + L526 timer end | `escalate` | callback 등록 YES · escalation marker 동시 발행 |

★ R6 MODIFY 정합 — 3 sub-case 의 callback_kind 분기 명시.

### 6.2 R9 conditional cell — task_type override default

| task_type | exit_category | default override | rationale |
|---|---|---|---|
| `read_only` | `qc_fail` | `escalate` + `chair_required` (★ §7 exception cell) | Maat verifier QC FAIL 시 회장 보고 |
| `system_hook` | `scope_guard_fail` | `hold_for_chair` (★ §7 exception cell) | finish-task.sh 수정 task scope-guard FAIL = critical |
| `callback_only` | `callback_path` | `skip` (★ §7 exception cell) | callback layer 자체 recursion 회피 |
| `formalization_commit_only` | `qc_fail` | `escalate` + `chair_required` (★ §7 exception cell · 회장 verbatim 고정) | task-2707 evidence 정합 |
| `closeout_marker_only` | `dirty_workspace_fail` | `hold_for_chair` (★ §7 exception cell) | closeout marker 작성 task 의 workspace corruption = critical |

★ R9 MODIFY 정합 — task_type 별 default kind override 5 cell 명시.

### 6.3 R12 OPTION A — default + 5 exception list

- §6.1 base default (★ 11 exit category × 7 kind = 77 cell default 정합)
- §6.2 5 exception cell (★ N=5)
- 나머지 cell = §6.1 default 그대로 적용 (★ 의도된 단순화)

---

## 7. G4 보강 — matrix 11 × 7 정밀화 (★ cancellation 분리 후)

### 7.1 11 exit category × 7 callback_kind default

| exit_category | default kind |
|---|---|
| `success_exit` | `normal` |
| `qc_fail` | `escalate` |
| `scope_guard_fail` | `escalate` |
| `taskctl_fail` | `hold_for_chair` |
| `dirty_workspace_fail` | `hold_for_chair` |
| `escalation_path` | `escalate` |
| `callback_path` (★ validator self-fail) | `hold_for_chair` |
| `callback_path` (★ registrar self-fail) | `fallback` |
| `fail_fast_generic` | `fail_fast` |
| `cancellation` (★ R6 분리) | `skip` |
| `BLOCKED / ESCALATED` (★ R6 분리) | `escalate` |

★ conditional cell **제거** → §6.2 5 exception cell 로 명시화.

---

## 8. G5 보강 — OPTION A exception list (★ 5 cell verbatim)

### 8.1 5 exception cell

| task_type × exit | should_register | callback_kind | chair_required | marker_required |
|---|---|---|---|---|
| `read_only × qc_fail` | YES | `escalate` | YES | YES (★ callback marker + escalation marker) |
| `system_hook × scope_guard_fail` | YES | `hold_for_chair` | YES | YES |
| `callback_only × callback_path` | NO | `skip` | NO | YES (★ recursion 회피 audit marker) |
| `formalization_commit_only × qc_fail` | YES | `escalate` | YES | YES (★ task-2707 evidence 정합 · 회장 verbatim 고정) |
| `closeout_marker_only × dirty_workspace_fail` | YES | `hold_for_chair` | YES | YES |

### 8.2 그 외 cell — §7.1 default 적용

★ N exception = 5 확정 · 회장 verbatim 정합.

---

## 9. G7 보강 — safety (★ duplicate enforcement + idempotency + marker namespace)

### 9.1 duplicate enforcement 단계

- **Stage 1**: helper launch 전 idempotency check (★ §4.1 Hunk A `PREREG_LOCK_FILE` 존재 확인)
- **Stage 2**: cron registration 후 cron_id 패턴 `task_id::callback_kind` (★ task-2626 baseline)
- **Stage 3**: marker check (★ `{task_id}.callback-*.json` 패턴)

### 9.2 idempotency key

- **primary**: `task_id::callback_kind` (★ task-2626 baseline 정합)
- **lock file**: `events/{task_id}.callback-prereg-lock` + `events/{task_id}.callback-fallback-lock`

### 9.3 marker namespace (★ collision 회피)

- pre-registration success: `{task_id}.callback-prereg-launch.json`
- Layer 2 registrar success: `{task_id}.callback-launch.json` (★ task-2626 baseline 유지)
- Layer 2 fallback success: `{task_id}.callback-fallback-launch.json`
- pre-registration failure: `{task_id}.callback-prereg-not-registered.json`
- Layer 1 validator failure: `{task_id}.normal-callback-not-registered.json` (★ 기존 schema 유지)
- Layer 2 fallback failure: `{task_id}.callback-fallback-not-registered.json`
- escalation: `{task_id}.escalate` (★ 기존 schema 유지)

### 9.4 safe-fail rule

1. helper launch FAIL → marker emit + safe-fail (★ exit code 영향 0)
2. lock file write FAIL → silent skip (★ filesystem 권한 문제 시 fail-closed 회피)
3. recursion lock 감지 → silent skip + audit log
4. marker collision 감지 → `{task_id}.callback-collision.json` 박제

---

## 10. G8 보강 — forbidden scope 명확화

### 10.1 in_scope (★ §10.3 expected_files)

- `scripts/finish-task.sh` (★ §4.1 Hunk A/B/C 만 · TOTAL ≤ 75 라인)
- `scripts/harness/v36/callback_preregistration.py` (★ 신규 helper · §4.1 Hunk A 모듈화 후보)
- `tests/harness/test_p2a_callback_preregistration.py` (★ 신규 contract test · §11 fixture)

### 10.2 out_of_scope (★ §10.4 forbidden_files 명확한 식별 기준)

| 파일 / 패턴 | rationale |
|---|---|
| `dispatch.py` | 신규 dispatch logic 변경 영역 외 |
| `dispatch/__init__.py` | callback registration 의 caller-side · helper-side 분리 doctrine |
| `dispatch/normal_fallback_callback_helper.py` | helper 자체는 R11 시간 한계 + idempotency key 의 기반 · 별도 round |
| `scripts/session-watchdog.sh` | watchdog 영역 분리 doctrine |
| `.claude/settings.json` | settings json 변경 금지 doctrine |
| `/home/jay/.claude/settings.json` | global settings 동일 |
| `scripts/harness/v36/dispatch_marker_writer.py` | task-2707 산물 보존 |
| `qc_verify.py` | **★ P2-D 영역** (★ tdd_check task_type 인식 추가 · P2-A 영역 외) |
| `utils/merge_queue_executor.py` / `utils/real_merge_hooks.py` | merge layer 분리 |
| `anu_v3/` | 별도 system layer |
| `glob: tests/**/*.py` 외 신규 test | RS-1~5 fixture 외 신규 test 금지 |
| `glob: dispatch/**/*.py` 외 helper | 신규 helper 작성 영역 외 |

### 10.3 P2-A ↔ P2-D 경계 명시

- **P2-A (본 task)**: finish-task.sh routing layer (★ Hunk A/B/C 만)
- **P2-D (별도 task · 회장 별도 인가)**: `qc_verify.py` 의 task_type 인식 + tdd_check FORMALIZATION_COMMIT_ONLY 면제 logic
- P2-A 만으로 task-2707 routing fail 회복: **YES** (★ Hunk A pre-registration 이 fail-fast 이전 통과 보장)
- P2-D 는 doctrine cleanup (★ task-2707 같은 doctrine conflict 재발 방지) · P2-A 와 별도

### 10.4 forbidden_files (★ verbatim 강제 sha256 동일)

- `dispatch.py` / `dispatch/__init__.py` / `dispatch/normal_fallback_callback_helper.py` / `scripts/session-watchdog.sh` / `.claude/settings.json` / `/home/jay/.claude/settings.json` / `scripts/harness/v36/dispatch_marker_writer.py` / `qc_verify.py` / `utils/merge_queue_executor.py` / `utils/real_merge_hooks.py` / `anu_v3/`

### 10.5 forbidden_actions (★ 9 강제)

1. 코드 수정 금지 (★ §10.1 scope 밖)
2. finish-task.sh 수정 금지 (★ §4.1 Hunk A/B/C 외)
3. helper 수정 금지 (★ dispatch/normal_fallback_callback_helper.py 등)
4. dispatch 금지 (★ 본 v2 = DRAFT only)
5. callback cron 수동 생성 금지
6. `.done` 생성 금지
7. PR / branch push / merge 금지
8. GitHub write 금지
9. P2-D / P2-B / P2-C 구현 금지

### 10.6 Capability Scope YAML (★ dispatch.py L4769 parser · 회장 인가 후 활성)

```yaml
allowed_resources:
  expected_files:
    - scripts/harness/v36/callback_preregistration.py
    - tests/harness/test_p2a_callback_preregistration.py
    - memory/reports/task-2708.md
    - memory/events/task-2708.formalization-commit-260529.json
    - memory/events/task-2708.callback-envelope.json
  allowed_existing_file_edits:
    - scripts/finish-task.sh
  forbidden_paths:
    - dispatch.py
    - dispatch/__init__.py
    - dispatch/normal_fallback_callback_helper.py
    - scripts/session-watchdog.sh
    - .claude/settings.json
    - /home/jay/.claude/settings.json
    - scripts/harness/v36/dispatch_marker_writer.py
    - qc_verify.py
    - utils/merge_queue_executor.py
    - utils/real_merge_hooks.py
    - anu_v3/
```

---

## 11. G6 보강 — regression pytest fixture (★ 5 RS · pass/fail 기준)

### 11.1 fixture 경로

- `tests/harness/test_p2a_callback_preregistration.py` (★ 신규 contract test)
- fixture 5: RS-1 / RS-2 / RS-3 / RS-4 / RS-5 각각 별도 fixture function

### 11.2 RS-1 — task-2707 tdd_check fail-fast → callback 등록 보장

- **fixture**: `tests/fixtures/p2a/task-2707-tdd-check-failfast.json` (★ envelope 800 bytes + QC tdd_check FAIL × 4 mock)
- **pass criteria**:
  - `events/{task_id}.callback-prereg-launch.json` 존재 + `status=ANU_OWNED_READY`
  - cron-list ANU key 에 `cron_id=task_id::normal` 존재
- **fail criteria**: marker 부재 또는 cron 부재 → FAIL

### 11.3 RS-2 — scope_guard fail-fast → callback 등록 보장

- **fixture**: `tests/fixtures/p2a/scope-guard-violation-35.json` (★ scope-guard 35 violations mock)
- **pass criteria**:
  - pre-reg marker + escalation marker 양 발행 (★ §9.3 namespace)
  - callback_kind = `escalate`
- **fail criteria**: 양 marker 중 1건 부재 → FAIL

### 11.4 RS-3 — success_exit normal callback 유지

- **fixture**: `tests/fixtures/p2a/success-baseline-5.json` (★ task-2626/2661/2705+4/2706/2706+1 baseline mock)
- **pass criteria**:
  - 기존 `callback-launch.json` schema 유지 (★ task-2626 baseline)
  - cron_id 패턴 `task_id::normal` 유지
  - `.done` 정상 생성
- **fail criteria**: 기존 schema 변경 → FAIL · `.done` 미생성 → FAIL

### 11.5 RS-4 — duplicate callback idempotent skip

- **fixture**: `tests/fixtures/p2a/duplicate-call-mock.json` (★ 동일 task_id::callback_kind 2회 호출 mock)
- **pass criteria**:
  - 1차 호출: `callback-prereg-launch.json` 발행
  - 2차+ 호출: `PREREG_LOCK_FILE` 존재 감지 → silent skip + audit log
  - cron 등록: 1건만 (★ duplicate 0)
- **fail criteria**: 2건 cron 등록 또는 marker 덮어쓰기 → FAIL

### 11.6 RS-5 — Layer 1 validator self-fail recursion 방지

- **fixture**: `tests/fixtures/p2a/layer1-self-fail-mock.json` (★ Layer 1 validator FAIL mock)
- **pass criteria**:
  - `P2A_LAYER2_FALLBACK_DONE=1` 환경변수 설정 후 Layer 2 fallback 1회 호출
  - `callback-fallback-launch.json` 발행 (★ §9.3 namespace)
  - Layer 1 재진입 0 (★ recursion lock 동작)
- **fail criteria**: Layer 1 재진입 또는 무한 Layer 2 호출 → FAIL

### 11.7 측정 방식

- **pytest fixture** (★ 회장 추천 가능성 영역 · 본 v2 default 선택)
- 실행: `pytest tests/harness/test_p2a_callback_preregistration.py -v`
- pass criteria: 5/5 PASS hard 강제

---

## 12. Acceptance Criteria

1. ★ §4.1 Hunk A/B/C **TOTAL ≤ 75 라인** 변경 강제
2. ★ §4.6 정상 success path 깨지지 않는 근거 검증
3. ★ §5 recursion lock 5축 동작 (★ R10 정합)
4. ★ §6 R6/R9/R12 MODIFY 실현 (★ 3 sub-case + conditional + N=5)
5. ★ §7 11 × 7 matrix default + §8 5 exception cell 매핑
6. ★ §9 idempotency key + marker namespace + safe-fail
7. ★ §10 forbidden_files 11건 sha256 동일 (★ HEAD~1==HEAD)
8. ★ §11 5 RS pytest fixture **5/5 PASS**
9. ★ §10.5 forbidden_actions 9 위반 0
10. ★ task-2707 routing fail caveat 해소 (★ task-2707 finish-task 재실행 시 callback 정상 등록 확인)
11. ★ GitHub write 0 / PR 0 / branch push 0 / merge 0

---

## 13. Callback Envelope Schema (★ ANU collector contract)

```json
{
  "task_id": "task-2708",
  "chair_authorization_id": "CHAIR-AUTH-TASK-2708-P2A-CALLBACK-BEFORE-FAILFAST-PREREGISTRATION-260529",
  "executor": "dev1-team / 헤르메스",
  "collector_role": "ANU",
  "collector_key": "c119085addb0f8b7",
  "result_path": "memory/events/task-2708.formalization-commit-260529.json",
  "report_path": "memory/reports/task-2708.md",
  "p2a_pre_registration_layer_implemented": true,
  "hunk_a_lines_added": "<N>",
  "hunk_b_lines_modified": "<N>",
  "hunk_c_lines_modified": "<N>",
  "total_lines_changed": "<N> (★ ≤ 75)",
  "regression_5_pass_count": "<N>/5",
  "summary_one_line": "P2-A pre-registration layer · R8/R10/R11 정합 · regression N/5 PASS"
}
```

★ envelope UTF-8 byte ≤ 3900 hard limit.

---

## 14. Safe-Fail Strategy

- 모든 step unexpected error → 즉시 멈춤 + `.escalate` marker
- forbidden_files sha 변화 감지 → 즉시 멈춤 + `.escalate`
- regression 5 중 1건이라도 FAIL → 즉시 멈춤 + `.escalate`
- §4.1 Hunk A/B/C 외 finish-task.sh 수정 감지 → 즉시 멈춤 + 회장 재인가 요청
- TOTAL 라인 변경 > 75 감지 → 즉시 멈춤 + 회장 재인가 요청

---

## 15. Out-of-Scope (★ 명시 강제)

- ★ P2-B (all-exit-path callback finalizer)
- ★ P2-C (idempotency marker enforcement 별도)
- ★ P2-D (★ task_type aware fail-fast routing · qc_verify.py · §10.3 명시)
- ★ tdd_check verifier 자체 수정
- ★ dispatch.py / dispatch/__init__.py / dispatch/normal_fallback_callback_helper.py 수정
- ★ session-watchdog.sh / .claude/settings.json 수정
- ★ Goal-to-Done / PHASE_AUTO / Core-Work 이원화

---

## 16. Chair Decision Pending (★ 회장 결정 6 영역)

1. ★ 본 v2 초안 승인 여부 (★ 8 axis × 4 enum 재검토 후 READY 전환 가능성 평가)
2. ★ §4.1 Hunk A/B/C 정확한 라인 (L17~L20 영역 / L1132-1149 / L1550) 회장 확정
3. ★ §11 pytest fixture vs 다른 측정 방식 확정
4. ★ §10.3 P2-A ↔ P2-D 경계 회장 확정
5. ★ §10.5 forbidden_actions 9 강제 정합
6. ★ executor dev1 헤르메스 dispatch 인가 — READY 판정 + 회장 별도 인가

---

## 17. Linked Markers

- `memory/tasks/task-2708.md` (★ v1 · 보존 · sha 4ec809a66...)
- `memory/reports/task-2708_draft_review_260529.md` (★ v1 review · 25 GAP · verdict B)
- `memory/events/task-2708.draft-review-decision-260529.json` (★ verdict B fixed)
- `memory/specs/p2_contract_refinement_r6_r14_260529.md` (★ R6~R14 refinement)
- `memory/events/p2_contract_refinement_decision_260529.json`
- `memory/specs/p2_exit_path_coverage_audit_260529.md` (★ 41 exit + R1~R5 NOT_COVERED)
- `memory/events/p2_exit_path_coverage_decision_260529.json`
- `memory/specs/p2_finish_task_callback_before_failfast_contract_draft_260529.md` (★ R1~R5 원본)
- `memory/events/c1_dispatch_marker_writer_formalization_accepted_with_known_caveats_chair_closeout_260529.json`
- `memory/events/task-2707+1.decision.json` (★ verifier 12/12 PASS)
- `memory/events/task-2707.implementation-pass-routing-fail-260529.json`
- `memory/events/p1b_finish_task_profile_contract_accepted_with_known_caveats_260529.json`
- `scripts/finish-task.sh` (★ §4.1 Hunk A/B/C 대상 · v2 spec 만)

---

## 18. ANU Doctrine Compliance

- ★ ANU 자체 P2-A 구현 0 (★ 본 v2 = DRAFT only)
- ★ ANU 자체 코드 수정 0 / finish-task.sh 수정 0 / helper 수정 0 / dispatch 0
- ★ ANU 자체 callback cron 수동 등록 0 / `.done` 생성 0
- ★ ANU 자체 PR / branch push / merge / GitHub write 0
- ★ ANU 자체 분류 결정 0 (★ R6~R14 + §6 sub-case + §7 matrix + §8 N exception 모두 회장 verbatim 매핑)
- ★ ANU 자체 신규 rule 신설 0
- ★ self-collector callback 금지 doctrine 정합 (★ ANU key c119085addb0f8b7 강제)
- ★ lineage rewrite 0 (★ v1 박제 보존)

---

## 19. Sentinel

★ 본 v2 = P2-A DRAFT v2. 회장 별도 인가 전 dispatch 0 · 코드 수정 0 · finish-task 수정 0 · helper 수정 0 · cron 수동 등록 0 · `.done` 생성 0 · PR/push/merge 0 · GitHub write 0. 끝

## goal_assertions (auto-generated)
- `pytest tests/harness/test_p2a_callback_preregistration.py -v`
