# task-2777+1 (r1) — PR #257 Gemini micro-fix (systemctl/DBus HIGH + epoch tz MED + cron helper MED)

상태명: `TASK2777_PR257_GEMINI_MICROFIX_DONE_ACTIVE_FALSE`
담당: dev2-team · 회장 승인: 2026-06-28 (bounded +1 micro-fix 1회 · OWNER resolve 금지)
판정: **MERGE_CANDIDATE 보고만** (merge 금지 — ANU 독립검증 → fresh Gemini → CI → merge candidate 재판정)
브랜치: `task/task-2777-dev2` 위 commit 추가 → **PR #257 head 갱신** (새 PR 생성 0)
설계 단일소스: `control_panel_status_command_spec_260628.md` + `capability_matrix_update_plan_260628.md`

## 0. 배경
PR #257(read-only `anu_control_status.py`)은 CI 수렴했으나 Gemini unresolved 3건
(HIGH 1 + MED 2) 으로 MERGE_CANDIDATE 미달. HIGH(조종석 systemd 상태 신뢰성 결함)은
style nit 이 아니므로 bounded +1 micro-fix 로 닫는다. OWNER resolve 로 닫지 않는다.

## 1. expected_files (외 변경 0)
- `scripts/anu_control_status.py` — 안전 처리 보정
- `tests/regression/test_anu_control_status_2777.py` — 회귀 13건 추가(14→27)
- `memory/reports/task-2777-r1.md` — 본 r1 보고서
- `memory/events/task-2777+1.done` — 완료 마커

(`.github/**` · `scripts/finish-task.sh` · `dispatch/anu_pickup_driver.py` · `memory/state/**` ·
inbox/archive/quarantine 등 forbidden_paths 무변경. 작업지시서 `memory/tasks/task-2777+1.md` 미커밋.)

## 2. 수정 1 — Gemini HIGH: systemctl / DBus 실패 안전 처리

**결함(이전):** `_systemd._probe` 가 `(proc.stdout or proc.stderr or "").strip()` 의 첫 줄을
status 로 그대로 반환 → DBus/session bus 불가(stdout empty + stderr "Failed to connect to bus…")
시 **raw stderr 가 status 필드/출력에 오염**되고, 실패를 `inactive/disabled` 로 오인할 여지.

**보정:**
- stdout 첫 토큰이 정상 status allowlist(`_KNOWN_ACTIVE_TOKENS` / `_KNOWN_ENABLED_TOKENS`)일
  때만 신뢰. exit code 가 nonzero 여도(`is-active inactive=3`, `is-enabled disabled=1`) stdout
  토큰이 정상이면 신뢰 — 정상 nonzero 를 실패로 오인 0.
- 미인식 stdout · empty · DBus unavailable · command missing(FileNotFoundError) · timeout
  (TimeoutExpired) · 기타 예외 → 모두 `"unavailable"` 로 **안전 표기**(확정 inactive/disabled 0).
- 원인은 `_classify_systemctl_stderr` 가 **고정 분류 토큰**(dbus_unavailable / command_missing /
  unit_not_found / permission_denied / systemctl_error)으로만 요약 → `warnings` 에
  `SYSTEMD_STATUS_UNAVAILABLE: <verb> reason=<token> rc=<n>` 형태로 추가. **raw stderr 원문 ·
  길게 출력 · traceback · key/env 유출 0.**
- `_systemd` 반환을 `(active, enabled, warnings)` 3-튜플로 확장, `collect()` 가 warnings 병합.

금지사항 준수: 실패인데 inactive/disabled 확정표시 0 · stderr 출력오염 0 · traceback 0 · raw key 0 · crash 0.

## 3. 수정 2 — Gemini MED: activation_epoch age timezone

**결함(이전):** ISO naive datetime 에 `dt.timestamp()` 사용 → **local(KST) 임의 해석**.
음수(미래) age 는 `max(0,…)` 로 무음 clamp(경고 없음).

