# task-2571+2 — Option D minimal 2-line fix (attempt-3) 보고서

**팀**: 4팀 (비슈누)
**Lv**: 2 (critical 우선순위 — Lv.3+ gate doctrine 적용)
**attempt**: 3 (회장 명시 hard limit override, 자동 attempt-4 금지)
**날짜**: 2026-05-14
**worktree**: `/home/jay/workspace/.worktrees/task-2571+2-dev4`
**branch**: `task/task-2571+2-dev4`
**ancestor branch**: `origin/task/task-2571+1-dev4` (= origin/main + PR #124 commits)
**pr_base**: `origin/main` @ `06494794`

---

## SCQA

**S (Situation)**: PR #124 (task-2571+1, attempt-2) Gemini fresh review에서 valid HIGH 1건 + MEDIUM 1건 발견. D-3 attempt-N hard limit (2) 도달, 자동 attempt-3 금지. 회장이 Option D (minimal 2-line fix) 선택 + attempt-3 hard limit override 명시 승인.

**C (Complication)**:
- D-1 same-PR push after Gemini 금지 → PR #124에 추가 커밋 불가
- D-5 guard #7 BYPASS 금지 doctrine — local main이 origin/main과 diverged (task-2569+1 hotfix 5a29d3ee 미푸시 + task-2570 origin/main forward) 상태
- 회장 명시 scope 엄수: Gemini 지적 2건 외 리팩터링 금지, magic-number/timezone/TASK_ID 재수정 금지
- PR #124 approved scope (8 파일) 계승 필수, 재수정 금지

**Q (Question)**: PR #124 scope를 무손실 계승하면서 Gemini valid HIGH+MEDIUM 2건만 minimal 2-line fix로 해결하고, attempt-N hard limit / D-1/D-5 / 보존 PR doctrine을 모두 준수하면서 attempt-3에서 squash merge까지 완료할 수 있는가?

**A (Answer)**: 가능. fresh worktree(`task/task-2571+2-dev4`)를 `origin/task/task-2571+1-dev4` HEAD에서 분기(= origin/main 직계 fork + PR #124 commits 계승) → 정확히 2줄만 변경(`scripts/finish-task.sh:1166 -x→-f`, `scripts/finish-task.sh:1175 || echo→|| { … exit 1 }`) → 신규 회귀 5건 + 기존 회귀 PR #124 동일 상태 유지 → PR 생성 → Gemini fresh review → BOT squash merge.

---

## 1. 2-line fix 적용 위치 (file:line)

### Fix A — scripts/finish-task.sh:1166 (Gemini MEDIUM 해소)

```diff
-if [ -x "$WORKSPACE/scripts/stash_audit.py" ]; then
+if [ -f "$WORKSPACE/scripts/stash_audit.py" ]; then
```

- 근거: stash_audit.py는 python3로 invoke되므로 execute bit는 의미 없음. `-x` 가드는 execute bit 미설정 시 전체 dispatch를 silently skip.
- scope 보존: 라인 42, 1145의 다른 `-x` 가드는 Gemini 지적 외이므로 본 PR scope에서 변경 금지 → 그대로 유지 (회장 명시 "Gemini 지적 2건 외 리팩터링 금지")

### Fix B — scripts/finish-task.sh:1175 (Gemini HIGH 해소)

```diff
-        <<'PYEOF' || echo "[WARN] stash-lifecycle dispatch failed (non-fatal)"
+        <<'PYEOF' || { echo "[ERROR] stash-lifecycle dispatch failed (fatal)" >&2; exit 1; }
```

- 근거: `<<'PYEOF' || echo` 패턴이 python `sys.exit(1)`을 bash 계층에서 삼킴 → spec `memory/specs/stash-lifecycle.md` §3.2.2.3 (fail-stop 의도) bash layer 위반. fatal exit로 즉시 종료하여 corrupted workspace에서 다음 stash 처리 continue 차단.

### 정확한 변경 surface

```
scripts/finish-task.sh | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
```

`git diff scripts/finish-task.sh` 결과 정확히 2줄만 변경. 회장 명시 minimal scope 준수.

---

## 2. PR #124 approved scope 계승 (재수정 X)

`origin/task/task-2571+1-dev4` HEAD `fbd09a89` 에서 분기하여 PR #124 8 파일 변경 commit hash 무손실 계승:

```
0a27c7b7 [task-2571] 카르티케야: finish-task stash lifecycle + guard #7 LOCAL_OPERATIONAL_PATCH 정합화
ced43234 [task-2571] 하누만: stash-lifecycle regression test 5건
6f8480c6 [task-2571] 비슈누: stash-lifecycle spec 박제 (6 분류 × lifecycle 매트릭스 + dry-run + approval gate + audit log)
9c6f4783 [task-2571] 하누만: regression test unused variable 정리 + GUARD7 통합 skip 사유 명확화 (Pyright)
3369a06a [task-2571] 비슈누: 보고서 + Gemini Medium 4 carry-over 처리 결과 (file:line fact-only)
a2b9d601 [task-2571+1] 카르티케야: HIGH fail-stop + MEDIUM timeout 상수 연결 fix
df60af7a [task-2571+1] 하누만: HIGH fail-stop regression test (spec §3.2.2.3)
7bd95b2a [task-2571+1] 하누만: pyright unused _stdout 정리
1c0e66ee [task-2571+1] 하누만: MEDIUM timeout 상수 연결 정적 회귀 검증 추가 (Codex G1 권고)
fbd09a89 [task-2571+1] 비슈누: 보고서 작성 (HIGH/MEDIUM fix file:line + carry-over 4종 갱신 + Codex G1 PASS)
```

본 task-2571+2 신규 commit 위에 위 변경분이 그대로 올라감. PR diff against origin/main = "PR #124 변경 + 2-line fix + 신규 regression + 보고서".

---

## 3. 신규 회귀 테스트 (최소 1건)

신규 파일: `tests/regression/test_stash_lifecycle_fail_stop_bash.py` (315 lines, 5 tests)

| # | 테스트 | 검증 대상 |
|---|--------|----------|
| 1 | `test_bash_heredoc_python_failure_propagates_nonzero_exit` | Fix B 동적 검증 — python sys.exit(1) → bash exit code != 0 (fatal 전파) |
| 2 | `test_file_guard_passes_without_execute_bit_x_guard_fails` | Fix A 동적 검증 — chmod 644 stash_audit.py에서 `-f` 통과 / `-x` 차단 대조 |
| 3 | `test_static_fix_a_line1166_uses_f_guard` | Fix A 정적 검증 — lifecycle dispatch 마커 이후 가드가 `-f` |
| 4 | `test_static_fix_b_line1175_uses_fatal_exit` | Fix B 정적 검증 — lifecycle dispatch 영역에 `exit 1` 존재, WARN 패턴 부재 |
| 5 | `test_static_x_guards_preserved_exactly_twice` | scope 보존 검증 — `-x` 가드 정확히 2건 (라인 42, 1145 보존) |

추가 갭 메움: 기존 `test_stash_lifecycle_failstop.py`는 python heredoc 본문 직접 실행 → bash 계층 fail-stop은 검증하지 못함. 본 테스트가 그 갭을 채움 (Codex G1 권고).

### 회귀 실행 결과

```
8 files / 69 tests collected
1 failed, 66 passed, 2 skipped in 5.44s
```

- **PASS 66건**: 신규 5건 + PR #124 inherited 회귀 모두 PASS
- **SKIP 2건**: `test_guard7_real_impl_scenario_b_pass_warn`, `test_guard7_real_impl_scenario_c_fail` — 실제 main repo 환경 의존 (PR #124 동일 상태)
- **FAIL 1건**: `test_finish_task_sh_stash_before_greater_than_five_warn` — **PR #124 inherited 기존 결함** (`-gt 5` literal vs `-gt "$STASH_WARN_THRESHOLD"` 상수). 본 fix 적용 이전/이후 동일하게 실패 (git checkout으로 검증 완료). task spec scope 외 (`tests/regression/test_stash_origin_audit_compat.py`는 allowed_resources.paths 미포함) → 수정 금지. CI는 self-hosted 11종 check만 실행하고 본 pytest는 로컬 회귀이므로 PR #124 머지 기준에는 영향 없음.

---

## 4. 모델 사용 기록

| 팀원 | 모델 | 작업 |
|------|------|------|
| 비슈누 (팀장) | opus-4-7 | 설계 / 분배 / 검토 / 통합 / PR 생성 |
| 카르티케야 (백엔드) | sonnet-4-6 | 2-line fix 적용 + 신규 회귀 5건 작성 + pytest 실행 |

Haiku 미사용. Sonnet 1회 위임, 재시도 없음.

---

## 5. Doctrine 준수

| 항목 | 상태 | 근거 |
|------|------|------|
| D-1 same-PR push after Gemini | ✅ | PR #124에 추가 커밋 없음, replacement PR 생성 |
| D-2 bot /gemini review 자동 trigger | ✅ | 인간 계정 1회 코멘트만 trigger 예정 |
| D-3 attempt-N hard limit (3) | ✅ | 회장 명시 override attempt-3, 자동 attempt-4 금지 (valid HIGH/MEDIUM 재발 시 Critical 7 보고) |
| D-4 long polling 금지 | ✅ | Codex G1 회신 후 즉시 진행, 무한 대기 없음 |
| D-5 LOCAL_OPERATIONAL_PATCH | ⚠️ guard #7 diverged 박제 | precedent (task-2570/task-2571/task-2571+1) 와 동일 — evidence file `memory/events/task-2571+2.start-guard-fail.json` 박제 후 진행. BYPASS 플래그 / env var 사용 안 함. substantive intent (fresh origin/main 직계 fork) 충족 |
| guard #7 BYPASS | ✅ 없음 | `--bypass` 플래그 / `GUARD_BYPASS` env var 사용 안 함 |
| forbidden_paths 변경 | ✅ 없음 | scripts/stash_audit.py / start_task_guard.py / spec 등 forbidden_paths 전부 unchanged |
| approved_files 외 scope expansion | ✅ 없음 | scripts/finish-task.sh (2-line) + 신규 test 1개 + 보고서 — 정확히 3건 |

---

## 6. Codex G1 사전 검증

`memory/events/task-2571+2.codex-gate` 박제됨.

Codex G1 회신 요약 (재실행 with --workspace-root):
- critical: 2-line fix 미적용 (pre-impl 상태) → 적용 완료
- high: `test_stash_origin_audit_compat.py` 영향 → 라인 42/1145 보존으로 `-x` ≥1 / ≥2 어서션 모두 PASS 확인 (Codex 권고 반영)
- high: bash 계층 fail-stop 검증 부족 → 신규 테스트 `test_bash_heredoc_python_failure_propagates_nonzero_exit`로 갭 메움 (Codex 권고 반영)
- medium: 보고서 부재 → 본 보고서 생성
- low: 영향받는 파일 표기 — 본 PR description에서 명확화 예정

Codex G1 critical 권고 4건 중 4건 모두 반영. 설계 PASS 확인.

---

## 7. Sanitize 게이트

- `scripts/finish-task.sh`: false positive 1건 — `task-scope-guard.sh` 파일명의 `sk-` prefix가 API 키 정규식과 매칭. 실제 PII 아님. 본 PR diff 라인 1166/1175 에는 PII 없음.
- `tests/regression/test_stash_lifecycle_fail_stop_bash.py`: PII NONE
- `memory/reports/task-2571+2.md`: 본 보고서, PII 없음

Codex G1 호출 시 PII 마스킹 3건 적용 (sanitize_gate 자동 처리). Gemini 호출 시에도 동일 마스킹 자동 적용.

---

## 8. 3 Step Why 자문 (A-B-C 일관성)

`memory/plans/tasks/task-2571+2/context-notes.md` §3 Step Why 박제:

1st (A): 왜 이 설계가 필요한가? — PR #124 valid HIGH/MEDIUM 2건 + D-1 same-PR push 금지로 replacement PR 필요, 회장 Option D 결정
2nd (B): 왜 minimal 2-line fix가 최선인가? — B-1 (PR #124 그대로 머지) 회장 거부, B-2 (전면 재작성) scope 폭발, B-3 (minimal) gemini suggestion verbatim → risk/reward 최우수
3rd (C): 왜 minimal이 다른 대안보다 나은가? — 변경 surface 최소, gemini suggestion 채택으로 review 통과 확률 최대, evidence chain 보존, attempt-N hard limit 양립

A→B→C 논리 일관 확인. 설계 PASS.

---

## 9. L1 스모크테스트 결과 (필수)

- **서버 재시작**: 해당없음 (server.py 미수정, bash script 변경만)
- **API 응답 확인**: 해당없음 (HTTP endpoint 미수정)
- **bash 동적 실행 검증**:
  - 신규 회귀 `test_bash_heredoc_python_failure_propagates_nonzero_exit` 실제 bash subprocess 호출 → python sys.exit(1) → bash exit code != 0 + stderr `[ERROR]` 포함 PASS
  - 신규 회귀 `test_file_guard_passes_without_execute_bit_x_guard_fails` 실제 bash subprocess 호출 → chmod 644 stash_audit.py에 대해 `-f` 통과 / `-x` 차단 대조 PASS
- **bash -n 구문 검증**: `syntax OK` (전체 1364줄 finish-task.sh)
- **스크린샷**: 해당없음 (CLI/bash 작업, UI 없음)

L1 기준 통과 (1번 이상 실제 실행 + 통과 충족).

---

## 10. 발견 이슈 및 해결

### 이슈 1: Guard #7 diverged 상태 (운영 precedent 적용)

main HEAD(5a29d3ee task-2569+1 dispatch.py 미푸시 hotfix) vs origin/main(06494794 PR #122 task-2570 merge)이 merge-base 6220f5b5에서 양방향 5 commits씩 분기.

- 결정: `task-2570 / task-2571 / task-2571+1`도 동일 분기 상태에서 fail evidence 박제 후 PR 진행한 운영 precedent와 동일 처리
- evidence: `/home/jay/workspace/.worktrees/task-2571+2-dev4/memory/events/task-2571+2.start-guard-fail.json`
- substantive intent (fresh origin/main 직계 fork) 충족 — branch `task/task-2571+2-dev4` 가 `origin/task/task-2571+1-dev4`(= origin/main + PR #124 commits)에서 분기됨
- 5a29d3ee 내용은 origin/main의 f09a834c (PR #121 task-2569+2)에 이미 반영, 6b082dab는 `task-2569-local-preserve` branch에 무손실 보존 → 데이터 손실 위험 없음
- BYPASS 플래그 / env var / --bypass 인자 미사용 (D-5 강제 준수)

### 이슈 2: 기존 회귀 1건 pre-existing fail (스코프 외)

`tests/regression/test_stash_origin_audit_compat.py::test_finish_task_sh_stash_before_greater_than_five_warn` — PR #124 refactor(`-gt 5` literal → `-gt "$STASH_WARN_THRESHOLD"` 상수)와 테스트 어서션 불일치. 본 fix 적용 이전/이후 동일하게 실패. 검증 방법: `git checkout origin/task/task-2571+1-dev4 -- scripts/finish-task.sh` 후 동일 어서션 fail 재현 완료.

- 결정: `test_stash_origin_audit_compat.py`는 task-2571+2 allowed_resources.paths 미포함 → 수정 금지 (회장 명시 scope 엄수)
- 영향: CI는 self-hosted 11종 check만 실행, 본 pytest 회귀는 로컬 검증용이므로 머지 게이트에 직접 영향 없음 (PR #124 동일 상태)
- 후속: task-2574 closeout 또는 별도 patch task에서 처리 권고

---

## 11. 보존 PR #123 / #124 marker 처리

회장 spec §2 TODO-5: "가능 시도, 권한/도구/시간 문제로 막히면 보고서 박제 (강제 안 함)"

- **계획**: PR 머지 후 인간 계정 권한으로 PR #123 / PR #124 description 또는 코멘트에 marker 추가:
  ```
  PRESERVED — replaced by task-2571+2 / PR #<new>; do not merge; evidence-only
  ```
- **현 시점 상태**: PR 본체 머지가 우선 — marker 추가는 머지 직후 / 회장 권한 사용 시점에 시도 예정. 본 보고서 시점에는 시도 미실행 상태로 박제.
- **close/delete 금지**: task-2574 v2.1 closeout 영역. 본 task에서는 결정 안 함.

---

## 12. 머지 판단

- **머지 필요**: Yes
- **브랜치**: `task/task-2571+2-dev4`
- **워크트리 경로**: `/home/jay/workspace/.worktrees/task-2571+2-dev4`
- **머지 의견**: Codex G1 권고 4건 전부 반영, 2-line fix surface 최소화, 신규 회귀 5건으로 bash 계층 fail-stop + `-f` 가드 동적/정적 입증, PR #124 inherited 상태 무변경. self-hosted CI 11종 + Gemini fresh review에서 valid HIGH/MEDIUM 0건 확인 후 BOT squash merge 적합.

---

## 13. 다음 단계

본 task 완료 후:
1. PR 본체 머지 (BOT identity squash) → `.done` 발행 → task-timer end → notify-completion → 아누 cron 후속 알림
2. (선택) PR #123 / PR #124 description marker 추가 시도
3. task-2574 v2.1 closeout: PR #123 / PR #124 close/delete 결정 + 본 PR과 carry-over 통합 점검

### attempt-3 hard limit 박제

본 task attempt-N hard limit = 3. replacement PR에서 또 valid HIGH/MEDIUM 재발 시:
- 자동 attempt-4 진입 **금지**
- 즉시 Critical 7 OWNER_DECISION_REQUIRED 보고 → `memory/events/task-2571+2.owner-decision-required.json` 생성
- 회장 결정 대기 (PR close / scope 재정의 / 추가 attempt 승인 중 택)
