# task-2778A — Terminal Artifact Surfacing 구현 (surfacing layer only)

상태명(착수): `TASK2778A_TERMINAL_ARTIFACT_SURFACING_IMPLEMENTATION_APPROVED_ACTIVE_FALSE`
담당: dev2-team
승인: 회장 2026-06-28 (글2 채택·범위 2778A로 엄격 제한)
근거: normal callback / terminal artifact visibility gap = 이미 발생한 명시적 병목(dev2 결과판정·PR#257 검증·callback 확인에서 반복 오판). META_INFRA 재개 트리거 "명시적 병목" 해당.
선행: task-2778 설계 문서 `memory/reports/terminal_callback_enforcement_design_260628.md` (DESIGN_READY) 읽고 정합 구현.

## 레벨
기능 구현 (신규 모듈 2 + 테스트 + 보고서)

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

## allowed_resources (본 task의 capability)

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

## 목표
worktree-local에 존재하는 terminal artifact(report, .done, envelope)를 ANU가 canonical `/home/jay/workspace`에서 authoritative하게 볼 수 있도록 **canonical terminal artifact index(manifest)** 를 구현한다.
근본 원인: `canonical ↔ worktree terminal artifact visibility gap`. 강제할 것은 "남기기"가 아니라 "ANU가 볼 수 있는 위치로 surface하기".

## 이번 단계가 아닌 것 (범위 밖 — 절대 금지)
1. pre-push hook 설치 아님
2. Stop hook 수정 아님
3. strict enforcement 아님
4. always-on pickup 아님
5. real callback delivery 아님
6. production activation 아님 / ACTIVE=true 아님
→ 이번 2778A는 **관측/증거 surfacing 코드**다. hook/activation 전혀 아님.

## expected_files (이 4개만 — effective diff == expected_files)
1. `dispatch/terminal_artifact_index.py` — terminal artifact manifest 생성/검증/조회 모듈
2. `scripts/anu_terminal_artifact_surface.py` — CLI (`--task-id` `--worktree` `--canonical-root` `--json`), read/surface only
3. `tests/regression/test_terminal_artifact_visibility_2778.py` — 신규 회귀
4. `memory/reports/task-2778a.md` — 구현 보고서

canonical index path 규약: `memory/events/terminal_artifact_index/<task_id>.json`

## 구현 요구 (16)
1. canonical root와 worktree root를 명확히 분리
2. canonical root 하위에만 terminal artifact manifest 생성
3. worktree report/.done/envelope는 **읽기만** 수행
4. `.done`이 gitignored/local-only여도 canonical manifest로 존재를 surface
5. envelope target이 canonical에 없어도 worktree target을 확인해 dangling 오판 방지 (→ `WORKTREE_LOCAL_TERMINAL_ARTIFACT_PRESENT`)
6. manifest에 task_id, branch, head, worktree_path, report_path, done_path, envelope_path, status enum 기록
7. raw key/secret/token 기록 금지
8. callback fire 금지
9. `cokacdir` 호출 금지
10. driver 실행 금지
11. fixture 생성 금지
12. real fire 금지
13. systemd 조작 금지
14. cron 등록 금지
15. flag ON 금지
16. ACTIVE=false 유지. 실패해도 traceback으로 죽지 말고 explicit status 반환(fail-open + explicit-marker). artifact path는 상대경로 중심.

## 상태 enum (최소 구현)
- `WORKTREE_LOCAL_TERMINAL_ARTIFACT_PRESENT`
- `CANONICAL_TERMINAL_ARTIFACT_MISSING`
- `TERMINAL_ARTIFACT_SURFACED_TO_CANONICAL`
- `CALLBACK_ENVELOPE_ONLY`
- `CALLBACK_ENVELOPE_DANGLING_TARGETS`
- `ANU_PICKUP_FROM_WORKTREE_REQUIRED`
- `TERMINAL_CALLBACK_MISSING`

## 테스트 요구 (test_terminal_artifact_visibility_2778.py)
- worktree report 존재 + canonical report 부재 → manifest 생성
- worktree .done 존재 + gitignored 상황 → manifest 생성
- canonical envelope가 target 못 찾지만 worktree target 존재 → dangling 보정
- artifact 모두 없음 → `TERMINAL_CALLBACK_MISSING` 또는 `ANU_PICKUP_FROM_WORKTREE_REQUIRED`
- raw key/secret 0 / callback fire 0 / driver 실행 0 / real fire 0
- read-only/manifest-only invariant

## 검증 요구 (13)
1. `python3 -m py_compile dispatch/terminal_artifact_index.py scripts/anu_terminal_artifact_surface.py`
2. 신규 regression PASS
3. task-2777 status command test PASS
4. task-2776 live inbox evidence test PASS
5. task-2775 runtime guard test PASS
6. raw key/secret scan 0
7. effective diff == expected_files
8. forbidden paths 변경 0
9. ACTIVE=false
10. flags OFF
11. real fire 0
12. systemd inactive/disabled 불변
13. cron 신규 0

## 금지 (위반 시 즉시 중단·보고)
pre-push hook 설치 / Stop hook 수정 / strict mode / actual callback delivery / dev self-key callback / raw key 추가 / ANU key literal / driver 실행 / fixture 생성 / real fire / systemd start·enable / recurring runner / production activation / PR #257 수정 / task-2777 재개 / task-2778B·C 착수 — **전부 금지.**

## 완료 상태명 체인
- 구현 완료: `TASK2778A_TERMINAL_ARTIFACT_SURFACING_IMPLEMENTED_ACTIVE_FALSE`
- PR 준비: `TASK2778A_TERMINAL_ARTIFACT_SURFACING_PR_READY_ACTIVE_FALSE`
- (merge candidate/merged는 ANU 검증·회장 승인 후 단계)

## callback / 종료 규칙 (필수 — 이번 작업의 본질과 직결)
- 작업 결과(성공/실패/blocked 무관) **봇 idle 전 ANU callback 필수**. callback ≠ merge ≠ finish-task.
- ANU-owned callback envelope만 작성. **dev self-key callback fire 금지. raw key 출력 금지.**
- callback envelope/result.json/report/.done은 **canonical에서 ANU가 볼 수 있게** 남길 것 (이 task가 푸는 문제 그 자체 — worktree-local에만 남기지 말 것).
- 전용 worktree에서 작업(start-guard). PR 생성은 task 브랜치에서.
- callback prompt UTF-8 ≤3900 bytes, envelope만(긴 보고는 result/report에 위임).
