# task-2708 보고서 — P2-A callback-before-failfast pre-registration layer

- **작업 ID**: task-2708
- **팀**: dev1-team (헤르메스/Hermes 팀장)
- **회장 인가**: `CHAIR-AUTH-TASK-2708-P2A-CALLBACK-BEFORE-FAILFAST-PREREGISTRATION-260529`
- **작업 일시**: 2026-05-29 (server time 17:54 dispatch)
- **검증 레벨**: normal
- **work_level / project_id**: normal / system

---

## SCQA 요약

- **S (Situation)**: finish-task.sh 의 callback registration(L1532 영역)이 fail-fast(exit 1) 보다 늦은 위치에 있어, 29개 fail-fast exit path 가 모두 callback 등록 0으로 끝난다. task-2707 production = IMPLEMENTATION_PASS_ROUTING_FAIL.
- **C (Complication)**: callback 회수가 누락되면 결과 routing 이 끊긴다. 그러나 finish-task.sh 는 시스템 핵심 완료 경로이므로 정상 success path 를 절대 깨뜨릴 수 없고, 변경은 최소(≤75라인)·idempotent·non-blocking 이어야 한다.
- **Q (Question)**: fail-fast 이전에 ANU normal callback 을 1회 결정성 등록하면서, 기존 success path·머지·QC·28개 fail-fast 로직을 변경 0으로 유지할 수 있는가?
- **A (Answer)**: finish-task.sh 진입 직후(Hunk A) ANU-key 전용 pre-registration layer 를 삽입하고, Layer 1 validator FAIL 시 Layer 2 fallback 을 re-entrant guard 로 1회 호출(Hunk B), Layer 2 registrar 는 pre-reg lock 존재 시 idempotent skip(Hunk C). 신규 helper `callback_preregistration.py` 로 모듈화하여 finish-task.sh 변경을 +38라인으로 한정. RS-1~5 회귀 5/5 PASS, 기존 harness 413 PASS(회귀 0).

---

## 1. 작업 내용

P2-A pre-registration layer 를 v2 스펙(task-2708.md §4/§5/§9) 에 맞춰 구현했다.

1. **신규 helper** `scripts/harness/v36/callback_preregistration.py` (235라인)
   - `dispatch.normal_fallback_callback_helper.launch_callback` 경유(helper 자체 수정 0)
   - idempotent(lock 파일) · non-blocking(모든 예외 → marker 박제 후 exit 0) · ANU-key only(self-key 는 launcher 가 fail-closed)
2. **finish-task.sh 3 Hunk** (+38라인, hard limit 75 이내)
   - Hunk A: 진입 직후 pre-registration layer (+22)
   - Hunk B: Layer 1 validator FAIL → Layer 2 fallback 1회 트리거 (+12, re-entrant guard)
   - Hunk C: Layer 2 registrar idempotent skip (+4, pre-reg lock 존재 시)
3. **회귀 테스트** `tests/harness/test_p2a_callback_preregistration.py` + fixture 5종 (RS-1~5)

---

## 2. 생성/수정 파일 목록 (planned/verified)

신규/수정 파일별 grep·테스트 검증 상태:

- `scripts/harness/v36/callback_preregistration.py` — 신규 helper(launch subcommand) — `python3 --help` + AST OK + smoke 4종 PASS — **verified**
- `scripts/finish-task.sh` (Hunk A, L12 영역) — pre-registration layer — `grep "task-2708 P2A"` 4건, `bash -n` PASS — **verified**
- `scripts/finish-task.sh` (Hunk B, L1180 영역) — Layer 2 fallback trigger + re-entrant guard — `grep P2A_LAYER2_FALLBACK_DONE` OK — **verified**
- `scripts/finish-task.sh` (Hunk C, L1583 영역) — Layer 2 idempotent skip — `grep P2A_PREREG_LOCK_C` OK — **verified**
- `tests/harness/test_p2a_callback_preregistration.py` — RS-1~5 회귀 테스트 — pytest 5/5 PASS — **verified**
- `tests/fixtures/p2a/task-2707-tdd-check-failfast.json` — RS-1 fixture — 테스트 로드 OK — **verified**
- `tests/fixtures/p2a/scope-guard-violation-35.json` — RS-2 fixture — **verified**
- `tests/fixtures/p2a/success-baseline-5.json` — RS-3 fixture — **verified**
- `tests/fixtures/p2a/duplicate-call-mock.json` — RS-4 fixture — **verified**
- `tests/fixtures/p2a/layer1-self-fail-mock.json` — RS-5 fixture — **verified**

planned(미검증) 항목: **0건**.

커밋(local only, push/PR/merge 0):
- `b8f9276` 불칸: helper + finish-task.sh Hunk A/B/C
- `7a86782` 불칸: unused os import 제거
- `3c7b371` 아르고스: RS-1~5 회귀 테스트
- `a5307b7` 아르고스: pyright 정리(importlib spec assertion + unused import)

