# TheRoad `architecture-refactor` 타당성 검토

작성일: 2026-04-17  
작성 범위: `docs/architecture-refactor/*`, `docs/architecture/*`, 현재 실제 코드(`backend/`, `frontend/`)  
수정 범위: 이 문서는 `docs/architecture-refactor-codex`에만 신규 작성

---

## 결론

`docs/architecture-refactor`의 **문제 인식 방향은 대체로 타당**하다. 특히 아래 4가지는 실제 코드와 강하게 부합한다.

1. `api/v1/steps.py`에 제어, 동기화, 스냅샷, 파일 I/O, step orchestration이 과하게 몰려 있다.
2. `_sync_checkpoints_to_db()`는 분해가 필요한 수준의 거대 함수다.
3. `ImageService`는 사실상 여러 서비스의 합체다.
4. manifest, step registry, runtime applicability, UI 표기가 하나의 계약으로 묶여 있지 않다.

다만 **초안 그대로 구현에 들어가면 위험하다**. 가장 큰 이유는 아래다.

1. 현재 시스템은 이미 **두 개의 실행 축**을 동시에 운영한다.  
   `episodes.py`는 `AnalysisService`를 사용하고, `steps.py`는 `StepRunner` 기반 파이프라인을 사용한다.
2. 초안의 핵심 원칙인 **"DB Primary, 체크포인트 Backup"** 은 방향으로는 맞지만, 현재 실행 엔진은 아직 **체크포인트 우선 생성 후 DB projection** 구조다.
3. **"Step은 Pure Transformation"** 은 일부 분석 step에는 맞지만, 이미지 step, `set_design`, `t2i_review`에는 그대로 적용하기 어렵다.
4. 문서의 일부 baseline 수치와 전제는 이미 현재 코드와 어긋난다. 이 상태로 일정을 잡으면 Phase 난이도 산정이 틀어진다.

따라서 이 리팩토링은 **승인 가능하지만, “문서 수정 후 착수”가 맞다.**  
추천 판단은 다음과 같다.

- 방향성: 승인
- 우선순위: 높음
- 실행 방식: 초안 그대로 진행 금지
- 권장 형태: `Phase 0(현행 계약 정리) → Phase 1(카탈로그/적용성/무효화 통합) → Phase 2(sync 분해) → Phase 3(image 경계 정리) → Phase 4(legacy 분석 경로 수렴) → Phase 5(frontend 서버 상태 재구성)`

---

## 조사 기준

검토에 사용한 핵심 근거:

- 현재 구조 문서
  - `docs/architecture/00-overview.md`
  - `docs/architecture/04-scene-direction.md`
  - `docs/architecture/05-image-generation.md`
  - `docs/architecture/06-data-contracts.md`
- 리팩토링 초안
  - `docs/architecture-refactor/00-analysis.md`
  - `docs/architecture-refactor/01-target-design.md`
  - `docs/architecture-refactor/02-before-after.md`
  - `docs/architecture-refactor/03-roadmap.md`
  - `docs/architecture-refactor/04-next-session-brief.md`
- 실제 코드
  - `backend/app/api/v1/steps.py`
  - `backend/app/core/step_runner.py`
  - `backend/app/core/step_manifest.py`
  - `backend/app/core/steps/__init__.py`
  - `backend/app/core/steps/image_steps.py`
  - `backend/app/core/steps/set_design_step.py`
  - `backend/app/api/v1/episodes.py`
  - `backend/app/services/analysis_service.py`
  - `backend/app/services/image_service.py`
  - `frontend/package.json`
  - `frontend/src/App.tsx`
  - `frontend/src/pages/EpisodeDetail.tsx`
  - `frontend/src/components/shared/PipelineStepsPanel.tsx`
  - `backend/tests/test_step_manifest_v3.py`

---

## 현재 구조의 핵심 사실

### 1. 실제 런타임은 단일 파이프라인이 아니다

