# task-2779_a — Axis A: pre-push terminal guard (warn/envelope mode) 구현 (PR 후보까지)

상태명(착수): `TERMINAL_CALLBACK_ENFORCEMENT_AXIS_A_PRE_PUSH_WARN_IMPLEMENTATION_APPROVED_ACTIVE_FALSE`
담당: dev2-team (오딘) — terminal_artifact_index.py 연속성
승인: 회장 2026-06-29 (GPT 단계판정: 실행계약 수용 → 축 A warn/envelope **코드+테스트+PR 후보까지만**. 실제 hook 설치/운영/strict/Stop hook 미승인)
근거: 실행계약 `memory/reports/terminal_callback_enforcement_contract_260629.md` / 설계 `terminal_callback_enforcement_design_260628.md` §C / P0c 증거 `p0c_phase1_observed_260629.md`

## 레벨
기능/보안 구현 (1 스크립트 + 격리 테스트 + 보고서). PR 후보 생성까지.

## merge_policy
none — PR 후보 생성까지만. merge는 ANU 독립검증→fresh Gemini→CI 후 회장 승인 별도. **봇 merge 금지.**

## ★★★ 승인 범위 (절대 경계)
- **warn/envelope mode 코드 + 격리 fixture 테스트 + PR 후보 작성만.**
- **실제 pre-push hook 설치 0 / 실제 push 실행 0 / strict·blocking mode 0 / Stop hook enforcer 0.**
- 이 스크립트는 작성되지만 `.git/hooks/`에 설치하지 않는다(설치는 다음 승인). git push 경로에 실제 결선 0.

## allowed_resources (본 task의 capability)

```yaml
allowed_resources:
  paths:
    - "scripts/pre_push_terminal_guard.py"
    - "dispatch/terminal_artifact_index.py"
    - "tests/regression/test_pre_push_terminal_guard_2779.py"
    - "memory/reports/task-2779a-r1.md"
  forbidden_paths:
    - "scripts/finish-task.sh"
    - "scripts/anu_control_status.py"
    - "scripts/anu_terminal_artifact_surface.py"
    - "dispatch/anu_pickup_driver.py"
    - "dispatch.py"
    - ".git/hooks/**"
    - ".github/**"
    - ".claude/**"
    - ".env*"
    - "memory/state/**"
    - "memory/events/terminal_artifact_index/**"
    - "**/inbox/**"
    - "**/archive/**"
    - "**/quarantine/**"
  commands:
    - "git"
    - "pytest"
    - "python3"
    - "gh"
  merge_policy: "manual"
  ttl_hours: 8
```

## 목표
task branch push **직전에 호출되도록 설계된**(설치는 아직 안 함) terminal guard 스크립트를 작성한다. 이 스크립트는: task_id·branch·head·worktree path를 받아 report/.done/envelope 존재를 확인하고, **있으면 canonical manifest로 surface, 없으면 canonical visible `PUSH_ATTEMPT_WITHOUT_TERMINAL_MARKER` envelope/manifest를 생성**한다. **warn/envelope mode = push를 막지 않는다(비차단).**

## 필수 요구 (15)
1. 새 스크립트 `scripts/pre_push_terminal_guard.py` (또는 동등 최소 스크립트, .py 권장)
2. 입력: task_id, branch, head, worktree path (인자/환경변수). 기존 surface CLI 인자 패턴 참고
3. 기존 `dispatch/terminal_artifact_index.py`의 build_manifest/surface_manifest **재사용**(신규 로직 최소)
4. terminal artifact(report/.done/envelope) 존재 → canonical manifest로 surface
5. terminal artifact 부재 → canonical visible `PUSH_ATTEMPT_WITHOUT_TERMINAL_MARKER` envelope/manifest 생성
6. **warn/envelope mode = push를 차단하지 않음**(exit 0, 경고/마커만). strict/blocking 미구현
7. 내부 오류 시 **fail-open + explicit marker**(crash로 push/종료 방해 금지)
8. 외부 path input은 기존 `_safe_artifact_path`/containment helper 통과(path traversal 무회귀)
9. dev self-key callback fire 0 · raw key/secret/ANU key literal 0
10. callback delivery(실발사) 0 · real fire 0
11. driver 실행 0 · systemd/cron 조작 0
12. **실제 `.git/hooks/pre-push` 설치 0 · 실제 push 실행 0**
13. 기존 P0c 증거(`memory/events/terminal_artifact_index/task-2778_a.json`) 덮어쓰기/삭제 0 — 테스트는 별도 fixture/task_id/tmp root
14. expected_files(allowed paths) 밖 수정 0 · forbidden paths 변경 0
15. ACTIVE=false 유지

