# task-2721 — P0-b: user-level systemd path driver → result.json 자동 pickup wake 결선 (구현만)

## 회장 인가 (2026-06-01)
P0-b **구현 dispatch 승인**. ★ **설치/활성화/pilot 은 승인 아님**(별도 회장 승인). 본 task = 파일 생성(implemented) + 테스트까지만. preflight = PASS_WITH_1_WARN(WARN=로컬 stale, fresh worktree 로 해소).

## worktree (★ 반드시 P0-a 포함 fresh base)
- `/home/jay/workspace/.worktrees/task-2721-dev1` (branch `task/task-2721-dev1`, base origin/main **29175f80** = PR #166 P0-a 포함). HEAD 확인됨·pickup_once/anu_runner_pickup_and_fire 체크아웃됨·lock-free 0.
- ★ 로컬 stale tree 사용 금지. 본 worktree 에서만 작업.

## expected_files (정확히 6개 — 그 외 어떤 파일도 수정 금지)
1. `deploy/systemd/anu-pickup.path` (user unit 파일 — **설치 아님, 파일만**)
2. `deploy/systemd/anu-pickup.service` (oneshot, ExecStart=sh entrypoint)
3. `scripts/anu_pickup_entrypoint.sh` (sh entrypoint)
4. `dispatch/anu_pickup_driver.py` (py runner)
5. `tests/regression/test_anu_pickup_driver_2721.py`
6. `docs/p0b_driver_runbook_260601.md`
(evidence: memory/events/task-2721.done · memory/reports/task-2721.md)
★ `dispatch/anu_result_pickup_runner.py` / `dispatch/anu_owned_callback_enforcement.py`(P0-a, main) **무수정** — import/호출만.
★ `scripts/finish-task.sh` **수정 금지**(writer atomic 화는 P0-b+1 후속).

## 설계 단일소스 (먼저 읽을 것)
`memory/tasks/task-2721-DRAFT.md` (v3) — 10요소·6조건·4축 분리 전부 반영됨. 본 task md 와 DRAFT 충돌 시 본 task md 우선.

## 1. systemd path/unit 파일 (생성만 — enable/install 0)
- `deploy/systemd/anu-pickup.path`:
  ```ini
  [Unit]
  Description=ANU result.json pickup watcher (P0-b, DISABLED by default)
  [Path]
  PathModified=%h/workspace/memory/events
  Unit=anu-pickup.service
  [Install]
  WantedBy=default.target
  ```
- `deploy/systemd/anu-pickup.service`:
  ```ini
  [Unit]
  Description=ANU pickup oneshot runner
  [Service]
  Type=oneshot
  WorkingDirectory=%h/workspace
  Environment=PYTHONPATH=%h/workspace
  ExecStart=%h/workspace/scripts/anu_pickup_entrypoint.sh
  ```
- ★ unit/service 에 ANU key·secret literal **0**(env 로 key 주입 금지, runner 가 .env.keys 로드).

## 2. sh entrypoint (`scripts/anu_pickup_entrypoint.sh`)
- ★ **activation flag 확인**: `%h/workspace/memory/state/p0b_driver_enabled` 부재/`enabled` 아님 → 즉시 `exit 0`(no-op).
- driver 레벨 **flock**(`$XDG_RUNTIME_DIR/anu-pickup.driver.lock`, `flock -n`) single-flight. 획득 실패 → `exit 0`.
- `cd %h/workspace && PYTHONPATH=%h/workspace python3 -m dispatch.anu_pickup_driver` 호출. ★ ANU key/secret 인자 전달 **0**.

## 3. py runner (`dispatch/anu_pickup_driver.py`)
- ★ **activation flag 재확인**(py 측에서도 부재 시 즉시 no-op return — sh+py 이중).
- ★ **scan 한정**: `glob('%h/workspace/memory/events/task-*.result.json')` **final 만**. `*.result.json.tmp-*`/partial/다른 marker·jsonl·md **scan 제외 → no-op**.
- 각 final result.json 에 **wake 전 6조건**(전부 PASS 시에만 `pickup_once` 진입):
  1. size > 0 (0 → quarantine)
  2. JSON parse PASS (실패 → quarantine)
  3. schema PASS (필수 필드/타입 실패 → quarantine)
  4. owner proof PASS (`verify_collector_authoritative` 실패 → quarantine)
  5. self/foreign key 아님 (owner_key ≠ ANU → quarantine)
  6. dedupe/done/acked 미처리 (이미 처리 4-tuple → **no-op**, quarantine 아님)
- quarantine = `memory/events/quarantine/` 이동 + 사유 evidence 기록. **wake 미발사**.
- 6조건 통과분만 P0-a `pickup_once(path)`(lock-free) 호출 → 성공 시 `anu_runner_pickup_and_fire`(ANU key sealed, self-key REFUSE) 로 wake.
- ★ secret 은 `utils.env_loader`/.env.keys 런타임 로드만. argv/log/jsonl 에 key literal **0**.
- evidence jsonl(§7) append.

## 4. activation flag (생성 금지 — 경로만 정의)
- `memory/state/p0b_driver_enabled`. **부재 = DISABLED = 전면 no-op**(sh+py 양쪽). 내용 `enabled` 일 때만 동작.
- ★ 본 task 에서 **flag 파일 생성 금지**(default disabled 유지). 테스트는 tmp dir 에 mock flag 로만.

## 5. driver 레벨 동시성 / lock
- ★ `pickup_once` 내부 **lock-free 유지**(P0-a). driver 는 **flock single-flight 만**(entrypoint). pickup_once 에 lock 재도입 금지.
- 중복 wake 방지 = dedupe 4-tuple + done/acked marker + idempotent no-op(P0-a 보유).

## 6. evidence marker / jsonl 스키마
- `memory/events/p0b_driver_runs.jsonl` (per-invocation append):
  ```json
  {"ts":"ISO8601+09:00","result_path":"...","verdict":"WAKE_BUILT|PICKUP_SKIP|QUARANTINE|FIRE_FAILED|NOOP_DISABLED|NOOP_NOT_TARGET",
   "owner_key_class":"ANU|FOREIGN|SELF","quarantined":false,"quarantine_reason":null,
   "fire_cron_id":null,"retry":0,"error":null,"driver":"systemd-path","activation":"enabled|disabled"}
  ```
- ★ ANU key literal 절대 미기록(owner_key_class 분류만).

## 7. runbook (`docs/p0b_driver_runbook_260601.md`)
- enable: (회장 승인 후) unit 설치 `~/.config/systemd/user/` + `systemctl --user daemon-reload` + `enable --now anu-pickup.path` + flag 생성. ★ **본 task 에서 실행 금지**, 절차 문서화만.
- disable/rollback: flag 삭제(즉시 no-op) + `systemctl --user disable --now anu-pickup.path` + unit 제거 + daemon-reload.
- pilot: `memory/state/p0b_pilot_oneshot`(1회 소비 self-clear), low-risk 단일 task 1회만. ★ 본 task 범위 밖.
- active 승격 5단계 증거(아래 §완료조건).

## regression (`tests/regression/test_anu_pickup_driver_2721.py`)
- ★ **default disabled no-op**: flag 부재 → py runner no-op(verdict NOOP_DISABLED), pickup_once 미호출(mock 호출 0).
- ★ **scan 한정**: task-*.result.json final 만 처리. tmp/partial/다른 marker·jsonl·md → NOOP_NOT_TARGET(미처리).
- ★ **quarantine**: size0 / parse fail / schema fail / owner proof fail / self·foreign key → quarantine + wake 0(mock pickup_once 미호출 또는 fire 0).
- dedupe/done/acked 존재 → no-op.
- 6조건 통과 → pickup_once 호출(**mock/stub**) → WAKE_BUILT.
- ★ **pickup_once 실호출 금지**: 전부 mock/stub. 실제 result.json pickup/wake/ANU key wake **실행 0**.
- key literal 0(테스트 코드 포함).

## 금지 (회장 verbatim)
- systemctl enable/start/**daemon-reload 금지** · unit 실제 설치 금지 · activation flag 생성 금지 · crontab 등록 금지.
- cokacdir cron 실행경로 사용 금지 · 실제 result.json pickup/wake 실행 금지 · ANU key wake 금지.
- dev bot callback schedule 생성 금지 · **finish-task.sh 수정 금지** · root/system-level 설치 금지.
- PR merge 금지 · force push/rebase/admin override 금지.
- ★ ANU key literal 을 unit/path/service/entrypoint/driver/test/runbook/argv/log/jsonl 어디에도 노출 금지.
- pickup_once 내부 lock 재도입 금지. P0-a main 파일 무수정.

## finalize (로컬 commit 까지만)
1. 구현 → regression PASS → `git add` expected_files(6) → 로컬 commit. **push/PR/merge 금지(ANU 수행)**.
2. `memory/reports/task-2721.md` 작성: implemented / wired candidate / wired pilot / **active=false** 구분 명시 + 각 파일 역할 + 테스트 결과 + key literal 0 증거.
3. `memory/events/task-2721.done` 생성. ANU key(c119085, sealed) callback. ★ ANU key literal 노출 0.

## 완료 조건 (회장 verbatim)
- 6개 expected_files만 변경 / regression PASS / default disabled·no-op 테스트 PASS / key literal 0 / scan 한정 테스트 PASS / quarantine 테스트 PASS / pickup_once 호출은 mock·stub 기반 테스트만 / 보고서에 implemented·wired candidate·wired pilot·active=false 구분 명시.
- active 승격 5단계(설치·활성화·pilot 후 별도): ①dev result.json만 작성 ②OS event 감지 ③pickup_once 호출 ④ANU key wake ⑤독립 ANU 세션 spawn → **본 task 에서는 active=false 고정**.

## ANU 후속 (봇 아님 — governance)
ANU 독립 재검증(6파일/regression/default no-op/scan 한정/quarantine/key literal 0/pickup_once mock/lock-free) → **PR-ready 후보까지만 보고**. 실제 설치·활성화·pilot·merge 는 별도 회장 승인 전까지 금지.

## allowed_resources
```yaml
allowed_resources:
  paths:
    - "deploy/systemd/anu-pickup.path"
    - "deploy/systemd/anu-pickup.service"
    - "scripts/anu_pickup_entrypoint.sh"
    - "dispatch/anu_pickup_driver.py"
    - "tests/regression/test_anu_pickup_driver_2721.py"
    - "docs/p0b_driver_runbook_260601.md"
    - "memory/events/task-2721.done"
    - "memory/reports/task-2721.md"
  forbidden_paths:
    - "scripts/finish-task.sh"
    - "dispatch/anu_result_pickup_runner.py"
    - "dispatch/anu_owned_callback_enforcement.py"
    - ".github/**"
  commands:
    - "pytest"
    - "python3 -m pytest"
    - "python3 -m py_compile"
  merge_policy: "none"
  ttl_hours: 48
```

## ★ dispatch 템플릿 G3(PR/머지) 무시. finalize 로컬 commit only 우선. finish-task --action pr 금지. systemctl/설치 일체 금지.

## goal_assertions (auto-generated)
- `python3 -m pytest tests/regression/test_anu_pickup_driver_2721.py -q`
- `python3 -c "import re,glob,sys; bad=[f for f in ['deploy/systemd/anu-pickup.path','deploy/systemd/anu-pickup.service','scripts/anu_pickup_entrypoint.sh','dispatch/anu_pickup_driver.py','tests/regression/test_anu_pickup_driver_2721.py','docs/p0b_driver_runbook_260601.md'] if 'c119085addb0f8b7' in open(f).read()]; sys.exit(1 if bad else 0)"`