```mermaid
flowchart LR
    A["POST /episodes/{id}/analyze"] --> B["AnalysisService"]
    B --> C[(DB)]
    B --> D["legacy checkpoint/json 흐름"]

    E["POST /steps/run-all<br/>POST /steps/{step_id}"] --> F["StepRunner"]
    F --> G["episode checkpoints"]
    G --> H["_sync_checkpoints_to_db()"]
    H --> C

    I["image_steps.py"] --> J["ImageService"]
    I --> H
```

근거:

- `backend/app/api/v1/episodes.py:125-201` 는 여전히 `AnalysisService.run_analysis()`를 백그라운드로 호출한다.
- `backend/app/services/analysis_service.py:894-1024` 는 step manifest가 아니라 자체 오케스트레이션으로 분석을 수행한다.
- `backend/app/api/v1/steps.py:161-260`, `602-688` 는 별도로 `StepRunner` 기반 실행 경로를 유지한다.
- `backend/app/core/steps/image_steps.py:51-59` 는 이미지 step이 API 내부 함수 `_sync_checkpoints_to_db` 를 직접 import 해서 호출한다.

이 사실이 의미하는 바:

- 리팩토링 문서는 "현재는 StepRunner 기반 파이프라인"으로 단순화하면 안 된다.
- 먼저 **어느 진입점이 정식 경로인지**를 정해야 한다.
- 그렇지 않으면 Service 분해를 해도 `AnalysisService` 와 `StepRunner` 둘 다 유지되는 이중 구조가 남는다.

---

## 리팩토링 초안에서 타당한 부분

## 1. `_sync_checkpoints_to_db` 분해는 반드시 필요하다

판정: **타당**

근거:

- `backend/app/api/v1/steps.py:768-1487` 전체가 한 함수다.
- 한 함수가 엔티티 sync, relation sync, scene_still sync, shot_type sync, outlook sync, orphan cleanup, t2i count 계산, episode status 갱신까지 담당한다.
- 호출 지점도 `run_all`, `run_step`, `restore_snapshot`, 이미지 step 사전 sync 등으로 여러 의미가 섞여 있다.

초안 보강점:

- `CheckpointSyncService` 하나로 끝내지 말고, 최소한 아래 단위로 쪼개는 설계가 필요하다.
  - `EntitySyncService`
  - `RelationSyncService`
  - `SceneStillSyncService`
  - `OutlookSyncService`
  - `EpisodeProjectionService`
- 이유는 호출 목적이 다르기 때문이다.
  - `scene_director` 전 short-id 보장
  - step 완료 후 projection
  - snapshot restore 후 projection
  - image step 진입 전 projection

즉, 초안의 "한 service로 옮긴다"는 방향은 맞지만, **도메인 단위 메서드 분해와 호출 목적 분리**까지 문서화해야 한다.

## 2. manifest 기반 invalidation 전환은 맞다

판정: **타당**

근거:

- `StepRunner.invalidate_downstream()` 는 이미 `get_all_downstream_recursive()` 를 사용한다 (`backend/app/core/step_runner.py:159-181`).
- 그런데 `toggle_shot_selection()` 는 여전히 하드코딩 리스트를 사용한다 (`backend/app/api/v1/steps.py:356-358`).
- 같은 함수 안에서 `_resume_sensitive` 도 하드코딩이다 (`backend/app/api/v1/steps.py:383-390`).
- 이 하드코딩은 현재 manifest와도 완전히 일치하지 않는다. 예를 들어 `composite_image_gen` 은 `shot_selection` 의 downstream이 아니다 (`backend/app/core/step_manifest.py:490-527`).

초안 보강점:

- downstream 계산은 manifest 기반으로 바꾸는 것이 맞다.
- 다만 `_resume_sensitive` 는 단순 downstream과 다르므로 별도 메타가 필요하다.
- 추천 메타:
  - `invalidates_on_upstream_change: true`
  - `checkpoint_can_be_marked_stale_in_place: true`
  - `resume_sensitive: true`

