---
task_id: task-2699
type: context
scope: task
created: 2026-05-27
updated: 2026-05-27
status: in-progress
---

# 맥락 노트: task-2699

**task**: task-2699

---

## 결정 근거

### [핵심 결정 1] http_post를 신규 모듈(owner_trigger_http_post.py)로 분리
- `OwnerTriggerOnly.__init__`은 이미 `http_post: Callable[[str,str,dict,dict],dict]`을 주입받는 구조(코어 무훼손). 따라서 production 구현은 **별도 모듈**에 작성하고 entry point에서 주입한다.
- 대안(코어에 직접 urllib 삽입) 기각: 코어 재작성 금지 원칙 위반 + test 주입성 상실.

### [핵심 결정 2] urllib 단일 endpoint, 방어적 재검증(defense-in-depth)
- 코어가 이미 `assert_endpoint_allowed`로 hard-block 하지만, production http_post도 호출 직전 `assert_endpoint_allowed(method, path)`를 **재검증**한다 (이중 안전). merge/pulls/admin 주입 시 ForbiddenEndpointError.
- token은 인자로 받은 headers에만 존재 → 어떤 예외/로그/diagnostics에도 headers/token 미포함. 추가로 `auto_gemini_triage._redact_tokens` 재사용으로 diagnostics redact (§3 재사용 요건).

### [핵심 결정 3] token_provider = OWNER_GEMINI_TRIGGER_TOKEN only, fail-closed
- env에서 `OWNER_GEMINI_TRIGGER_TOKEN`만 읽음. 부재/빈값 → TokenBoundaryViolation (POST 0). BOT_GITHUB_TOKEN/GH_TOKEN 등 fallback 절대 0.

### [핵심 결정 4] router §8 = main 반영 누락 (정본 = worktree)
- 규명 결과: `owner_gemini_trigger_router.py` + 의존 2개(`gemini_evidence_freshness_checker.py`, `owner_gemini_trigger_router_audit.py`)가 **모든 worktree에서 byte-identical** (md5 `a60e3c7d...`, 24개 worktree 일치). main에는 **미tracked** + orphan `.pyc`만 잔존.
- 결론: 정본은 worktree canonical 사본, main 반영이 **누락**된 상태. → 본 task에서 byte-identical 정본을 main에 반영(단일화) + orphan pyc 제거.
- 안전성: 모든 사본이 동일 내용이므로 fork 아님. 의존 2개는 stdlib + anu_v2 내부만 의존(self-contained), AST parse OK 확인.

## 3 Step Why 자문 (Lv.3+)

- **1st Why — 왜 이 설계(http_post 분리 주입 + entry point)가 필요한가? (A)**
  코어는 검증/dedupe/redaction이 완성됐으나 실제 POST 구현체가 0이고 구동 진입점이 없어 capability가 "코드만 존재, 실행 불가" 상태. ANU 단발 트리거 시 매번 ad-hoc POST를 새로 작성하게 되어 doctrine 위반. 정식 경로를 production-ready로 완성해야 ad-hoc을 영구 제거 가능.

- **2nd Why — 왜 A(분리 주입)가 최선의 접근인가? (B)**
  코어의 주입 구조(http_post/token_provider Callable)를 그대로 활용하면 코어 무훼손 + test 주입성 유지 + 단일 책임 분리(SRP)가 동시에 성립. 코어에 urllib을 직접 넣으면 재작성 금지 위반 + mock 불가로 live POST test 위험.

- **3rd Why — 왜 B가 다른 대안보다 나은가? (C)**
  대안 ① 코어 직접 수정 → 금지 위반 + 회귀 위험. 대안 ② entry point에서 inline lambda http_post → redaction/endpoint 재검증 누락 위험 + 재사용 불가. 분리 모듈은 endpoint 재검증·redaction·dry-run을 캡슐화하고 CLI/scheduler 양쪽이 동일 구현을 공유하므로 가장 안전하고 일관적.
- **A-B-C 일관성**: A(정식경로 완성 필요) → B(주입구조 활용이 무훼손·테스트성 동시충족) → C(분리모듈이 보안검증 캡슐화로 대안 우위). 논리 일관 ✓.

## 참조 자료

- 코어: `anu_v2/owner_trigger_only.py` (OwnerTriggerOnly, invoke_from_scheduler, FORBIDDEN_ENDPOINT_PATTERNS)
- decision v1: `anu_v2/owner_trigger_decision.py` (validate_decision 8조건)
- audit: `anu_v2/owner_trigger_audit.py` (token_hash_prefix, dedupe flock)
- scheduler: `anu_v2/executor_scheduler.py` (ExecutorScheduler — owner_trigger 주입받음)
- redact 재사용: `anu_v2/auto_gemini_triage.py::_redact_tokens` (stdlib only)
- spec: `memory/specs/system_owner_trigger_only_capability_spec_260511.md`, `memory/specs/system_owner_gemini_trigger_router_spec_260523.md`

## G2 게이트 결과 (2026-05-27)

- **마아트 독립 검증**: 10/10 PASS, 결함 0, 구현 승인. 코어 3파일(owner_trigger_only/decision/audit) 무훼손 git diff 확인.
- **로키 레드팀**: NEEDS_HARDENING → 4건 발견:
  - F-1 [Med] `_redact_tokens` gho_/ghu_/ghr_ prefix 미처리 → **수정** (owner_trigger_http_post.py에 `_redact_owner_response` 로컬 broader-prefix redact 추가, auto_gemini_triage 무수정).
  - F-4 [Low] `_make_gh_runner` os.environ 상속으로 OWNER 토큰 gh subprocess 노출 → **수정** (gh/git runner env에서 OWNER_GEMINI_TRIGGER_TOKEN 제거).
  - F-2 [Med] audit JSONL OS레벨 변조 시 dedupe 우회 → **수용 위험**: owner_trigger_audit.py는 "재사용·코어 무훼손" 자산이며 동일권한 프로세스의 파일 변조는 파일기반 audit의 일반 한계(본 wiring이 도입한 결함 아님). 별도 하드닝 task 권고.
  - F-3 [Low] validate_decision pr<=0 미검증 → **수용**: `_build_post_comment_path`가 2차 차단(defense-in-depth 이미 존재), decision.py는 코어 무훼손 대상.
- 하드닝 후 회귀 226 passed / 0 failed 재확인.

## 주의사항

- ★ live POST 절대 금지 (mock/dry-run only). 실제 OWNER PAT로 test POST 금지.
- ★ token raw 값을 코드/test/로그/marker/stdout/stderr 어디에도 하드코딩·노출 금지.
- ★ COMMENT_BODY 외 body 허용 금지 (외부 입력 body 차단).
- ★ merge/push/PR/admin endpoint 경로 추가 금지.
- ★ artifact/memory를 PR head commit 금지 (runtime code only).
