Context
PR #639 의 `EditorialArticleState.regen_ig_slides: Optional[set[int]]` 필드는 정의했지만 실제 사용 안 됨. 현재 RegenIgCarouselService 는 항상 6장 전체 재생성.
`packages/ai-server/src/editorial_article/state.py`
```python
일부 슬라이드만 재생성할 때 (admin Regen subset). None 이면 전체 (1..6)
새로. 안 비어있으면 그 인덱스만 R2 교체 + ig_slides_urls 부분 갱신.
regen_ig_slides: Optional[set[int]]
```
`RegenIgCarouselService.regen_ig_carousel_job` 의 `slides: str` 인자도 future-proof 로만 정의 (`carousel.py:1937-1939` 의 `if 1 in slides` 류 로직은 `_run` 가 이미 지원).
Proposed approach
- ai-server side:
- regen service 가 `slides` 인자를 parse 해서 `state.regen_ig_slides` 에 전달
- `generate_ig_carousel_node` 가 `state.regen_ig_slides` 가 있으면 그 인덱스만 새로 생성 + 기존 ig_slides_urls 와 머지 (DB load → 부분 교체)
- admin UI side:
- IgCarouselPreview 의 Regenerate 버튼에 modal 추가 — slides multiselect (전체 또는 일부) + 선택 후 `useRegenIgCarousel.mutate({id, slides: "1,3,5"})`
- gRPC RegenIgCarouselRequest 의 `slides` 필드는 이미 wiring 됨
Acceptance
- admin 에서 "슬라이드 1, 3 만 regen" 트리거 가능
- 나머지 슬라이드 (2, 4, 5, 6) 의 R2 URL 변경 없음
- caption.txt 도 새로 빌드 (brand 멘션 변경될 수 있어서)
References: PR #639 ("Part B — IG carousel")
Context
PR #639 의 `EditorialArticleState.regen_ig_slides: Optional[set[int]]` 필드는 정의했지만 실제 사용 안 됨. 현재 RegenIgCarouselService 는 항상 6장 전체 재생성.
`packages/ai-server/src/editorial_article/state.py`
```python
일부 슬라이드만 재생성할 때 (admin Regen subset). None 이면 전체 (1..6)
새로. 안 비어있으면 그 인덱스만 R2 교체 + ig_slides_urls 부분 갱신.
regen_ig_slides: Optional[set[int]]
```
`RegenIgCarouselService.regen_ig_carousel_job` 의 `slides: str` 인자도 future-proof 로만 정의 (`carousel.py:1937-1939` 의 `if 1 in slides` 류 로직은 `_run` 가 이미 지원).
Proposed approach
Acceptance
References: PR #639 ("Part B — IG carousel")