# task-2782 보고서 — 4종 종단 terminal callback 결선 (구현 후보, ACTIVE=false)

- 작업 ID: task-2782 | 팀: dev1-team (팀장 헤르메스) | 레벨: Lv3
- 작성: 2026-07-03 | 상태: **구현 후보 완료 (ACTIVE=false).** worktree 커밋까지. PR/merge/활성화 없음.
- worktree: `/home/jay/workspace/.worktrees/task-2782-dev1` · branch `task/task-2782-dev1`

## SCQA 요약
- **S**: (Situation) finish-task 종단은 SUCCESS(.done)만 ANU terminal callback 결선(before-exit guard)이 커버. task-2781에서 `state_file_missing` → `.done.escalated`만 남고 result.json·ANU 콜백 0(CALLBACK_NOT_DELIVERED).
- **C**: (Complication) escalation/BLOCKED/FAILED/state_file_missing 4종 실패 종단에 terminal status 보장 결선 부재(result.json 미생산·callback 미호출). 또한 `terminal_state_callback.py`가 dispatched marker 부재 시 fail-open 침묵 종료(관측 marker 0).
- **Q**: (Question) 4종 종단 모두 ①terminal marker ②최소 result.json ③terminal callback 등록 또는 fail-closed marker가 남게 결선하되, 회장 경계(ACTIVE=false: real fire 0·self-key 0·ANU key literal 0·PR/merge 0·owner-pickup 미활성·canonical 무접촉)를 지키려면?
- **A**: (Answer) 2파일 수정 + 신규 회귀 10케이스로 결선. 로컬 pytest **10/10 PASS**, L1 격리 스모크 4종 종단 전수 확인 + emit fail-closed marker 확인. ACTIVE=false 유지(real fire 0·self-key 0·ANU literal 0·PR/merge 0).

## 수정 파일별 검증 상태

파일 경로는 워크트리(task/task-2782-dev1) 절대경로 — 변경은 워크트리 브랜치에만 존재(main 무접촉, ACTIVE=false).

| 파일 | 설명 | grep 검증 | 상태 |
|------|------|-----------|------|
| /home/jay/workspace/.worktrees/task-2782-dev1/scripts/harness/v36/terminal_state_callback.py | 폴백 컨텍스트 해석 + fail-closed marker(silent exit0 제거) | grep "_resolve_callback_context" OK | verified |
| /home/jay/workspace/.worktrees/task-2782-dev1/scripts/finish-task.sh | 4종 종단 result.json(pickup_required 포함)+terminal callback 결선 헬퍼 | grep "_write_terminal_result_and_callback" OK | verified |
| /home/jay/workspace/.worktrees/task-2782-dev1/tests/regression/test_finish_task_terminal_termination_2782.py | 신규 회귀 10케이스(self-key 발사 0·ANU literal 미등장 포함) | grep "test_no_self_key_fire_and_no_anu_literal" OK | verified |

## 작업 내용 (수정/생성 파일)

### 1) 수정 `scripts/finish-task.sh` (+77)
- 신규 헬퍼 `_write_terminal_result_and_callback(state, reason)`:
  - callback_context 판정: dispatched marker → capabilities fallback → `unresolved_fail_closed`.
  - 최소 result.json 원자적 write(mktemp→python json→`mv -f`). 고정 9-필드 envelope:
    `{task_id, terminal_state, result_path(self-ref), report_path, callback_schedule_created:false, pickup_required:true, callback_context, reason, ts(UTC ISO8601)}`.
  - write 실패(mktemp/json/mv) 시 침묵 통과 금지 → `.terminal-result-failed.json` 박제.
  - emit은 `TERMINAL_CALLBACK_ENABLED` gated(기본 OFF=ACTIVE=false) — OFF면 result.json/context만 박제.
  - `callback_schedule_created=false` 고정(OS-level pickup runner가 closeout 소유, executor self-key 발사 0 — task-2730 contract), `pickup_required=true` 명시.
- 호출 삽입 4분기: QC FAIL(≈761) / state_file_missing→ESCALATED(≈962) / taskctl BLOCKED(≈989) / taskctl ESCALATED(≈1006). 각 terminal marker 발행 직후.

### 2) 수정 `scripts/harness/v36/terminal_state_callback.py` (+70)
- 컨텍스트 라벨 상수: `CTX_DISPATCHED_MARKER`/`CTX_FALLBACK_SCHEDULE_ID`/`CTX_UNRESOLVED_FAIL_CLOSED`.
- `_get_attempt_id`: 중첩 `dispatch.schedule_id` 폴백 해석.
- 신규 `_resolve_callback_context()`: dispatched marker(sid) → capabilities/`<task>.json` → unresolved 3-tier.
- 신규 `_write_fail_closed_marker()`: 미해결 시 `.terminal-callback-failed.json`(status=`CALLBACK_CONTEXT_UNRESOLVED_FAIL_CLOSED`) 박제 — **silent exit 0 제거**(비차단이되 침묵 금지).
- `_build_envelope`: optional `callback_context` 필드. `emit()` failure 분기가 resolver 사용 + 미해결 시 fail-closed marker.
- ANU key는 sealed import(`DEFAULT_ANU_KEYS`)만 — 리터럴 0. self-key는 launch_callback가 fail-closed(변경 없음).

### 3) 신규 `tests/regression/test_finish_task_terminal_termination_2782.py` (~742줄, 10케이스)
mock/tmp 완전 격리 — 실제 cron/ANU 발사 0. bash 헬퍼는 brace-depth 매칭(`_find_func_line_range`)으로 함수만 추출해 격리 subprocess 실행.

## 통합 diff (merge-base 대비)
`3 files changed, 888 insertions(+), 1 deletion(-)` — expected_files와 정확히 일치.