---

## 3. 테스트 결과 (Evidence)

- **RS-1~5 회귀**: `pytest tests/harness/test_p2a_callback_preregistration.py` → **5 passed in 0.16s**
  - test_rs1_failfast_path_registers_callback PASSED
  - test_rs2_scope_guard_failfast_registers PASSED
  - test_rs3_success_baseline_normal_callback PASSED
  - test_rs4_duplicate_idempotent_skip PASSED
  - test_rs5_layer1_self_fail_no_recursion PASSED
- **기존 harness 회귀**: `pytest tests/harness/` → **413 passed in 2.89s** (회귀 0건)
- **bash 문법**: `bash -n scripts/finish-task.sh` → PASS
- **라인수**: `git show -- scripts/finish-task.sh | grep -c '^+'` → 37 (순수 추가, hard limit 75 이내)
- **금지파일 불변**: `git diff --name-only b8f9276^ HEAD | grep <forbidden 11종>` → 0건 (PASS)

---

## 4. L1 스모크테스트 결과 (필수 기록)

본 작업은 system_hook(bash/python) 작업이므로 DIRECT-WORKFLOW §4.8 "subprocess/정제 작업" L1 기준을 적용했다(실제 프로세스 실행 + 결과 파일 확인).

- **서버 재시작**: 해당없음 (웹 서버/대시보드 작업 아님 — finish-task.sh 완료 hook)
- **API 응답 확인**: 해당없음 (HTTP API 아님 — 대신 helper subprocess 실제 실행으로 대체)
- **스크린샷**: 해당없음 (CLI/bash 작업 — 프론트 변경 0)
- **subprocess 실제 실행 (L1 통과)**:
  - SMOKE 1 envelope 부재 → skip, exit 0 ✓
  - SMOKE 2 owner==self-key → FAIL_CLOSED marker, exit 0(non-blocking) ✓
  - SMOKE 3 owner==ANU key → PASS, lock + `callback-prereg-launch.json` status=ANU_OWNED_READY verdict=PASS ✓
  - SMOKE 4 lock 존재 → idempotent silent skip, 마커 재생성 0, launch_callback 미호출 ✓
  - **L1 E2E (bash-level Hunk A)**: 가짜 envelope 로 Hunk A bash 블록 그대로 실행 → helper 가 ANU-owned argv 생성(`cron_id=*::normal`, `--key c119085addb0f8b7 --once`, `collector_role=ANU`, `owner_is_independent_anu=true`) + 마커/lock 생성 + "ANU-owned PASS" echo 확인 ✓

L1 미통과 항목: 없음 (최소 1개 이상 실제 실행 + 통과 충족).

---

## 5. 발견 이슈 및 해결 (Zero Issue = Red Flag 대응)