즉, **"모두 recursive downstream으로 통일"이 아니라 "recursive downstream + step 속성 메타"** 가 맞다.

## 3. Applicability 중앙화도 맞다

판정: **타당**

근거:

- `StepRunner.check_applicability()` 는 현재 사실상 `always/on_demand/disabled` 만 처리한다 (`backend/app/core/step_runner.py:82-92`).
- manifest에는 이미 `if_planning_doc`, `if_has_outlooks` 가 존재한다 (`backend/app/core/step_manifest.py:26`, `498`).
- `run_all_steps()` 는 `if_planning_doc` 만 별도 처리하고 `if_has_outlooks` 는 고려하지 않는다 (`backend/app/api/v1/steps.py:191-204`).
- `get_all_steps()` 도 applicability를 실행 판단에 반영하지 않고 단순 문자열만 내려준다 (`backend/app/api/v1/steps.py:79-126`).
- `set_design` 는 manifest에서는 `always`인데, 실제 step 내부에서는 `SET_DESIGN_ENABLED=false` 면 no-op 이다 (`backend/app/core/steps/set_design_step.py:38-49`).

초안 보강점:

- `ApplicabilityValidators` 는 `StepRunner` 에만 넣으면 부족하다.
- 최소 4곳이 같은 resolver를 써야 한다.
  - `StepRunner.check_applicability()`
  - `GET /steps`
  - `POST /steps/run-all`
  - UI의 active 필터링 로직
- 현재 UI는 `not_applicable/disabled/on_demand` 문자열만 보고 필터링한다 (`frontend/src/components/shared/PipelineStepsPanel.tsx:173-185`).

즉, **validator 레지스트리는 맞지만, "엔진 전용"이 아니라 "catalog 전역 계약"이어야 한다.**

## 4. `ImageService` 분리는 필수다

판정: **강하게 타당**

근거:

- `backend/app/services/image_service.py` 는 5,281줄이다.
- import 시점부터 ref generation, scene generation, validation, prompt translation, fal.ai angle edit, variation/i2i, upload, review, primary selection, trace 저장, world guide 조립까지 모두 포함한다 (`backend/app/services/image_service.py:1-50`, 메서드 목록 `558`, `1233`, `2864`, `2896`, `3194`, `3342`, `3627`, `3802`, `4805`, `4920`).

초안 보강점:

- 초안의 3분할 방향은 맞다.
- 다만 step 경계도 함께 다시 정의해야 한다.
- 현재는 image step 경계 자체가 흐리다.
  - `RefImageGenStep` 가 composite 결과까지 처리하고 `composite_image_gen` 을 completed로 표시한다 (`backend/app/core/steps/image_steps.py:345-375`).
  - `CompositeImageGenStep` 도 다시 `generate_reference_images_only()` 를 호출한다 (`backend/app/core/steps/image_steps.py:434-436`).

즉, 이 영역은 단순 서비스 파일 쪼개기가 아니라 **"step 의미 재정의"까지 포함하는 리팩토링** 이어야 한다.

## 5. Frontend 서버 상태 구조 개선 필요성도 맞다

판정: **타당**

근거:

- 현재 프론트는 Vite SPA + React Router 기반이다 (`frontend/package.json:6-10`, `frontend/src/App.tsx:1-219`).
- `EpisodeDetail.tsx` 는 수동 fetch/callback/state 조합이 크다 (`frontend/src/pages/EpisodeDetail.tsx:129-186`).
- 다만 초안의 `useState 51개` 수치는 현재 코드 기준으로 맞지 않는다. 실제 `useState` 선언은 15개다.

초안 보강점:

- 문제는 맞지만 baseline 수치는 갱신해야 한다.
- React Query 도입은 유효하나, API 상태 모델이 먼저 안정돼야 한다.
- 지금 바로 React Query만 얹으면 불안정한 상태 계약을 더 넓게 퍼뜨릴 수 있다.

---

## 리팩토링 초안의 보정이 필요한 부분

