# task-2709 보고서 — allowed_resources.paths schema mismatch fix (BOTH layer)

> ★ **최종 상태: IMPLEMENTATION_COMPLETE_BUT_COMPLETION_BLOCKED_BY_INFRASTRUCTURE**
> 코드 구현·검증 100% 완료(RS-1~5 PASS, self-violation 0). 단 finish-task.sh 완료가 **infra defect 2(공유 브랜치 오염, §9/§12 범위 외)**로 차단됨 → `.escalate` 생성, 회장 결정 대기. (task-2708 동일 패턴)

- **task_id**: task-2709
- **team**: dev1-team (헤르메스/Hermes)
- **chair_authorization_id**: `CHAIR-AUTH-TASK-2709-ALLOWED-RESOURCES-PATHS-SCHEMA-FIX-260529`
- **classification**: C BOTH (회장 verbatim 승인 — force-flag 이벤트 기록)
- **검증 레벨**: normal
- **commit**: `50bdce87` (로컬, branch `task/task-2703-v36-harness-dev1`)

---

## S (Situation / 상황)

dispatch 시스템은 task md의 `allowed_resources` YAML 블록을 capability snapshot(`memory/capabilities/{task_id}.json`)으로 박제하고, `scripts/task-scope-guard.sh`가 이를 읽어 변경 파일이 허용 scope 내인지 검증한다. task-2706은 `paths` key를 사용해 정상 동작했으나, task-2707/2708은 `expected_files` / `allowed_existing_file_edits` key만 사용했다.

## C (Complication / 문제)

- task-2707/2708 capability snapshot의 `allowed_resources.paths` = `null`.
- `task-scope-guard.sh` L64는 `paths` key만 read → 모든 변경 파일이 `paths 미포함` false positive로 flag.
- 결과: scope-guard FAIL → finish-task L451 exit 1 → callback registration 미도달 (task-2708 = IMPLEMENTATION_COMPLETE_BUT_COMPLETION_BLOCKED_BY_INFRASTRUCTURE).
- 근본 원인(§2.4): **양 layer cross-key normalization 부재** (snapshot generator + scope-guard 모두). 단일 layer 버그가 아니라 defense-in-depth 부재 → 분류 **C BOTH**.

## Q (Question / 핵심 질문)

양 layer를 backward-compatible하게 정규화하여, 기존 `paths` 패턴(task-2706)은 100% 보존하면서 신규 key 패턴(task-2707/2708)을 자동 정규화할 수 있는가? (수정 ≤ 50 lines, forbidden 파일 무변경, PR/merge 0)

## A (Answer / 해결)

`paths`가 비면(falsy) `expected_files ∪ allowed_existing_file_edits`(순서보존·중복제거)로 채우는 cross-key normalization을 양 layer에 추가. 기존 `paths`가 채워져 있으면 그대로 유지(union 미적용).

### 수정 파일

1. **`dispatch/__init__.py`** (Layer 1 — snapshot generator)
   - 신규 함수 `_normalize_allowed_resources(ar)` 추가 (L939~).
   - `_parse_allowed_resources` yaml 분기(L926) + regex fallback 분기(L933) 양쪽 return에 정규화 적용.
   - regex parser에 `expected_files` / `allowed_existing_file_edits` 보조 추출 추가 (L1026~) — yaml 미가용 환경에서도 cross-key 동작 보장.

2. **`scripts/task-scope-guard.sh`** (Layer 2 — reader)
   - L64 paths reader에 cross-key fallback 추가 (L65~): `paths`가 비면 `expected_files ∪ allowed_existing_file_edits`로 채운다. forbidden_paths 우선순위(L121)는 불변.

3. **`tests/test_allowed_resources_paths_normalization.py`** (신규 회귀 테스트, 8 케이스)

### 변경 규모

- dispatch/__init__.py: 35 lines (insertions 32 / 치환 1 / del 0 net)
- scripts/task-scope-guard.sh: 10 lines (insertions 9 / del 1)
- **합계 42 insertions / 3 deletions = 45 changed (≤ 50 budget 충족)**

---

## 테스트 결과

### 단위 테스트 (RS-1~5) — 8/8 PASS
```
test_rs1_backward_compat_paths_preserved          PASSED  (RS-1 paths 우선)
test_rs1_input_dict_not_mutated                   PASSED  (원본 불변)
test_rs2_forward_compat_union                     PASSED  (RS-2 union 순서보존+중복제거)
test_rs2_parse_allowed_resources_union            PASSED  (yaml 파싱 경로 union)
test_rs3_task2708_replay_no_paths_key             PASSED  (RS-3 paths key 부재)
test_rs4_empty_union_no_paths_added               PASSED  (RS-4 빈 union)
test_rs5_mixed_schema_paths_priority              PASSED  (RS-5 paths 우선)
test_rs5_empty_paths_list_triggers_union          PASSED  (빈 리스트→union)
8 passed in 0.14s
```

### 기존 회귀 — 11/11 PASS (회귀 0)
`tests/test_1921b_team_scope.py` 11 passed.

---

## ★ L1 스모크테스트 결과 (실제 scope-guard 실행)