1. **스펙 snippet ↔ 실제 helper CLI 불일치 (HIGH)**: v2 스펙 §4.1 의 illustrative snippet 은 `launch --envelope-path --output` 를 사용하나, 실제 `dispatch.normal_fallback_callback_helper launch` CLI 는 `--prompt --at --executor-key --owner-key --chat-id --kind --task-id` 를 요구. → **해결**: helper 수정 금지 doctrine 준수하며 신규 `callback_preregistration.py` 가 실제 CLI(launch_callback 함수) 를 정확히 경유하도록 구현. 스펙 snippet 은 §4.1 명시대로 "illustrative" 로 취급.
2. **helper unused `os` import (LOW, pyright)**: → **해결**: import 제거(commit 7a86782).
3. **test 파일 pyright ✘ 9건 (importlib ModuleSpec|None 내로잉 + unused `types`) (MEDIUM)**: → **해결**: 3개 spec 로드 지점에 `assert spec is not None and spec.loader is not None` 추가 + unused import 제거(commit a5307b7). 잔여는 cosmetic ★ hint(unused pytest/**kwargs)만 — 테스트 mock 관례상 비차단.
4. **Hunk A 위치가 cancellation 체크 이전 (LOW, known caveat)**: 회장 지정 위치(§4.1 "TASK_ID/WORKSPACE/EVENTS_DIR 초기화 직후")가 L60 cancellation 체크보다 앞. cancelled task 가 finish-task.sh 를 잘못 호출하면 pre-reg 가 동작. → **분석**: doctrine 상 cancelled task 는 finish-task.sh 호출 금지이며, pre-reg 는 non-blocking·idempotent 이라 실해 0. 회장 지정 위치 준수가 우선이므로 caveat 으로 기록(P2-A scope 내 미수정).
5. **pyright import 해석 경고 (cosmetic)**: `dispatch.normal_fallback_callback_helper` 가 정적 분석에서 미해석(PYTHONPATH 미설정). → **분석**: 런타임(PYTHONPATH=/home/jay/workspace)에서 정상 해석(smoke 검증). 동일 v36 모듈 다수가 같은 패턴 — 비차단.

범위 외 미해결 이슈: 없음.

---

## 6. 게이트 결과

- **G1 (설계 게이트)**: affected_files = finish-task.sh + 신규 helper + 신규 test/fixture. 다른 팀/타 task 와 파일 겹침 0. forbidden_files 11종 미수정 확인(`HEAD~ vs HEAD` 0건).
- **G2 (구현 게이트)**: 팀 테스터(아르고스) RS-1~5 5/5 PASS + 기존 harness 413 PASS. 기능 테스트(smoke 4종 + bash E2E) 완료.
- **G3 (머지 게이트)**: ★ task 스펙 §10.5/§12.11/§15/§18/§19 가 **PR/branch push/merge/GitHub write 0** 을 명시(acceptance criterion #11). 본 system_hook 작업은 worktree 미생성(project_id=system) + 스펙 강제에 따라 **PR/머지 미수행, local commit only**. (이는 일반 G3 PR 흐름과 충돌하나, task-specific·회장 작성·acceptance-encoded 금지가 우선하므로 PR 미생성. 머지 판단은 아누에게 위임.)

---

## 7. 머지 판단

- **머지 필요**: No (task 스펙 §12.11 PR/merge/push 0 강제 — local commit only)
- **브랜치**: task/task-2703-v36-harness-dev1 (system 작업, worktree 미생성 — project_id=system)
- **워크트리 경로**: 해당없음
- **머지 의견**: 4개 local commit(b8f9276/7a86782/3c7b371/a5307b7) 으로 기록. 작업트리에 무관한 기존 dirty 변경 다수 존재 → `git add -A` 미사용, 대상 파일만 개별 add. 머지/push 는 스펙상 금지이므로 아누 판단 대기.

---

## 8. 셀프 QC 8항목 점검

1. 다른 파일 영향: finish-task.sh 가 신규 helper 를 호출 — helper 신규 생성으로 의존 충족, 기존 호출부 변경 0.
2. 엣지 케이스: envelope 부재/lock 존재/self-key/예외 — 4종 smoke 로 커버.
3. 작업 지시 일치: §4 Hunk A/B/C + §5 recursion lock(env var 2종 + lock file 2종) + §9 marker namespace 구현.
4. 에러 처리/보안: 모든 예외 try/except→exit 0(non-blocking), self-key fail-closed(ANU key 강제), prompt UTF-8 ≤3900 byte guard.
5. 테스트 경로 커버: RS-1(failfast 등록)/RS-2(scope_guard)/RS-3(success baseline 비파괴)/RS-4(duplicate idempotent)/RS-5(recursion 방지) 5/5.
6. 발견 이슈 직접 해결: §5 의 5건 중 4건 해결 + 2건 caveat 분석 기록.
7. 아키텍처 원칙: 단일 책임(helper 1 launch 책임), DRY(launcher 재사용), 기존 launcher 수정 0.
8. 인터페이스 변경: 신규 helper CLI 만 추가, 기존 helper/finish-task.sh 호출 시그니처 변경 0.
13. L1 스모크: §4 기록(통과).

---

## 9. 모델 사용 기록

- **불칸 (백엔드)**: sonnet — helper + finish-task.sh Hunk A/B/C 구현 (일반 코딩/시스템 로직)
- **아르고스 (테스터)**: sonnet — RS-1~5 회귀 테스트 + fixture (테스트 코드)
- **헤르메스 (팀장, Opus)**: 설계/분배/검토/통합 + L1 smoke 검증 + pyright 정리 통합
- 이리스(프론트)·아테나(UX/UI): 미활성 — 본 작업은 백엔드/bash/python system_hook 으로 프론트 변경 0.
- haiku 미사용 (시스템 핵심 파일 정밀 작업으로 sonnet 이상 적용).

---

## 10. forbidden_actions 준수 (§10.5)

1. 코드 수정 — scope(§10.1) 내만 ✓
2. finish-task.sh — Hunk A/B/C 외 수정 0 ✓
3. helper(normal_fallback_callback_helper.py) 수정 0 ✓
4. dispatch 0 (본 작업은 실행이지 신규 dispatch 아님) ✓
5. callback cron 수동 등록 0 (finish-task.sh Layer 2 가 ANU 등록 — 수동 등록 0) ✓
6. `.done` 수동 생성 0 (finish-task.sh 만이 완료 경로) ✓
7. PR/branch push/merge 0 ✓
8. GitHub write 0 ✓
9. P2-B/C/D 구현 0 ✓
- forbidden_files 11종 sha 불변(HEAD~ vs HEAD diff 0) ✓

---

## 11. ★ 완료 차단 (Completion Blocker) — scope_guard_fail → hold_for_chair

구현은 완료·검증되었으나(§1~§10), `finish-task.sh` 가 **SCOPE-GUARD 게이트**에서 exit 1 로 차단되어 `.done` 미생성 상태다. 이는 task-2708 §6.2/§8.1 의 `system_hook × scope_guard_fail → hold_for_chair` 케이스에 정확히 해당한다.

### 11.1 차단 근거 — 두 가지 인프라 이슈 (내 작업의 scope 위반 아님)

1. **snapshot `paths: null` 생성기 버그**: `memory/capabilities/task-2708.json` 의 `allowed_resources.paths` 가 null. dispatch.py snapshot 생성기가 task-2708.md §10.6 의 `expected_files`/`allowed_existing_file_edits` 키를 기록했으나, `scripts/task-scope-guard.sh` 는 `paths` 만 읽는다 → 스키마 불일치로 모든 변경 파일이 "paths 미포함" 처리(내 in-scope 8건 포함). **dispatch.py 는 task-2708 forbidden 파일이라 본 task 에서 수정 불가.**
2. **공유 브랜치 오염**: scope-guard diff 기준 = `git diff main..HEAD`. 현재 브랜치 `task/task-2703-v36-harness-dev1` 는 main(35e81f01) 위에 task-2703 의 10 commit(54 파일)을 포함하고, 그 위에 task-2708 의 5 commit(8 파일)이 적재됨. system_hook task 의 worktree 미생성(project_id=system) doctrine 으로 공유 dev 브랜치에 commit → main..HEAD 에 task-2703 파일 혼입.

### 11.2 위반 분석 (59건) — 내 commit 기여 0

- **forbidden_paths 위반 4건** (.claude/settings.json, dispatch/__init__.py, scripts/harness/v36/dispatch_marker_writer.py, scripts/session-watchdog.sh) — ★ **전부 task-2703 commit 소속, 내 commit 0건** (b8f9276^..HEAD diff 로 확인).
- **paths 미포함 55건** — 내 in-scope 8건(snapshot paths-null 거짓양성) + task-2703 오염 47건.

### 11.3 callback contract 영향 (★ task-2708 premise 의 실증)

scope-guard exit 1(finish-task.sh L473) 이 Layer 1/2 callback registration(L1106+) 보다 **앞에서** 종료 → task-2708 자체가 callback 등록 0 으로 종료. 이는 task-2708 이 해결하려는 **IMPLEMENTATION_PASS_ROUTING_FAIL** premise 의 production 실증이다. Hunk A pre-registration 은 anu_callback envelope 존재 시에만 발화하나 task-2708 은 envelope 미생성 → 미발화. 봇 manual cron 등록은 §10.5 #5 금지로 미수행.

### 11.4 미수행 조치 (doctrine 준수)

- scope-guard 우회(`.scope-guard-done` 수동 생성) — 게이트 우회 금지
- 수동 `.done` 생성 — 절대 금지
- 공유 repo 브랜치 rewrite/surgery — 5개 running task 충돌 위험으로 미수행
- manual callback cron 등록 — §10.5 #5 금지
- forbidden 파일(dispatch.py 등) 수정 — §10.4 금지

### 11.5 회장/아누 결정 요청

1. dispatch.py snapshot 생성기 `paths` 정규화 버그 수정 인가(별도 task) — `paths = expected_files + allowed_existing_file_edits + tests/fixtures/p2a/**`
2. task-2708 완료 경로 선택: (a) task-2703 main 머지 선행 후 재실행, (b) system_hook task worktree 격리, (c) task-2708 commit 을 main 기준 독립 브랜치로 분리
3. 위 해소 후 `finish-task.sh task-2708` 재실행 → callback 등록 + `.done`

상세 escalation: `memory/events/task-2708.escalation-detail-260529.json` + `memory/events/task-2708.escalate`.

---

## 비고

- 본 작업은 v2 draft 의 chair verdict A 확정(`task-2708.force-flag-1x...260529.json` 회장 verbatim "verdict A 확정 · P2-A 구현 발사 승인") + dispatch(`task-2708.dispatched-20260529.json`, chair_authorization_id) 에 근거한 **인가된 구현**이다. task 파일 본문의 "DRAFT only / no implementation" 문구는 draft 단계 ANU 자기제약으로, chair verdict A + dispatch 인가로 대체됨.
- ANU normal callback 회수: 본 task 완료 시 finish-task.sh Layer 2(L1583 영역, lock 부재 → 정상 실행)가 ANU-key(c119085addb0f8b7) 단일 launcher 로 callback 을 등록 → SELF_COLLECTOR/SENDFILE_ONLY/NOT_REGISTERED contract 충족(executor self-key 미사용).

## 세션 통계
- 총 도구 호출: 0회


## 세션 통계
- 총 도구 호출: 0회