## 1. "DB Primary, 체크포인트 Backup"은 지금 바로 원칙으로 선언하기엔 이르다

판정: **방향은 맞지만 초안은 과감하고 과소명세**

핵심 이유:

- `StepRunner.run()` 의 실제 실행 순서는 `step_run 갱신 → checkpoint 저장` 이다 (`backend/app/core/step_runner.py:243-290`).
- 대부분의 분석 step은 DB에 직접 쓰지 않고 checkpoint만 생성한다.
- DB projection은 나중에 `steps.py` 쪽에서 `_sync_checkpoints_to_db()` 로 반영된다 (`backend/app/api/v1/steps.py:217-223`, `247-253`, `657-667`).
- snapshot/restore 역시 checkpoint archive가 중심이다 (`backend/app/api/v1/steps.py:430-599`).

즉 현재 구조는 엄밀히 말해:

- `step_run`, 일부 UI runtime flag: DB 우선
- 분석 산출물: checkpoint 우선 + DB projection
- 생성 이미지: DB 메타 + filesystem 파일 동시 유지

따라서 문서 원칙을 아래처럼 고쳐야 한다.

### 권장 수정 원칙

1. `step_run`, `scene_still.is_selected`, `image_generated` 같은 **런타임 상태**는 DB primary.
2. LLM 분석 산출물은 당분간 **checkpoint canonical + DB projection** 으로 유지.
3. 이미지 자산은 **DB 메타 + 파일 시스템 동시 소유** 로 본다.
4. DB-primary 전환은 별도 migration phase로 분리한다.

이렇게 바꾸지 않으면 snapshot/복원, diff 디버깅, 재현성 보장이 문서에서 사라지지만 코드에서는 여전히 필수로 남는다.

## 2. "Step은 Pure Transformation"을 모든 step에 적용하면 범위가 폭증한다

판정: **부분 타당**

반례:

- `WorldGuideStep` 는 DB에서 직접 읽고 `WorldGuide` 를 insert/commit 한다 (`backend/app/core/steps/image_steps.py:243-305`).
- `SetDesignStep` 는 이미지 파일 생성 뒤 `ImageAsset` DB 등록을 시도한다 (`backend/app/core/steps/set_design_step.py:80-95`).
- `t2i_review` 는 자기 step이 아닌 다른 checkpoint를 덮어쓴다 (`backend/app/core/steps/t2i_review_step.py:52-92`).
- 이미지 step들은 `ImageService` 를 호출하면서 DB/파일 side effect를 일으킨다 (`backend/app/core/steps/image_steps.py:310-530`).

권장 수정:

- step 유형을 나눠야 한다.
  - `analysis-transform step`
  - `projection/sync step`
  - `asset-producing step`
  - `editorial/mutation step`
- "pure transformation" 원칙은 첫 번째 유형에만 강제하는 것이 현실적이다.

## 3. manifest가 현재 진짜 single registry는 아니다

판정: **초안 설명이 부족**

근거:

- step 메타는 `STEP_MANIFEST` 에 있지만 (`backend/app/core/step_manifest.py`)
- step class resolution은 별도 `STEP_CLASSES` 에 있다 (`backend/app/core/steps/__init__.py:43-101`).
- applicability 해석은 `StepRunner`, `run_all_steps`, `get_all_steps`, UI가 각각 따로 한다.
- 테스트도 여전히 구버전 manifest를 가정한다 (`backend/tests/test_step_manifest_v3.py:9-23`, `64-76`).

권장 수정:

- 장기적으로는 `step_catalog` 하나로 정리하는 것이 맞다.
- 최소한 문서에 아래가 추가되어야 한다.
  - 메타 진실원
  - class binding 진실원
  - applicability 진실원
  - UI active/disabled/not_applicable 해석 진실원

즉, 문제는 manifest 부재가 아니라 **registry가 분산되어 있다는 점** 이다.

## 4. Legacy 정리 항목의 우선순위가 과하다