## 테스트 결과 (로컬 pytest)
`python3 -m pytest tests/regression/test_finish_task_terminal_termination_2782.py` → **10 passed in 0.45s**.
- TC1 state_file_missing ESCALATED → result.json + callback_context : PASS
- TC2 dispatched marker 부재 → fallback/fail-closed marker(silent exit0 금지) : PASS
- TC3 BLOCKED → result.json : PASS
- TC4 FAILED(QC FAIL) → result.json : PASS
- TC5 SUCCESS(.done) 회귀 0 : PASS
- TC6 4종 종단 terminal_state 전수 assert : PASS
- TC7 callback 등록 실패 주입 → NOT_REGISTERED/fail-closed marker : PASS
- TC8 result.json에 callback_schedule_created·callback_context·**pickup_required** 필드 : PASS
- TC9 **self-key 발사 0 + ANU key literal 미등장** : PASS
- TC10 **owner_pickup_required가 CALLBACK_DELIVERED로 미승격** : PASS
- 문법: `bash -n finish-task.sh` OK.

## L1 스모크테스트 결과 (필수 기록)
- **서버 재시작: 해당없음** — finalize 서브프로세스/결선 층 작업(대시보드·API 서버 무관).
- **API 응답 확인: 해당없음** — HTTP 엔드포인트 없음.
- **스크린샷: 해당없음** — 프론트엔드 아님.
- **subprocess 실동작 검증(실행함, worktree 경로 명시 isolated)**:
  - 4종 종단(FAILED/BLOCKED/ESCALATED×2) 격리 실행 → 각 `terminal-result.json` 생성, 9-필드 완전, `pickup_required=true`·`callback_schedule_created=false`·`CALLBACK_DELIVERED` 미등장 — **PASS**.
  - `emit(dry_run=True)` + dispatched/capabilities 부재 + qc-result FAIL → `.terminal-callback-failed.json`(status=`CALLBACK_CONTEXT_UNRESOLVED_FAIL_CLOSED`) 생성 — **PASS**.
- ★ 종료검증 함정 준수: 봇 자신의 최종 finish-task는 canonical(수정 전) finish-task.sh로 실행될 수 있으므로, 수정본 검증은 **pytest + worktree 경로 명시 isolated 실행으로만** 보고. 봇 자신의 finish-task 종단을 end-to-end proof로 주장하지 않음.

## 발견 이슈 및 해결
- **이슈1(이전 세션 미완 완성)**: 브랜치에 선행 커밋(609c2465)이 있었으나 result.json에 스펙 필수 `pickup_required` 누락 + 테스트 8개(케이스 9·10 미작성, 8에 pickup_required assert 없음). → 불칸이 `pickup_required:true` 추가, 아르고스가 케이스 8 보강 + 9(self-key/ANU literal)·10(delivered 미승격) 추가. 10/10 PASS. (커밋 d530fe62)
- **이슈2(canonical 오탐 배제)**: `git diff origin/main..HEAD`가 canonical 2파일 대규모 삭제로 표시. 조사 결과 커밋 609c2465는 canonical **UNCHANGED_BY_COMMIT**, 차이는 origin/main이 분기점(merge-base faac8d11) 이후 앞서나간 것. worktree canonical은 33557B/24403B 정상(비-0)·merge-base와 sha 동일. → canonical 무접촉 확인, STOP_REPORT 미해당.
- 미해결 이슈: 없음.

## forbidden 무접촉 증거
- canonical 2파일 `utils/replacement_pr_runner.py`·`tests/regression/test_replacement_pr_runner_2510.py` → **UNTOUCHED**(merge-base와 sha 동일, 33557B/24403B).
- merge-executor 체인(`merge_queue_executor.py`·`real_merge_hooks.py`·`repository_policy_adapter.py`·`automation_contracts.py`·`replacement_pr_runner.py`)·`gemini_evidence_verify.py`·`dispatch.py`: 전부 untouched.
- `.github/**` 0 changed · `.git/hooks/**` 0 changed.
- **ANU key literal(c119085…) 0건**(sealed import만). 신규 real-fire/cron/systemd 코드 0건(emit flag-gated, 기존 launcher 경유).

## ACTIVE=false 불변식 준수
- PR 생성 0 · merge 0 · ci.yml/ruleset 0 · Merge Queue 0 · real fire 0 · executor self-key 콜백 0 · owner-pickup runner 미활성.
- `CALLBACK_DELIVERED` 승격 없음 — 최대 상태 = 등록/`OWNER_PICKUP_REQUIRED`(TC10 강제).
- 본 task 완료 후에도 상태는 구현 후보(ACTIVE=false). 실제 활성화는 PR→리뷰→merge 후 별도 검증.

## 머지 판단
- **머지 필요**: No (worktree 커밋 + ANU 보고까지. GPT verbatim: PR 생성 금지).
- **브랜치**: `task/task-2782-dev1`
- **워크트리 경로**: `/home/jay/workspace/.worktrees/task-2782-dev1`
- **커밋**: 609c2465(base) + d530fe62(pickup_required + TC9·10)
- **머지 의견**: pytest 10/10 · forbidden 무접촉 · canonical 무접촉 · ANU literal 0 · ACTIVE=false 준수. merge_policy=none(finalize-only). 실제 활성화/머지는 아누·회장 판단 대기.

## 모델 사용 기록
- 불칸(백엔드, sonnet): finish-task.sh `pickup_required` 필드.
- 아르고스(테스터, sonnet): 테스트 TC8 보강 + TC9·10 신규.
- 팀장 헤르메스(opus): 설계·구조 검증·canonical 오탐 배제·통합/L1 검증.
- haiku 미사용(스펙 민감 infra 작업 — sonnet 이상 강제).

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

