# task-679: 5단계 파이프라인 text_insight 무한루프 + 빈 문자열 원인 분석

## 작업 레벨: Lv.2 (원인분석 + 수정)

## 증상
1. **text_insight 콘텐츠 생성 시 빈 문자열 반환**: `FiveStagePipeline().generate(topic, content_type="text_insight")` 실행하면 `{"text": "", ...}` 반환. review_score는 53~59로 정상이나 text가 빈 문자열.
2. **무한루프/장시간 행**: 파이프라인이 5단계 Claude CLI 호출 중 특정 단계에서 멈추거나 무한 반복. 1팀장(Opus)도 동일 증상 경험.
3. **카드뉴스(cardnews)는 정상 작동**: 동일 파이프라인에서 content_type="cardnews"는 slides가 정상 반환됨.

## 이미 수정한 것 (효과 없음)
`five_stage_pipeline.py` line 165-178에서:
- line 168: `hooking_output.get("updated_content", "")` → `hooking_output.get("updated_content")` (빈 문자열 기본값 제거)
- line 173: `isinstance(final_content, str)` → `isinstance(final_content, str) and final_content` (빈 문자열 거부)

→ 이 수정 후에도 text가 빈 문자열. 즉 `writing_output.get("text", "")`도 빈 문자열이라는 뜻.

## 분석 방향

### 가설 1: writing 단계가 text 키를 안 넣음
- `03_writing.md` 프롬프트는 text_* 타입에 `"text": "본문..."` JSON을 요청
- 하지만 Claude Sonnet이 다른 키(예: `"thread"`, `"content"`, `"body"`)로 반환할 가능성
- 확인: writing 단계 출력을 직접 캡처하여 키 목록 확인

### 가설 2: JSON 파싱 문제
- `_parse_json_response()`의 regex `r"```(?:json)?\s*(\{.*?\})\s*```"` — non-greedy `.*?`가 중첩 JSON에서 내부 객체만 캡처
- text_* 출력이 단순(`{"text":"...","hashtags":[]}`)이면 문제 없지만, 중첩 구조면 파싱 실패
- 확인: writing/hooking/review 각 단계의 raw Claude 응답을 캡처

### 가설 3: 무한루프 원인
- `MAX_FULL_RETRIES` 재시도 루프에서 review_score 파싱 실패(-1) → 무한 continue
- 또는 `_run_stage`의 `MAX_STAGE_RETRIES`에서 반복 실패
- 확인: retry 횟수, timeout 설정, Claude CLI 응답 시간

### 가설 4: _call_claude 호출 방식 차이
- text_* vs cardnews에서 프롬프트 구성이 다를 수 있음 (system/user 분리 방식)
- 확인: `pipeline_prompts.py`에서 text_insight 프롬프트 구성 확인

## 분석 대상 파일

1. `/home/jay/projects/ThreadAuto/content/five_stage_pipeline.py` — 메인 파이프라인
2. `/home/jay/projects/ThreadAuto/content/pipeline_prompts.py` — 프롬프트 로더
3. `/home/jay/projects/ThreadAuto/prompts/pipeline/03_writing.md` — writing 프롬프트
4. `/home/jay/projects/ThreadAuto/prompts/pipeline/04_hooking.md` — hooking 프롬프트
5. `/home/jay/projects/ThreadAuto/prompts/pipeline/05_review.md` — review 프롬프트

## 요구 사항

1. **각 단계별 raw 출력 캡처**: writing/hooking/review 단계의 Claude 응답을 파일로 저장하여 실제 JSON 구조 확인
2. **text_insight vs cardnews 차이점 규명**: 왜 cardnews만 정상인지 구조적 원인 분석
3. **버그 수정**: 원인 파악 후 수정. 수정 후 text_insight 파이프라인이 빈 문자열이 아닌 실제 텍스트를 반환하는지 검증
4. **무한루프 방지**: retry 로직에 안전장치 추가 (이미 있는지 확인)
5. **테스트**: 수정 후 text_insight 파이프라인 실행하여 실제 텍스트 생성 확인

## 참고
- 카드뉴스 정상 결과 예시: `/home/jay/projects/ThreadAuto/output/task677_result.json`
- 텍스트 실패 결과 예시: `/home/jay/projects/ThreadAuto/output/text_content_task678_v2.json`
- 이미 적용된 수정: `five_stage_pipeline.py` line 168, 173 (위 참조)