판정: **부분 타당**

`analysis_steps_legacy.py` 이동이나 lifecycle 필드 추가는 나쁘지 않다.  
하지만 현재 더 급한 것은 파일 이동이 아니라 **실제 실행 경로 수렴** 이다.

왜냐하면:

- legacy의 핵심 문제는 "파일이 같은 디렉토리에 있다"가 아니라
- `episodes.py` 가 아직도 `AnalysisService` 기반 분석 진입점을 유지하고 있다는 점이다 (`backend/app/api/v1/episodes.py:125-250`).

권장 수정:

- 파일 이동은 Phase 0/1이 아니라 더 뒤로 미뤄도 된다.
- 먼저 정리할 것은 다음 두 가지다.
  - `POST /episodes/{id}/analyze` 의 운명
  - `POST /steps/run-all` 과의 역할 분담

## 5. 로드맵의 일부 항목은 아키텍처 리팩토링이 아니라 기능/버그 수정이다

판정: **분리 필요**

예:

- "번역 실패 시 warning 전파"는 유용하지만 architecture refactor의 핵심 축은 아니다.
- 이 항목은 `bugfix/hardening` 트랙으로 빼는 것이 맞다.

권장 수정:

- 로드맵을 두 트랙으로 분리
  - Architecture track
  - Production hardening track

이렇게 해야 일정과 효과가 명확해진다.

## 6. 일정 추정이 낙관적이다

판정: **낮게 잡힘**

이유:

- Applicability 중앙화만 해도 `StepRunner`, `GET /steps`, `run-all`, UI, 테스트를 함께 만져야 한다.
- sync 분해는 restore/run-all/run-step/image-step 사전 sync 의미 차이까지 정리해야 한다.
- image 단계는 서비스 분해 전에 step 경계부터 다시 정해야 한다.
- legacy 분석 경로 수렴은 단순 파일 이동이 아니라 API contract 조정이다.

권장 판단:

- 초안의 Phase 1(1~2일)은 최소 3~5일로 보는 것이 현실적이다.
- `ImageService` 와 legacy analysis convergence는 각각 독립 phase로 분리하는 편이 맞다.

---

## 문서에 반드시 추가되어야 할 보강 항목

## 1. "현재 공식 경로" 선언

아래 둘 중 하나를 문서 맨 앞에서 결정해야 한다.

- 옵션 A: `StepRunner` 경로를 공식 경로로 승격하고 `AnalysisService` 는 폐기 대상
- 옵션 B: `AnalysisService` 를 유지하되 step pipeline은 expert/debug 경로로 한정

지금은 두 경로가 모두 살아 있어 설계 문서가 현실보다 단순하다.

## 2. 데이터 소유권을 2-tier가 아니라 3-class로 정의

권장 분류:

| 데이터 종류 | 권장 진실원 | 비고 |
|---|---|---|
| 실행 상태(`step_run`, progress, runtime flags) | DB | 즉시 UI/제어용 |
| 분석 산출물(`entity_t2i`, `scene_detail`, `outlook_phase3`) | checkpoint canonical + DB projection | 당분간 유지 |
| 이미지 자산 | DB 메타 + 파일 시스템 | 둘 다 필요 |

이 분류가 있어야 snapshot, restore, export, resume semantics가 흔들리지 않는다.

## 3. step 유형 분류

문서에 아래 구분을 추가하는 것이 좋다.

| 유형 | 예시 | 리팩토링 규칙 |
|---|---|---|
| transform step | `scene_camera_flow`, `scene_consistency` | pure transformation 지향 |
| projection step | `_sync` 계열 | DB 쓰기 전담 |
| asset step | `ref_image_gen`, `scene_image_pipeline`, `set_design` | side effect 허용 |
| editorial step | `t2i_review` | 타 checkpoint 변경 명시 |

## 4. Step catalog 통합 계획

통합 대상:

- `STEP_MANIFEST`
- `STEP_CLASSES`
- applicability resolver
- UI visibility / lifecycle
- invalidation metadata

