# All Stop 복구 + 블로그 글작성 subprocess 전환

## ★★★ worktree 금지, main 직접 작업, 즉시 커밋 ★★★

## 수정 1: 시스템뷰 All Stop 버튼 + 확인 팝업 복구

### 문제
시스템뷰에 있던 All Stop 비상 정지 버튼이 사라짐.
이전에 구현했지만 worktree 머지로 덮어쓰여짐.

### 확인 필요
- 기존 task-1810 보고서 참고: `/home/jay/workspace/memory/reports/task-1810.1.md`
- All Stop API가 server.py에 남아있는지 확인: `grep "all-stop\|allstop\|all_stop" server.py`
- 프론트엔드(App.js 또는 시스템뷰 컴포넌트)에서 버튼 코드 확인

### 구현 내용
**UI (시스템뷰 하단)**:
- 빨간 "All Stop" 버튼
- 클릭 시 확인 팝업: "정말 모든 작업을 중지하시겠습니까?" (confirm 또는 모달)
- 확인 후 POST /api/all-stop 호출

**API (server.py)**:
- POST /api/all-stop
- 모든 봇 프로세스 kill (EXCLUDE_PATTERNS: dashboard/server.py, systemd, grep 제외)
- 모든 독립 subprocess kill (정제, 글작성 등)
  - refine-lock.json의 PID kill
  - blog-generate-lock.json의 PID kill (수정 2에서 추가)
- 응답: `{"status": "ok", "killed": N}`

**★★★ 절대 규칙**: All Stop API를 curl로 테스트하지 않는다. 코드 리뷰만으로 검증.

## 수정 2: 블로그 글작성 threading → subprocess 전환

### 문제
현재 글작성이 `threading.Thread(daemon=True)`로 실행됨.
서버 재시작(auto_reload 포함) 시 글 작성 중에 강제 종료됨.

### 수정
`_background_blog_generate` 함수를 별도 Python 스크립트로 분리하고,
서버에서 `subprocess.Popen`으로 실행.

**방법 A (권장 — 최소 변경)**:
기존 `_background_blog_generate` 함수는 그대로 두고,
서버의 글작성 POST 핸들러에서 subprocess로 래핑:

```python
# 기존
t = threading.Thread(target=_background_blog_generate, args=(...), daemon=True)
t.start()

# 변경
cmd = [
    sys.executable, "-c",
    f"import sys; sys.path.insert(0, '{Path(__file__).parent}'); "
    f"from server import _background_blog_generate; "
    f"_background_blog_generate({repr(keywords)}, {repr(additional_content)}, "
    f"{repr(tone)}, {repr(model)}, {repr(str(status_path))}, {repr(str(lock_path))})"
]
proc = subprocess.Popen(cmd, ...)
```

**방법 B (깔끔 — 별도 스크립트)**:
`/home/jay/workspace/dashboard/scripts/blog_generate.py` 생성
- CLI: `python3 blog_generate.py --keywords "..." --tone "..." --model "..." --status-path "..." --lock-path "..."`
- 서버에서 `subprocess.Popen(["python3", "scripts/blog_generate.py", ...])` 호출

방법 B 권장 — 정제(kakao_knowledge)와 동일한 패턴.

### lock 파일
- 기존 status_path/lock_path 패턴 유지
- All Stop에서 lock 파일의 PID를 읽어 kill

## 수정 3: All Stop에서 독립 프로세스 kill 추가

All Stop 핸들러에서:
1. 봇 프로세스 kill (기존)
2. refine-lock.json PID kill (정제 프로세스)
3. blog-generate-lock.json PID kill (글작성 프로세스 — 수정 2에서 추가)

```python
# lock 파일 기반 프로세스 kill
for lock_file in ["refine-lock.json", "blog-generate-lock.json"]:
    lock_path = data_dir / lock_file
    if lock_path.exists():
        lock_data = json.loads(lock_path.read_text())
        pid = lock_data.get("pid")
        if pid and _is_process_alive(pid):
            os.kill(pid, signal.SIGTERM)
```

## 검증 시나리오
1. **All Stop 버튼**: 시스템뷰에 빨간 버튼 보임
2. **확인 팝업**: 버튼 클릭 → "정말 모든 작업을 중지하시겠습니까?" 표시
3. **글작성 독립**: 글작성 시작 후 서버 재시작 → 글작성 계속 진행
4. **All Stop + 독립 프로세스**: All Stop 실행 → 정제/글작성 프로세스도 종료
5. **★ curl 테스트 금지** — All Stop API는 코드 리뷰만으로 검증