임시 WORKSPACE로 실제 `bash scripts/task-scope-guard.sh` 실행 (memory/capabilities/**는 forbidden이라 실제 snapshot 미오염):

- **서버 재시작**: 해당없음 (system_hook — 서버 프로세스 무관)
- **API 응답 확인**: 해당없음 (스크립트/모듈 단위 검증)
- **스크린샷**: 해당없음 (CLI/스크립트 작업)
- **실제 스크립트 실행 결과**:
  - **RS-3 task-2708 replay** (paths=null, expected_files만): `[scope-guard] PASS: 2 files in scope`, exit 0 → **수정 전 FAIL이던 핵심 결함(infra defect 1) 회복 확인** ✅
  - **RS-1 baseline** (paths 박힘): `PASS: 1 files in scope`, exit 0 → backward compat 확인 ✅
  - **RS-1b** (paths 박힘 + expected_files에만 있는 파일 변경): `FAIL` exit 1 → paths 우선이라 expected_files 미사용, 과확장 없음 확인 ✅
  - **RS-4 forbidden 우선** (forbidden 파일이 fallback union에도 포함): `FAIL: forbidden_paths 위반` exit 1 → forbidden 우선순위 불변 확인 ✅

→ L1 통과 (실제 스크립트 실행 4건 모두 기대 동작).

---

## 발견 이슈 및 해결

- **pyright import 경고** (`utils.env_loader` 등 미해결): 워크스페이스 경로 미인식 환경 이슈. 런타임 `import dispatch` 정상 동작 확인 → 실제 오류 아님, 조치 불필요.
- 변경 규모 45 lines로 §11 safe-fail 임계(50) 미만 — 회장 재인가 불필요.

---

## 머지 판단

- **머지 필요**: No (★ task forbidden_actions #6/#7: PR/branch push/merge/GitHub write 금지)
- **브랜치**: `task/task-2703-v36-harness-dev1` (로컬 micro-commit `50bdce87`)
- **워크트리**: 미사용 (project_id 없는 system 작업 → DIRECT-WORKFLOW Step 1.5 스킵)
- **머지 의견**: 본 task는 회장 verbatim으로 PR/merge 금지. 로컬 커밋만 수행. 머지/배포는 회장 별도 결정 영역.

---

## 생성/수정 파일 목록

수정:
- `dispatch/__init__.py`
- `scripts/task-scope-guard.sh`

생성:
- `tests/test_allowed_resources_paths_normalization.py`
- `memory/events/task-2709.formalization-commit-260529.json`
- `memory/events/task-2709.callback-envelope.json`
- `memory/reports/task-2709.md` (본 보고서)

---

## Acceptance Criteria 충족 (§7)

1. ✅ 양 layer minimal modification (45 lines ≤ 50)
2. ✅ RS-1~5 모두 PASS (단위 8/8 + scope-guard 실행 4/4)
3. ✅ 기존 harness regression 0 (test_1921b 11/11, task-2706 baseline 보존)
4. ✅ forbidden_files 변경 0건
5. ✅ forbidden_actions 8 위반 0
6. ✅ task-2708 case replay 시 scope-guard PASS (infra defect 1 해소)
7. ✅ GitHub write 0 / PR 0 / branch push 0 / merge 0

---

## 모델 사용 기록

- 불칸(백엔드, sonnet): 양 layer 코드 수정 — 정확한 old/new string 기반 surgical edit
- 아르고스(테스터, sonnet): RS-1~5 회귀 테스트 작성
- 이리스(프론트)/아테나(UX): 미소환 (프론트/디자인 작업 없음)
- 헤르메스(팀장, opus): 설계·통합·L1 스모크·검증·보고
- haiku 미사용

## ★ 완료 차단 (infra defect 2) 및 회장 결정 요청

finish-task.sh 실행 시 scope-guard가 **FAIL** → `.escalate` 생성, `exit 1` (머지·`.done`·callback step 미도달).

- **원인**: finish-task.sh L457이 `git diff main..HEAD`로 scope-diff를 계산. 현 브랜치 `task/task-2703-v36-harness-dev1`에 task-2703 v36 harness 변경 **66파일**이 누적 → scope-guard가 타작업 파일을 flag.
- **위반 58건 전수 분석**: task-2709 내 파일 위반 **0건**. forbidden 위반 5건(`.claude/settings.json`, `finish-task.sh`, `harness/v36/*`, `session-watchdog.sh`)·scope-외 53건은 **전부 task-2703 산물**(제가 미수정).
- **task-2709 범위 내 해결 불가** 이유: finish-task.sh 수정(#2)·수동 `.done`(#4)·수동 callback cron(#5)·`main..HEAD 재구성`(§8) 모두 금지.
- **task-2708 동일 패턴**: §9가 anticipate한 "IMPLEMENTATION_COMPLETE_BUT_COMPLETION_BLOCKED_BY_INFRASTRUCTURE".

### 회장 결정 요청 (택1)
1. infra defect 2(shared branch contamination) 별도 task 발의 → 해소 후 task-2709 finish-task 재실행 (§9: callback registration 정상 회복 가능성).
2. task-2709 fix를 **격리 브랜치/clean base**에서 재 finish-task 실행 인가 (내 4파일만 diff에 포함되도록).
- 어느 쪽이든 **코드 fix 자체는 재작업 불필요** — 이미 검증 완료. 완료 절차(merge/callback)만 infra 해소 후 재실행하면 됨.

- **escalation 상세 마커**: `memory/events/task-2709.escalation-detail-260529.json`
- **scope-violation 증거**: `memory/events/task-2709.scope-violation.json` (58건, self 0)

## ANU Doctrine 준수

- ANU 자체 dispatch 0 / 코드 수정 0 / 분류 결정 0 (회장 verbatim 매핑만) / task-2710 자동 발의 0
- callback cron 등록: finish-task.sh가 ANU key(`c119085addb0f8b7`)로 강제 (self-key 0, sendfile-only 0)
- 디자인 작업 없음 (satori/gemini/hybrid 미호출)

## 세션 통계
- 총 도구 호출: 0회