현재 문서는 manifest만 강조하지만 실제로는 이것만으론 부족하다.

## 5. 테스트 복구를 Phase 0로 올려야 한다

현재 테스트 일부는 이미 현행 구조와 맞지 않는다.

근거:

- `backend/tests/test_step_manifest_v3.py:9-23` 는 여전히 `26단계`, `image 4개` 를 가정한다.
- 같은 파일 `64-76` 은 `scene_director` 가 `scene_split`, `scene_detail` 이 `scene_dependency/outlook_extraction/scene_cinematography` 에 의존한다고 본다. 현재 manifest와 다르다.

이 상태에서는 "리팩토링 전 안전망" 역할을 기대하기 어렵다.  
따라서 테스트 정렬은 Phase 0 또는 최소 Phase 1 선행 조건이어야 한다.

---

## 권장 수정 로드맵

## Phase 0. 현행 계약 정리

목표:

- 현재 공식 경로 결정
- registry 분산 지점 정리
- 테스트 baseline 복구
- 문서 수치 갱신

작업:

- `AnalysisService` 경로와 `StepRunner` 경로를 비교한 공식 운영 방침 작성
- applicability 해석 지점을 목록화
- step count, category count, active/optional/deprecated count 재산출
- stale 테스트 갱신

산출물:

- `current-runtime-contract.md`
- `step-catalog-contract.md`
- 정정된 baseline metrics

## Phase 1. Step catalog 통합

목표:

- manifest/class/applicability/UI 표시 규칙을 하나의 계약으로 통합

작업:

- applicability resolver 공용화
- invalidation metadata 추가
- lifecycle/visibility 메타 설계
- `get_all_steps`, `run_all_steps`, `StepRunner` 동기화

산출물:

- 중앙 catalog 모듈
- 하드코딩 invalidation 제거

## Phase 2. Checkpoint projection 분해

목표:

- `_sync_checkpoints_to_db()` 해체

작업:

- entity / relation / still / outlook / episode projection 분리
- 호출 목적별 API 정리
- restore semantics 명문화

산출물:

- domain sync services
- targeted sync entrypoints

## Phase 3. Image domain 재구성

목표:

- `ImageService` 분해 + step 의미 재정의

작업:

- reference/composite/scene/variation/editorial 도메인 분리
- `ref_image_gen` 과 `composite_image_gen` 의 실제 책임 정렬
- asset step 공통 규칙 수립

산출물:

- 3~4개 image domain service
- 재정의된 image step boundaries

## Phase 4. Legacy analysis convergence

목표:

- `AnalysisService` 와 `StepRunner` 중 하나로 수렴

작업:

- `/episodes/{id}/analyze` 의 내부를 step pipeline으로 교체하거나
- 반대로 step pipeline을 부가 경로로 격하

산출물:

- 단일 공식 분석 진입점

## Phase 5. Frontend 서버 상태 재구성

목표:

- 수동 fetch/state 조합을 서버 상태 abstraction으로 정리

작업:

- query abstraction 도입
- episode detail / pipeline panel / image review 순차 전환

산출물:

- 프론트 서버 상태 계층
- 응답 모델 정리

---

## 초안 문서별 수정 권고

## `00-analysis.md`

추가해야 할 것:

- `AnalysisService` 와 `StepRunner` 이중 운영
- `ref_image_gen` 이 `composite_image_gen` 상태를 직접 완료 처리하는 구조
- 테스트 기준 붕괴
- "DB primary" 전환의 현재 구조상 제약

정정해야 할 것:

- stale baseline metrics
- frontend state 수치

## `01-target-design.md`

수정해야 할 것:

- "DB는 진실원, 체크포인트는 백업"을 즉시 원칙이 아니라 migration target으로 표현
- "Step은 Pure Transformation"을 transform step 범위로 축소
- `Step Catalog` 개념을 manifest보다 넓게 정의

## `02-before-after.md`

