# task-2771+1 보고서 — PR #247 Gemini HIGH2+medium2 content qc micro-fix

## SCQA 요약

**S (상황)**: PR #247(branch `task/task-2771-dev1`, base main, head 772508c5)에 Gemini가 content QC 모듈
`teams/content/qc/qc_verify.py`의 HIGH 2건 + medium 2건(경로 traversal 방어 미흡)을 지적해 머지가 차단됨.

**C (문제)**: HIGH 미해소 상태로는 PR #247 머지 불가. 단, 신규 설계/리팩터는 회귀 위험. ANU가 이미 fix 완료본
번들(2파일, sha256 고정)을 검증 완료(content QC 17 passed, 전체 174 passed)하여 전달함.

**Q (질문)**: 재구현 0으로 ANU fix 번들을 PR #247에 정확히 반영해 HIGH 2 + medium 2를 닫고 머지 가능 상태로 만들 수 있는가?

**A (답변)**: 기존 worktree(`task-2771-dev1`)에 번들 2파일을 그대로 교체(sha256 == manifest 정확 일치) → git diff 정확히 2파일 →
regression 174 passed → 1 commit(6fc945b9) push로 PR #247 업데이트 완료. L1 스모크에서 HIGH-1 가드(악성 task_id 거부)
실동작 확인. 머지는 owner 승인 대기(manual). 소요: 단일 세션, 2파일 밖 수정 0건.

---

## 변경 파일 목록 (내 commit 6fc945b9 vs parent 772508c5 — 정확히 2파일)
- `teams/content/qc/qc_verify.py` (sha256 `b786d91c…`, 10792B)
- `tests/regression/test_content_qc_2769.py` (sha256 `17b8e83e…`, 8145B)

> PR #247 전체 diff에는 8파일이 보이나, 6파일(dispatch/__init__.py, dispatch/gate_shadow_runner.py,
> scripts/finish-task.sh, 3개 regression test)은 **PR base commit 772508c5에 이미 존재하던 sealed bundle**이며
> 본 task에서 **건드리지 않음**. 본 commit은 허용 2파일만 변경(아래 증거).

## HIGH 2 대응 내용 (ANU fix = source of truth, 재구현 0)
- **HIGH-1** (run_gate, qc_verify.py:126~133): `task_id` 검증 추가 — `_SAFE_TASK_ID = ^[A-Za-z0-9._+-]+$`,
  `"/"`·`".."` 포함 시 경로 구성 전 거부. invalid → `invalid-task-id.qc-result` FAIL(fail_closed).
- **HIGH-2** (산출물 paths 루프): 절대경로 거부 + `(workspace/p).resolve()`가 workspace 내부인지 검증, `..` traversal 거부.

