# Lock-in 1 — Kill-switch First-line 검증

**timestamp**: 2026-05-04T02:11:00+09:00
**원칙**: cancelled 검사 + guard 실행은 항상 '모든 로직의 첫 줄'에서 실행. 한 줄이라도 앞에 있으면 FAIL.

## 매트릭스

| # | 파일 | 위치 | 가드 위치 | 가드 앞 statement |
|---|---|---|---|---|
| 1 | `scripts/anu_confirm_bot/main.py` | `_execute_approve()` 함수 본문 | docstring 직후 첫 실행 statement | **0줄** |
| 2 | `scripts/finish-task.sh` | `else` 블록 (merge 단계) | 진입 첫 줄 (주석 제외) | **0줄** (실행 statement) |
| 3 | `scripts/auto_merge.py` | `execute_merge()` 함수 본문 | docstring 직후 첫 실행 statement | **0줄** |

## grep 증거

### #1 anu_confirm_bot/main.py:_execute_approve

```
$ sed -n '107,123p' scripts/anu_confirm_bot/main.py
def _execute_approve(task_num: int, pr_num: int) -> dict:
    """gh pr merge 호출. GitHub API가 직렬화 보장.

    Lock-in 1 First-line: cancelled 검사 + guard.sh 강제 실행이 함수 첫 statement.
    Lock-in 2 Hard stop: 가드 실패 시 gh pr merge subprocess 진입 0회 보장.
    """
    _cancelled_marker = Path(config.WORKSPACE_ROOT) / "memory" / "events" / f"task-{task_num}.cancelled"
    if _cancelled_marker.exists():
        return {"ok": False, ...}
    _guard_sh = Path(config.WORKSPACE_ROOT) / "scripts" / "guard.sh"
    _task_id = f"task-{task_num}"
    for _stage in ("pre-push", "qc-check"):
        _gp = subprocess.run(["bash", str(_guard_sh), _stage, _task_id], ...)
        if _gp.returncode != 0:
            return {"ok": False, ..., "blocked_by": f"guard.sh.{_stage}"}
    cmd = ["gh", "pr", "merge", ...]
```

→ 함수 진입 후 docstring → cancelled 변수 unpack(허용) → cancelled 검사(가드) → guard.sh 호출 (가드). 가드 앞 실행 statement 0줄.

### #2 finish-task.sh else 블록

```
$ sed -n '428,447p' scripts/finish-task.sh
if [ -f "$MERGE_DONE_FILE" ]; then
    echo "[INFO] .merge-done 이미 존재 — 머지 단계 스킵."
elif [ -z "$PROJECT_PATH" ]; then
    echo "[INFO] PROJECT_PATH 미지정 — 머지 단계 스킵 (시스템 작업)."
else
    # ── Lock-in 1 First-line: cancelled + guard.sh가 else 블록 첫 statement ──
    if [[ -f "$EVENTS_DIR/${TASK_ID}.cancelled" ]]; then
        echo "[CANCELLED] merge step blocked — task $TASK_ID cancelled (.cancelled 마커 존재)" >&2
        exit 1
    fi
    if ! bash "$WORKSPACE/scripts/guard.sh" pre-push "$TASK_ID"; then
        echo "[GUARD-FAIL] pre-push guard 실패 — merge 차단 ($TASK_ID)" >&2
        exit 1
    fi
    if ! bash "$WORKSPACE/scripts/guard.sh" qc-check "$TASK_ID"; then
        echo "[GUARD-FAIL] qc-check guard 실패 — merge 차단 ($TASK_ID)" >&2
        exit 1
    fi
    echo "[INFO] 머지 실행: ..."
```

→ `else` 블록 진입 후 주석(허용) → cancelled 검사 → guard.sh pre-push → guard.sh qc-check. 가드 앞 실행 statement 0줄.

### #3 auto_merge.py:execute_merge

```
$ sed -n '260,278p' scripts/auto_merge.py
    def execute_merge(self, project_path: str, task_id: str, team_id: str) -> dict[str, Any]:
        """worktree_manager.py finish --action merge 실행.

        Lock-in 1 First-line: cancelled 검사 + guard.sh가 함수 첫 statement.
        Lock-in 2 Hard stop: 가드 실패 시 worktree_manager finish subprocess 진입 0회 보장.

        Raises:
            RuntimeError: 머지 실패 (충돌 등) 또는 cancelled/guard 차단.
        """
        _cancelled_marker = self.workspace / "memory" / "events" / f"{task_id}.cancelled"
        if _cancelled_marker.exists():
            raise RuntimeError(f"cancelled — merge blocked [{task_id}] (.cancelled 마커 존재)")
        _guard_sh = self.workspace / "scripts" / "guard.sh"
        _gp = subprocess.run(["bash", str(_guard_sh), "pre-push", task_id], ...)
        if _gp.returncode != 0:
            raise RuntimeError(f"guard.sh pre-push FAIL [{task_id}]: ...")
        worktree_manager = Path(__file__).parent / "worktree_manager.py"
```

→ 함수 진입 후 docstring → cancelled 변수 unpack(허용) → cancelled 검사 → guard.sh 호출. 가드 앞 실행 statement 0줄.

## 합격 판정

3 곳 모두 가드 앞 실행 statement 0줄 = **Lock-in 1 PASS**

허용된 항목: docstring, 주석, 변수 unpack (`_cancelled_marker = ...`).
불허된 항목 (모두 부재 확인): 로깅, audit_log 기록, 시간 측정, 마커 생성, 그 외 모든 실행 statement.