**보정:** `_epoch_age_seconds` → `(age_seconds, warning_token)` 튜플.
- 순수 숫자 → `time.time() - epoch` (**tz 독립**). 비유한값(inf/nan) → `EPOCH_PARSE_UNAVAILABLE`.
- ISO8601 **tz-aware** → offset 으로 정확 비교.
- ISO8601 **naive** → local 임의 해석 금지 → age=None + `UNKNOWN_NAIVE_DATETIME` warning
  (UTC 강제 가정도 안 함 — 명시적 UNKNOWN).
- 파싱 실패 → `EPOCH_PARSE_UNAVAILABLE`. 미래 epoch → age 0 + `EPOCH_IN_FUTURE` warning.
- 음수/실패 모두 **crash 0**, KST 환경 안정. `collect()` 가 epoch warning 표면화.

## 4. 수정 3 — Gemini MED: cron helper 구조 (raw key 금지)

**결함(이전):** module import 후 결과를 무시하고 import 성공/실패 무관하게 UNKNOWN 반환 — 의도 모호.

**보정:** `_cron_count` → `(value, reason)` 튜플. sealed read-only helper
(`scripts.anu_cron_count_readonly.cron_count_readonly`)가 **실제 존재 + 계약(int>=0, raw key
출력/기록 0) 충족 시에만** 사용. 부재 / 계약 불명확 / 비-count 반환 / 예외 → `UNKNOWN_KEY_REQUIRED`
유지 + **왜 UNKNOWN 인지 reason 명시**(예: "no sealed read-only cron helper module present;
raw key required and forbidden"). `cron_reason` 를 출력 dict/human/warnings 에 표면화.
**raw key literal 0 · 새 key path 0 · cron 등록/수정 0 · cron-list 직접 호출 0.**

## 5. 테스트 (14 → 27, +13 신규)
- systemctl DBus failure(empty stdout + stderr) → crash 0 · unavailable · `SYSTEMD_STATUS_UNAVAILABLE`
  warning · raw stderr 가 status/JSON/human 오염 0 · JSON round-trip 정상.
- systemctl nonzero+정상 토큰(inactive/disabled) → 신뢰(경고 0).
- systemctl command missing / timeout / empty stdout → crash 0 · unavailable · warning.
- systemctl 실패 경로 read-only invariant(파일 생성/수정/append 0).
- epoch numeric → TZ(UTC/Asia·Seoul/America·New_York) 독립 동일 age.
- epoch naive → local 해석 0 · `UNKNOWN_NAIVE_DATETIME` · build_status 표면화.
- epoch aware / future / garbage / inf / nan / 빈값 → 안전 unknown/warning, crash 0.
- cron unavailable → `UNKNOWN_KEY_REQUIRED` + reason · `--key`/`cron-list` literal 출력 0.

**결과:**
- `test_anu_control_status_2777.py` 27 passed.
- 인접 회귀(2776 / 2775 / 2760 / 2721 + 2777) 166 passed.
- `python3 -m py_compile` PASS (source + test).
- (참고: `test_base_source_isolation_2729p9.py` 는 worktree 이름 고정 환경 전제로 본 worktree 에서
  collection skip — 본 변경과 무관한 사전 존재 환경 제약.)

## 6. read-only 불변 / 게이트
flag 생성·수정 0 · state 수정 0 · evidence append 0 · driver 실행 0 · fixture 생성 0 ·
callback/real fire 0 · systemd 조작 0(조회만) · cron 등록 0 · archive 이동 0 · key 출력/rotation 0.
effective diff = expected_files 2개. ACTIVE=false · flags OFF 유지 · scope 확장 0.

## 7. 판정
HIGH 1 + MED 2 대응 완료, bounded fix. **merge 금지.** PR #257 head 갱신 후 ANU 독립검증 →
fresh Gemini → CI 재실행 → MERGE_CANDIDATE 재판정. 본 보고는 candidate **보고만**.
