# task-2774+15 — PR #254 same-PR micro-fix: schedule_id None 명시 체크 (HIGH)

상태명: `TASK2774P15_PR254_SCHEDULE_ID_NONE_EXPLICIT_MICROFIX_APPROVED_ACTIVE_FALSE`
담당: dev2-team (PR #254 / branch `task/task-2774+13-dev2` 소유)
회장 승인: 2026-06-26 — fresh Gemini 신규 **HIGH** 1건 **+15 micro-fix**(dismiss 아님). **극소 범위·루프 확장 금지.**

## 배경
ANU 독립검증 + fresh-head 재리뷰서 **신규 HIGH 1건**(04:07:30Z, d4849cb9):
`_g("schedule_id")` 결과가 **정수 0 같은 falsy non-None** 값일 때 `or ""` 패턴이 **빈 문자열로 잘못 치환** →
schedule_id 누락 / 이후 `bool(schedule_id)` 기반 reg 판정 오동작 가능. schedule_id 는 real launcher
audit/registration 판정과 연결되는 값이라 안전하게 닫는다. (이전 stale 2건은 +14가 코드로 해소 완료.)

## 수정 범위 (엄수)
- **PR #254 위 same-PR micro-fix commit 1개만** 추가 (새 PR 금지). branch `task/task-2774+13-dev2`.
- 수정 허용: **`dispatch/anu_callback_launch_audit.py`** + **`tests/regression/test_normal_callback_real_launcher_2774p13.py`** 만.
- **설계문서 수정 금지.**
- **driver / runner / enforcement / pickup_once / governor / matrix / flag 수정 금지.**

## 수정 요구
1. `_g("schedule_id")` 값을 **먼저 변수에 담는다**.
2. **None 일 때만** 빈 문자열로 처리.
3. non-None 값은 **falsy 여도** `str(value).strip()` 결과를 사용.
4. 예시(권장):
   ```python
   schedule_raw = _g("schedule_id")
   schedule_id = str(schedule_raw).strip() if schedule_raw is not None else ""
   ```
5. 기존 정상 문자열 schedule_id 동작 유지. `schedule_id=0` 같은 값이 의도치 않게 `""` 로 바뀌지 않음.
6. **raw key redaction(+14) / exception type-only(+14) / default OFF / gated launcher 동작은 절대 깨지지 않아야 함.**

## 필수 테스트 (test_normal_callback_real_launcher_2774p13.py)
- schedule_id 문자열 정상 유지.
- schedule_id `None` → `""`.
- schedule_id `0` → `"0"` (또는 최소한 빈 문자열로 오판되지 않음).
- schedule_id `False` 같은 non-None falsy 값도 None 과 구분.
- 기존 `--key VALUE` redaction PASS. 기존 `--key=VALUE` redaction PASS.
- exception type-only PASS.
- 신규 17(또는 수정 후 관련) 테스트 PASS · `py_compile` PASS · 관련 회귀 PASS.

## ★ LOOP_BOUNDARY
- 이번은 **+15 schedule_id HIGH 1회 micro-fix**.
- fresh Gemini 가 **새 HIGH/Critical7** 다시 내면 → **즉시 CHAIR_REQUIRED** 보고.
- 같은 schedule_id 축 **스타일성 MED만** 나오면 → **evidence 분리 후보**로 보고.
- 자동 +16/+17 수정 루프 **금지**.

## 절대 금지
merge / B real 1-shot 실행 / real cokacdir 발사 / activation flag 생성 / systemd start·enable /
recurring 생성 / matrix `ACTIVE=true` / `ENFORCED=true` 단정 / driver·runner·enforcement·pickup_once 수정 /
설계문서 수정 / auto merge.

## 보고 항목 (필수)
micro-fix commit sha · 수정 파일 · schedule_id None/0/False 테스트 결과 · 기존 raw key redaction 유지 증거 ·
기존 exception type-only 유지 증거 · 테스트 결과 · CI/Gemini 상태 · ACTIVE=false 유지 증거 · merge-ready 여부.

## 완료 처리 + ANU callback (필수)
- 완료 시 `memory/events/<task_id>.done` + `memory/reports/<task_id>.md`.
- 봇 idle 전 **ANU callback 필수**(성공/실패/blocked 무관). collector=ANU, owner_key=`c119085addb0f8b7`,
  self-key 금지. callback prompt UTF-8 **≤3900 bytes** (`printf '%s' "$P" | wc -c`), envelope 만
  (task_id·PR#254·new head sha·result_path·report_path·one-line). **merge 절대 금지.**

## 진행
dev2 는 micro-fix 구현·테스트(real fire 0)·로컬검증 후 branch push 로 PR #254 갱신, 보고서 + .done +
ANU callback 으로 마감. ANU 가 이후 독립검증/Gemini 재리뷰/CI 확인 → 회장 머지 판단.

## allowed_resources (본 task의 capability)

```yaml
allowed_resources:
  paths:
    - "dispatch/anu_callback_launch_audit.py"
    - "tests/regression/test_normal_callback_real_launcher_2774p13.py"
    - "memory/reports/task-2774+15.md"
    - "memory/events/task-2774+15.done"
  forbidden_paths:
    - ".github/**"
    - "memory/events/*.cron-*"
    - "scripts/finish-task.sh"
    - "memory/state/p0b_driver_enabled"
    - "memory/state/p0b_callback_launch_enabled"
    - "dispatch/anu_result_pickup_runner.py"
    - "dispatch/anu_owned_callback_enforcement.py"
    - "dispatch/anu_pickup_driver.py"
  commands:
    - "pytest"
    - "python3 -m py_compile"
    - "git"
    - "gh"
  merge_policy: "manual"
  ttl_hours: 24
```