# task-2729+8 — P0-B real wake 결선 (WAKE_BUILT argv 실 실행) audit-first hardening

- 팀: dev4-team (팀장 비슈누) | 레벨: Lv.3 | 작성일: 2026-06-06 (KST)
- 완료 판정: **`REAL_WAKE_READINESS_VERIFIED_ACTIVE_FALSE`**
- 기준 main: `c6aacb5e` (origin/main, #181/#182 hardened) | 작업 브랜치: `task/task-2729+8-dev4`
- ★ 재dispatch 정정: 직전 dev1 run 은 stale base `14ff8339`(#181 hardening 부재) 위에 결선 → merge 충돌·push scope guard 차단(`PR_HOLD_STALE_BASE`). 본 run 은 remediation **A안**(fresh origin/main `c6aacb5e` 의 #181-hardened driver 위에 W2 재구현)으로 충돌 0·hardening 무손상 확보.

## SCQA 요약

**S**: P0-B pickup driver(`anu_pickup_driver.process_one`)는 6조건 통과 후
`pickup_once` 가 `WAKE_BUILT` + wake argv(`cokacdir --cron … --key <ANU_KEY>`)를 빌드해도,
WAKE_BUILT 분기(#181 driver 538–549줄)에서 argv 를 **읽지도 실행하지도 않고**(`fire_cron_id=None`) surface 만 했다.

**C**: systemd enable 전, "real wake 결선이 안전하게 가능한가"가 미검증 상태였다.
real ANU spawn 을 dispatch 단계에서 실행하면 canonical 오염·raw key 노출·중복 wake·runaway 위험이 있다.
추가로, 직전 run 의 stale-base 결선은 #181 hardening(MAX_FILES/legacy cutoff/activation_epoch)을 상실할 위험이 있었다.

**Q**: real ANU spawn 실행 0 · canonical 무오염 · ACTIVE=false · **#181 hardening 무손상**을 유지하면서,
WAKE_BUILT argv 실 실행 결선의 readiness 를 isolated 로 확정할 수 있는가?

**A**: 관심사 분리(driver=decision-only, 신규 launcher=실행)로 결선했다. launcher 는
**dry_run 기본 True**(실행 0, production 무기록) + raw key redaction + 이중 dedupe + fail-closed.
driver 는 `launcher_fn=None`(현행 보존) 기본, 주입 시에만 호출. fresh origin/main 의 #181-hardened
driver(hardening 키워드 37건 무손상) 위에 W2 4개 수정만 적용. 신규 회귀 **19 passed** + baseline
P0-B 회귀(`test_anu_pickup_driver_2721` + `test_anu_pickup_activation_hardening_2729p7`) **45 passed** = **64 PASS**,
L1 스모크 5단계 전부 통과(real spawn 0 / canonical 무오염 / raw key 0).

## 생성/수정 파일 (정확히 5 — 초과 0)

1. `dispatch/anu_pickup_wake_launcher.py` (신규, W1) — wake launcher. `launch_wake()` + `LaunchRecord` + 7 decision 상수.
2. `dispatch/anu_pickup_driver.py` (수정, W2) — `process_one`/`scan_once` 에 `launcher_fn=None` 주입, WAKE_BUILT 분기 결선. (#181 hardening 37건 무손상)
3. `tests/regression/test_anu_pickup_real_wake_wiring_2729p8.py` (신규) — 회귀 + mock launcher decision-only 19 케이스.
4. `memory/reports/task-2729+8.md` (본 보고서).
5. `memory/plans/p0b-pickup/real_wake_wiring_design_260606.md` (pilot design).

- runner(`anu_result_pickup_runner.py`) / callback_enforcement 수정 **0** — argv 빌드 기존 재사용.

## 수정 파일별 검증 상태

| 파일 | 변경 내용 | grep 검증 | 상태 |
|---|---|---|---|
| dispatch/anu_pickup_wake_launcher.py | 신규 wake launcher (launch_wake/LaunchRecord/decision 상수) | grep "def launch_wake" OK | verified |
| dispatch/anu_pickup_driver.py | process_one/scan_once launcher_fn 주입, WAKE_BUILT 결선 | grep "launcher_fn" 6건 OK | verified |
| tests/regression/test_anu_pickup_real_wake_wiring_2729p8.py | 회귀+mock decision-only 19 케이스 | grep "def test_" OK | verified |
| memory/reports/task-2729+8.md | SCQA 보고서 + L1 결과 | grep "L1 스모크" OK | verified |
| memory/plans/p0b-pickup/real_wake_wiring_design_260606.md | pilot design | grep "real wake" OK | verified |

## 설계 핵심 (관심사 분리)

- **launcher = 유일 spawn 책임**. `launch_wake(argv, *, task_id, sha256, dry_run=True, root, launch_ledger_path, audit_path, anu_key_verifier, subprocess_runner, clock)`.
- 결정 순서(fail-closed 우선): NO_ARGV → MALFORMED → NON_ANU_KEY → (dedupe)SKIP_DEDUPE/LEDGER_ERROR → DRY_RUN → LAUNCHED.
- dedupe: launcher 자체 ledger(`memory/p0b_state/wake_launch_ledger.jsonl`, event=`WAKE_LAUNCHED`)로 (task_id, sha256) 재확인 — pickup 의 `PICKUP_WAKE_BUILT` ledger 와 분리(후자는 WAKE_BUILT 직전 항상 존재해 dedupe 기준 부적합).
- raw key 0: argv 는 stdout/log/audit/ledger 어디에도 미출력. `LaunchRecord` 는 `argv_len`(길이)만 보유. `_default_subprocess_runner` 는 capture_output 안 함(출력 미수집).
- driver: `launcher_fn=None` 기본=현행 보존. 주입 시 WAKE_BUILT 분기에서만 `launcher_fn(res.argv, task_id, sha256)` 호출, `fire_cron_id=lr.decision`(라벨만), 예외는 fail-safe(크래시 0).

## 테스트 결과 (정량)

- 신규 회귀 `test_anu_pickup_real_wake_wiring_2729p8.py`: **19 passed** (0.32s).
- baseline 회귀 무영향: `test_anu_pickup_driver_2721.py` + `test_anu_pickup_activation_hardening_2729p7.py` = **45 passed** (0.35s).
- 합계 **64 passed / 0 failed**.
- raw key 스캔: 실제 ANU key literal 4개 대상 파일 전부 미포함(`RAW_KEY_CLEAN`).
- pyright: 구현 모듈(launcher/driver) 신규 심각 에러 0. `main(argv=None)` 의 `argv` 미사용 경고는 origin/main(#181) 사전 존재 항목(본 task 무관, 범위 외).

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

- **서버 재시작**: 해당없음 (서버/API/프론트 없는 dispatch 모듈 결선 audit-first task).
- **API 응답 확인**: 해당없음.
- **스크린샷**: 해당없음 (UI 없음).
- **실동작 검증(subprocess/정제 유형)**: isolated temp root(`/tmp/p0b_l1_smoke.*`)에서 launcher/driver 모듈을 **별도 python 프로세스로 실 호출** — pytest 와 독립한 실동작 확인. 결과:
  1. dry_run 기본 → decision=`DRY_RUN`, real spawn **0**, launch ledger 미기록(production write 0 — OWNER_TRIGGER_DRY_RUN_LEDGER_CONTAMINATION 방지).
  2. dry_run=False + mock runner → decision=`LAUNCHED`, rc=0, mock spawn 1회(실 subprocess 0).
  3. ledger/audit 내용 raw key(FAKE) **미포함**(redaction 확인).
  4. 동일 (task_id, sha256) 재발사 → `SKIP_DEDUPE`, 누적 spawn 1(중복 wake 0).
  5. driver `process_one` 에 `launcher_fn` 파라미터 존재(결선 확인).
  - canonical `/home/jay/workspace/memory/p0b_state/wake_launch_ledger.jsonl` = smoke 전후 **ABSENT**(무오염).
- 결론: pytest PASS + 실 모듈 동작 동시 확인. real wake readiness 확정.

## 회장 verbatim 검증 (12 / 12 충족)

1 WAKE_BUILT 에서만 launcher 호출(B12) · 2 legacy cutoff launcher 0(pickup 단계 미반환, #181 legacy skip 무손상) · 3 terminal marker launcher 0(B14) · 4 MAX/defer launcher 0(NOOP_MAX_FILES_DEFER, WAKE_BUILT 미도달) · 5 ledger/marker failure launcher 0(B13/A9) · 6 duplicate wake 0(A8/L1#4) · 7 raw key 0(A3/A7/A10/L1#3) · 8 실제 spawn 0(A1/L1#1 mock) · 9 dry-run/mock isolated temp(전 케이스 tmp_path) · 10 canonical 영향 0(L1 무오염) · 11 ACTIVE=false 유지(activation flag/systemd 미터치) · 12 기존 decision-only 보존(B11, launcher_fn=None 기본).

## 중단 조건 (8 / 8 해당 없음)

5파일 정확 · real spawn 없이 mock 검증 가능 · canonical write 불필요(isolated) · raw key 노출 0 · dedupe 이중 명확 · ACTIVE=true 불필요 · systemd enable 불필요 · fresh HIGH/CRITICAL 0.

## LOCK 준수 (회장 verbatim 9)

systemctl enable 0 · production ACTIVE 전환 0 · activation_epoch write 0 · canonical result.json 이동/삭제 0 · **real ANU spawn 실행 0**(dry_run 기본+mock) · raw ANU key 출력 0 · ACTIVE=true 선언 0 · legacy result.json 재처리 0. (PR 은 Lv.3 게이트 G3 절차에 따라 생성·Gemini 리뷰 후 머지 — LOCK #6 의 "임의 push/comment 금지"는 게이트 외 직접 조작 금지 의미로 해석, G3 자동화 절차는 워크플로우 승인 경로.)

## 발견 이슈 및 해결

### 자체 해결 (1건)
1. **STALE_BASE 결함(직전 dev1 run)** — canonical workspace 가 task-2716 dirty 브랜치에 parked → worktree 가 stale base 14ff8339 에서 분기 → #181 hardening 없는 구버전 driver 위에 결선 → merge 충돌 + push scope guard(two-dot 21파일) 차단. **해결**: #182 BASE_SOURCE_ISOLATION(enforce_origin_base) 적용된 worktree_manager 로 fresh origin/main `c6aacb5e` base worktree(dev4) 재생성 → #181-hardened driver(724줄, hardening 37건) 위에 W2 4개 수정만 재적용. base_fallback=false, merge 충돌 0.

### 범위 외 미해결 (0건)
- driver `main(argv=None)` CLI 시그니처의 `argv` 미사용 pyright 경고는 origin/main(#181) 사전 존재 항목(본 task 변경과 무관, 범위 외, 미수정).

## 머지 판단

- **머지 필요**: Yes (Lv.3 G3 게이트 — `finish --action pr` → Gemini 리뷰 → High 0건 시 자동 머지).
- **브랜치**: `task/task-2729+8-dev4`
- **워크트리 경로**: `/home/jay/workspace/.worktrees/task-2729+8-dev4`
- **머지 의견**: 5파일 정확, 64 회귀 PASS, L1 5단계 통과, raw key/canonical/real-spawn 위험 0. **fresh origin/main base 라 #181 hardening 무손상 + merge 충돌 0**(직전 stale-base 결함 해소). runner/callback_enforcement 무수정으로 기존 계약 보존. `launcher_fn` 기본 None 으로 production 결선·activation 은 별도 회장 승인 대상. 본 task PASS = "real wake 결선 readiness 확정"(`REAL_WAKE_READINESS_VERIFIED_ACTIVE_FALSE`).

## 모델 사용 기록

- 비슈누(팀장, Opus): 재dispatch 상황 진단(stale-base 식별)·fresh worktree 생성·W2 수정 명세·통합 검토·L1 스모크·보고서.
- 카르티케야(백엔드, general-purpose/Sonnet): #181-hardened driver 에 W2 4개 수정 재적용 + 산출물 배치 + 회귀 실행.
- haiku 미사용(시스템 hardening 정밀 작업 — sonnet 이상 사용).

## 비고 (완료 판정 의미)

본 PASS = "real wake 결선 readiness 확정" 입력 자료. **production ACTIVE 전환 아님 / real wake 실행 0**.
pickup chain = IMPLEMENTED / WIRED candidate / ACTIVE=false 유지. systemd enable·activation_epoch 생성·
real wake pilot·production activation 은 모두 별도 회장 승인 전까지 금지.