추가해야 할 것:

- Before에 `AnalysisService` 경로
- Before에 image step 경계 붕괴 사례
- After에 step 유형 분류

## `03-roadmap.md`

수정해야 할 것:

- Phase 0 추가
- translation warning 같은 bugfix 항목 분리
- file move/lifecycle보다 registry/applicability/test 정렬을 먼저
- 일정 보수적으로 조정

## `04-next-session-brief.md`

수정해야 할 것:

- 첫 작업을 "Phase 1 quick wins"가 아니라 "Phase 0 contract fix"로 바꾸는 것이 맞다

---

## 최종 판단

이 리팩토링 초안은 **문제 진단 자체는 맞다.**  
특히 sync monolith, image monolith, invalidation 하드코딩, applicability drift, frontend imperative state 문제 지적은 현실과 잘 맞는다.

하지만 **현재 코드베이스의 가장 중요한 구조적 사실 두 가지가 초안에 충분히 반영되지 않았다.**

1. 분석 진입점이 아직 `AnalysisService` 와 `StepRunner` 로 이원화되어 있다.
2. 분석 산출물의 persistence 모델은 아직 "DB primary"가 아니라 "checkpoint canonical + DB projection"에 가깝다.

따라서 권장 결론은 다음과 같다.

- 이 초안은 폐기할 수준은 아니다.
- 그러나 **수정 없이 구현에 들어가면 설계가 현실을 따라가지 못한다.**
- 먼저 문서를 위 보정안대로 갱신한 뒤, `Phase 0` 부터 시작하는 것이 가장 안전하다.

---

## 근거 코드 인덱스

- `backend/app/core/step_runner.py:82-92`  
  applicability 해석이 제한적이다.
- `backend/app/core/step_runner.py:243-290`  
  실행 엔진은 checkpoint 저장을 기본 경로로 사용한다.
- `backend/app/api/v1/steps.py:79-126`  
  step listing 로직이 applicability/blocked 판단을 별도로 구현한다.
- `backend/app/api/v1/steps.py:191-204`  
  run-all filtering이 별도 구현되어 있다.
- `backend/app/api/v1/steps.py:356-390`  
  shot toggle invalidation/resume-sensitive 가 하드코딩이다.
- `backend/app/api/v1/steps.py:768-1487`  
  `_sync_checkpoints_to_db()` 가 과도하게 큰 projection 함수다.
- `backend/app/core/steps/image_steps.py:51-59`  
  image step이 API private function에 역의존한다.
- `backend/app/core/steps/image_steps.py:345-375`  
  `ref_image_gen` 이 `composite_image_gen` 상태를 직접 완료 처리한다.
- `backend/app/core/steps/image_steps.py:434-436`  
  `CompositeImageGenStep` 도 다시 reference generation service를 호출한다.
- `backend/app/core/steps/set_design_step.py:38-49`  
  `set_design` 는 runtime env에 의해 no-op 되지만 manifest는 `always` 다.
- `backend/app/api/v1/episodes.py:125-250`  
  `/episodes/{id}/analyze`, `/reanalyze-scenes` 가 아직 `AnalysisService` 를 쓴다.
- `backend/app/services/analysis_service.py:919-938`, `1026-1085`  
  legacy 분석 경로는 destructive DB cleanup와 자체 checkpoint 흐름을 가진다.
- `backend/app/services/image_service.py:1-50` 및 메서드 분포  
  이미지 도메인 책임이 한 클래스에 과집중되어 있다.
- `frontend/package.json:6-18`, `frontend/src/App.tsx:1-219`  
  프론트는 Vite + React Router 기반 SPA다.
- `frontend/src/pages/EpisodeDetail.tsx:129-186`  
  상태/콜백 조합이 크지만, 초안 수치처럼 51개의 `useState` 는 아니다.
- `backend/tests/test_step_manifest_v3.py:9-23`, `64-76`  
  테스트 일부는 이미 현행 step graph와 어긋난다.