## medium 2 대응 내용
- **medium-3** (`_parse_allowed_paths`): allowed_resources 블록 없으면 빈 리스트(전체 폴백 오탐 제거) +
  `paths:` 항목 trailing comment(#) 무시 + 따옴표 우선 추출.
- **medium-4** (산출물 읽기): `fp.is_dir()` → 구조화 FAIL, `read_text` try/except → structured FAIL(traceback crash 방지).

## 테스트 결과 (regression PASS)
실행: `pytest tests/regression/{test_content_qc_2769, test_gate_shadow_runner_2765, test_gate_shadow_finish_task_2766,
test_goal_contract_strict_template_2767, test_v213_phase1_gate_2757}.py`
→ **174 passed in 0.55s** (실패 0). 불칸 1회 + 팀장 직접 재검증 1회 = 2회 PASS 확인.

## L1 스모크테스트 결과 (필수 기록)
- **서버 재시작**: 해당없음 (qc_verify.py는 subprocess 기동형 QC 모듈, 상주 서버 아님)
- **API 응답 확인**: 해당없음 (HTTP API 아님)
- **subprocess 실동작 확인 (실행함)**:
  - HIGH-1 가드: `python3 teams/content/qc/qc_verify.py --task-id "../../etc/passwd" --team content`
    → FAIL + 사유 `"unsafe task_id 거부(path traversal 방어): '../../etc/passwd'"`, `fail_closed: true`. **가드 실동작 확인**.
  - 정상 경로: `--task-id "task-2771+1"` → crash 없이 실행(.qc-result 생성). traceback 0.
  - 스모크 산출물(`invalid-task-id.qc-result`)은 정리 완료, git 작업트리 clean.
- **스크린샷**: 해당없음 (CLI subprocess, UI 없음)
- 판정: L1 통과 (subprocess 실행 + path traversal 가드 실거부 확인).

## 2파일 밖 수정 0 증거
```
$ git diff --name-only 772508c5 6fc945b9
teams/content/qc/qc_verify.py
tests/regression/test_content_qc_2769.py
```
forbidden(dispatch/**, scripts/finish-task.sh, teams/shared/**, gate_shadow_runner.py) **무수정**.

## PR 상태
- PR #247 **업데이트**(새 PR 아님). branch `task/task-2771-dev1`.
- base: main / 새 head: `6fc945b9` (이전 772508c5에 1 commit 추가).
- mergeable: MERGEABLE, state: OPEN.
- Gemini HIGH thread: **코드 fix push로 해소**(임의 resolve 0). CI/Gemini polling 0.
- **merge_approved: false** — owner 승인 대기(merge_policy: manual). 자동 merge/squash 시도 0.

## 머지 판단
- **머지 필요**: No (owner 승인 대기 — manual policy)
- **브랜치**: task/task-2771-dev1
- **워크트리 경로**: /home/jay/workspace/.worktrees/task-2771-dev1
- **머지 의견**: HIGH 2 + medium 2 코드 fix push로 해소, regression 174 passed, L1 가드 실동작 확인.
  2파일 밖 수정 0. owner가 Gemini 재리뷰 HIGH 0건 확인 후 머지 권장.

## 모델 사용 기록
- 불칸(백엔드): sonnet — 번들 2파일 cp 교체 + sha256 검증 + git diff 스코프 확인 + regression 실행 (기계적 적용, haiku 미사용)
- 팀장(Opus): 직접 재검증(diff/sha256/regression 재실행), commit/push, L1 스모크, 보고서. 코딩 위임 원칙 준수.
- 이리스/아테나/아르고스: 미소환 (프론트/UX/별도 테스터 불요 — 백엔드 QC 모듈 단일 파일 micro-fix).

## ★ 완료 게이트 차단 — ANU 판정 필요 (발견 이슈 및 해결 시도)

### 현상
`finish-task.sh task-2771+1 dev1` 실행 시 QC는 PASS(WARN)했으나, **scope-guard가 FAIL → .done 차단 + .escalate 생성**.
- failure-envelope: `failure_kind: scope_violation_count_8`, `terminal_state: SCOPE_GUARD_FAIL`,
  `collector_role: ANU`, `owner_key: c119085…(ANU key)`, `self_key_used: false`,
  `registration_mode: failure_callback_before_exit_guard` → **ANU-owned 실패 콜백 정상 등록**(self-key 0).

### 원인 분석 (구조적 false-positive)
scope-guard는 `git diff --name-only main..HEAD`로 **PR 브랜치 전체 누적 diff(8파일)**를 task 허용 2파일과 대조함.
6개 위반 파일(dispatch/__init__.py, dispatch/gate_shadow_runner.py, scripts/finish-task.sh, 3개 gate_shadow/goal_contract regression)은
**전부 부모 commit 772508c5("[task-2771] apply sealed core/work infra bundle (8 files)")의 변경**으로,
본 task(task-2771+1)의 증분 commit 6fc945b9은 **건드리지 않았음**.
- 증거: `git diff --name-only 772508c5 6fc945b9` → 정확히 `teams/content/qc/qc_verify.py` + `tests/regression/test_content_qc_2769.py` 2파일.

즉 finish-task.sh의 scope-guard는 **기존 PR 위에 commit을 얹는 증분 micro-fix task의 scope base를 PR head(772508c5)가 아닌 origin/main(38ee0555)으로 잡아** 부모 PR의 정당한 변경까지 본 task 위반으로 집계함. (scope base override인 worktree-base.json은 merge-base가 빈 값일 때만 fallback → 본 케이스 미적용.)

### 자체 해결이 불가능한 이유 (STOP_REPORT 사유)
- 6파일 수정/제거 → task 금지(forbidden_paths: dispatch/**, scripts/finish-task.sh) + 부모 PR 정당 변경 훼손. 불가.
- scripts/finish-task.sh / scope-guard 로직 수정 → task forbidden_paths. 불가.
- task 파일 allowed_paths 확장/scope 게이밍 → 우회 시도(금지). 불가.
- 따라서 **ANU 판정 에스컬레이션**이 유일한 정당 경로. (수동 .done 생성 금지 준수.)

### 권고 (ANU)
본 task 산출물(증분 2파일 fix)은 정확·완료. PR #247은 이미 6fc945b9로 업데이트됨(MERGEABLE).
scope-guard 차단은 누적-PR-diff vs 증분-allowed-paths 불일치에 의한 게이트 artifact.
ANU가 (a) 증분 base 772508c5 기준 scope 재검증(2파일 PASS) 후 closeout, 또는 (b) PR #247 owner 머지 판단으로 진행 권장.

## 비고 / 발견 이슈 및 해결
- 적용 단계 이슈 0건: 번들 sha256가 manifest와 1차 시도 정확 일치, regression 1차 174 passed, L1 가드 실동작 1차 확인.
- 완료 게이트 이슈 1건(위): scope-guard 누적-diff false-positive → ANU 에스컬레이션(실패 콜백 등록 완료).
- base/PR head: base 772508c5 → 새 head 6fc945b9.
