# task-2722 — P0-b+1: result.json writer atomicization (fsync 보강)

## 회장 인가 (2026-06-02)
P0-b+1 승인. writer 위치 정정 수용: **finish-task.sh 아님** → `executor_write_result_json @ dispatch/anu_owned_callback_enforcement.py`(P0-a main). 이미 tmp write→os.replace(atomic rename) 구현됨, **빠진 fsync(file)+fsync(dir)만 surgical 보강**. 범위 = writer atomicization으로만 제한.

## worktree
- `/home/jay/workspace/.worktrees/task-2722-dev1` (branch `task/task-2722-dev1`, base origin/main **7839dede** = PR #167 P0-b 포함). os.fsync 0 확인(보강 대상).

## expected_files (회장 승인 — 정확히 2 코드 + evidence)
1. `dispatch/anu_owned_callback_enforcement.py` ← **주 수정**(P0-a main, writer atomicization으로만 제한)
2. `tests/regression/test_anu_owned_callback_enforcement_2717.py` ← 회귀 보강
(evidence: memory/events/task-2722.done · memory/reports/task-2722.md)
★ 그 외 어떤 파일도 수정 금지.

## 목적 / 패턴
result.json writer 크래시·전원차단 내성. 패턴 = **tmp write → fsync(file) → fsync(dir) → atomic rename(os.replace)**.
- 현재(L796~798): `with open(tmp,"w") → json.dump → os.replace(tmp,path)` — atomic rename은 있음.
- ★ 추가: `with` 블록 닫기 전(또는 직전) `fh.flush()` + `os.fsync(fh.fileno())`(file), `os.replace` 후 **parent dir fd `os.fsync`**(dir). rename durability 보장.

## 허용 수정 (회장 verbatim)
- `executor_write_result_json` 내부 또는 **바로 인접한 helper만** 수정.
- tmp write 후 **fsync(file)** 보강.
- os.replace 후 **parent directory fsync** 보강(`dfd=os.open(dir,O_RDONLY); os.fsync(dfd); os.close(dfd)` 류, 실패 시 fail-safe).
- ★ 기존 result.json **schema / envelope / field name / callback contract 변경 금지**.
- ★ 기존 **function signature 변경 금지** · 기존 **caller 동작 변경 금지**.
- regression 추가/보강.
- ※ 동일 패턴 L720~722(다른 record writer)도 같은 helper 적용 가능하나, **schema/signature 불변** 한정.

## 금지 (회장 verbatim)
- finish-task.sh 수정 금지(writer 없음) · pickup driver(`anu_pickup_driver.py`) 수정 금지 · systemd unit 수정 금지.
- activation flag 생성 금지 · systemctl enable/start/daemon-reload 금지 · 실 pickup/wake/ANU key wake 금지.
- credential/permission 확장 금지 · result schema 변경 금지 · callback authority 정책 변경 금지.
- QC text-token / finish-task worktree_path / gate-parser hardening / medium defer 4건 **혼입 금지**.
- merge 금지 · push/PR 금지(ANU governance) · ANU key literal 노출 0 · `.github/**` 금지 · P0-a `anu_result_pickup_runner.py` 무수정.

## 검증 조건 (회장 verbatim — finalize 전)
1. `python3 -m pytest tests/regression/test_anu_owned_callback_enforcement_2717.py -q` 전 PASS(증가).
2. **fsync(file fd) 호출 검증**(mock spy ≥1).
3. **fsync(parent dir fd) 호출 검증**(mock spy ≥1).
4. **atomicity regression**: tmp 미완성/write 중 크래시 시뮬 → final result.json 미생성(이전값 유지, 부분 result 0).
5. `os.replace` 경로 유지(atomic rename 보존).
6. schema 불변 검증(필드/구조 동일).
7. function signature 불변 검증.
8. ANU key full literal **0**.
9. active=false / installed=false / wired=false 유지.
10. `git diff --name-only origin/main` = expected_files 내부(코드 2 + evidence).
- smoke: `python3 -m py_compile dispatch/anu_owned_callback_enforcement.py`.

## loop budget (회장 verbatim)
- HIGH/CRITICAL 신규 발생 시 자동 fix 금지 → CHAIR_REQUIRED.
- medium 자동 보강 최대 2회. same blocker 2회+ → LOOP_BOUNDARY → CHAIR_REQUIRED.
- expected_files 밖 수정 필요 → CHAIR_REQUIRED.

## finalize (로컬 commit 까지만)
1. fix → regression PASS → `git add` expected_files → 로컬 commit. **push/PR/merge 금지(ANU governance)**.
2. `memory/reports/task-2722.md` 작성(fsync file/dir 보강 / os.replace 보존 / schema·signature 불변 / atomicity regression / ANU key 0).
3. `memory/events/task-2722.done` 생성. ANU key(c119085, sealed) callback. ★ ANU key literal 노출 0.

## ANU 후속 (봇 아님 — governance)
독립 재검증(2코드 scope/fsync file+dir/atomicity/schema·signature 불변/regression/key 0/active=false) → **LOCAL_FIX_VERIFIED 까지만 보고**. push/PR/OWNER gemini/merge 는 별도 회장 승인 전까지 금지.

## allowed_resources
```yaml
allowed_resources:
  paths:
    - "dispatch/anu_owned_callback_enforcement.py"
    - "tests/regression/test_anu_owned_callback_enforcement_2717.py"
    - "memory/events/task-2722.done"
    - "memory/reports/task-2722.md"
  forbidden_paths:
    - "scripts/finish-task.sh"
    - "dispatch/anu_result_pickup_runner.py"
    - "dispatch/anu_pickup_driver.py"
    - "deploy/systemd/**"
    - ".github/**"
  commands:
    - "pytest"
    - "python3 -m pytest"
    - "python3 -m py_compile"
  merge_policy: "none"
  ttl_hours: 48
```

## ★ dispatch 템플릿 G3(PR/머지) 무시. finalize 로컬 commit only. push/PR/merge/systemctl/설치 일체 금지.

## goal_assertions (auto-generated)
- `python3 -m pytest tests/regression/test_anu_owned_callback_enforcement_2717.py -q`
- `python3 -c "import sys; s=open('dispatch/anu_owned_callback_enforcement.py').read(); sys.exit(0 if 'os.fsync' in s and 'os.replace' in s else 1)"`
- `python3 -c "import sys; s=open('dispatch/anu_owned_callback_enforcement.py').read(); sys.exit(1 if 'c119085addb0f8b7' in s else 0)"`