## 테스트 필수 (8, 격리 fixture/tmp root)
1. terminal artifact 존재(tmp fixture) → surface manifest 생성됨
2. terminal artifact 부재 → `PUSH_ATTEMPT_WITHOUT_TERMINAL_MARKER` envelope/manifest 생성됨
3. warn mode = 부재여도 비차단(exit 0)
4. 내부 오류 주입 시 fail-open(crash 없이 explicit marker)
5. malicious rel path는 기존 containment helper로 거부(traversal 무회귀)
6. self-key 미사용 / raw key 0 정적 검증
7. 실제 .git/hooks 설치·실제 push 호출이 코드 경로에 없음(정적/구조 검증)
8. 기존 surfacing/2778 regression 무회귀

## 검증 요구 (13)
1. `python3 -m py_compile scripts/pre_push_terminal_guard.py`
2. `pytest tests/regression/test_pre_push_terminal_guard_2779.py` PASS
3. 기존 `test_terminal_artifact_visibility_2778.py` PASS(무회귀)
4. raw key/secret scan 0
5. ANU key literal 0
6. expected_files 밖 수정 0
7. forbidden paths 변경 0(.git/hooks 포함)
8. callback/fire/systemd/cron/activation 0
9. 실제 push 실행 0 · hook 설치 0
10. ACTIVE=false
11. P0c 증거 무변경
12. 새 PR 후보 생성(머지 0)
13. strict/blocking·Stop hook enforcer 미포함

## STOP 조건 (하나라도 필요해지면 즉시 중단 → `TERMINAL_CALLBACK_ENFORCEMENT_CONTRACT_STOP_REPORT`)
금지 항목 필요 / 축 B(Stop hook) 진입 필요 / strict 차단 필요 / expected_files 밖 수정 필요 / 실제 hook 설치·push 필요 / callback delivery·real fire 필요 / systemd·cron 필요 / merge·production write 필요.

## 금지 (13)
실제 pre-push hook 설치 / 실제 push 실행 / strict·blocking mode / Stop hook enforcer 구현 / callback delivery·real fire / owner key·credential·raw key touch / systemd enable·start·install / cron·daemon·always-on / production write / merge·push·auto-merge / broad Phase 2 확장 / finish-task.sh 본체 변경 / P0c 증거 삭제·덮어쓰기 — **전부 금지.**

## 완료 후보 상태명
- 구현 완료: `TERMINAL_CALLBACK_ENFORCEMENT_AXIS_A_PRE_PUSH_WARN_IMPLEMENTATION_DONE_ACTIVE_FALSE`
- 이후: ANU 독립검증 → fresh Gemini → active HIGH 0/Critical 0 → CI → MERGE_CANDIDATE_READY (회장 승인 별도)
- STOP 조건 발생: `TERMINAL_CALLBACK_ENFORCEMENT_CONTRACT_STOP_REPORT`

## callback / 종료 규칙
- 결과(성공/실패/blocked 무관) **봇 idle 전 ANU callback 필수**. callback ≠ merge ≠ finish-task.
- ANU-owned callback envelope만. **dev self-key callback fire 금지. raw key 출력 금지.**
- callback/result/report/.done은 **canonical에서 ANU가 볼 수 있게** 남길 것.
- 새 worktree/브랜치/PR로 작업(새 task id task-2779_a). PR 후보 생성까지(merge 0).
- callback prompt UTF-8 ≤3900 bytes, envelope만.

## goal_assertions (auto-generated)
- `python3 -m py_compile scripts/pre_push_terminal_guard.py`
- `pytest tests/regression/test_pre_push_terminal_guard_2779.py`