<!-- FILE: _St/ai_read.md | ROLE: sort 전체 프로젝트 현재 기준 -->

## 패키지 v319 현재 기준
- 단일 목표: 수동 MP4 1건 편집복사 완료 직후 `폴더를 열까요?` 확인/취소를 제공하면서 Side `편집폴더 열기` 버튼도 함께 유지한다.
- 정상 흐름: 수동 MP4 클릭 → WS8771 원본 저장 → `편집폴더에 복사` 체크 시 picker → 선택 폴더로 해당 MP4 1건 copy2 → 복사 성공 후 `폴더를 열까요?`.
- `확인`은 WS8771 GUI main thread에서 기존 `ig_open_folder` 경로로 Windows 탐색기를 즉시 연다. `취소`는 아무 작업 없이 끝낸다.
- 확인/취소 어느 쪽이든 picker의 `target_folder`는 Background 응답에 유지되어 `chrome.storage.session[IGBLUE/edit_folder_runtime_v318]`으로 현재 Chrome 세션에만 보관되고 Side `편집폴더 열기`가 활성화된다.
- 편집폴더 경로는 Instagram localStorage나 영구 chrome.storage.local에 저장하지 않는다. Chrome 재시작 시 session 경로는 사라진다.
- 자동 다운로드 여러 파일 편집복사 dead code는 Side에서 제거했다. 편집 대상은 수동 MP4 1건뿐이다.
- 사용자 실제 Windows 확인으로 v317 수동 MP4 원본 저장 → picker → 선택 → 1건 복사는 `[완료]`다. v319 확인/취소 창과 Side 버튼 열기는 실제 Windows `[검증대기]`다.
- 버전: manifest `2.3.8.3190 / 2.3.8.319`, Auto8700·Cache8701·WS8771·RTC8790_8791 표시 `2.3.8.319 / v319`.

## 패키지 v318 현재 기준
- 단일 목표: 수동 MP4 편집복사에서 사용자가 picker로 선택한 편집폴더를 현재 Chrome 실행 세션 동안만 기억해 Side `편집폴더 열기` 버튼을 활성화한다.
- 경로는 `chrome.storage.session`만 사용하며 Instagram localStorage와 영구 `chrome.storage.local`에는 저장하지 않는다.
- Background는 picker 성공 `target_folder`를 session에 기록하고 runtime 메시지로 Side에 알린다. Side를 닫았다 다시 열어도 같은 Chrome 실행 세션이면 session에서 복원한다.
- 버튼 클릭은 기존 WS8771 `ig_open_folder`로 선택 폴더를 연다. 새 수동 picker 시작 전 이전 경로를 지운다.
- v317 수동 MP4 1건 다운로드 → picker → 선택 → 복사는 사용자 실제 Windows 확인 OK로 완료 처리한다.
- 버전: manifest `2.3.8.3180 / 2.3.8.318`, Auto8700·Cache8701·WS8771·RTC8790_8791 표시 `2.3.8.318 / v318`.
- v318 버튼 활성/열기 실제 Windows는 `[검증대기]`.

## 패키지 v317 현재 기준
- 단일 목표: Side를 닫아도 수동 다운로드 상태 강조가 동작하도록 설정/이벤트를 extension 전역으로 고정하고, `편집폴더에 복사`를 자동 여러 파일이 아니라 수동 MP4 1건 완료 직후로 교정한다.
- `다운로드 상태 강조` 설정의 원본은 `chrome.storage.local[ndy_ig_visual_settings]`이다. 해당 설정은 Instagram `localStorage`를 읽거나 쓰지 않는다. Content는 탭 시작 시와 storage 변경 시 MAIN에 설정을 전달한다.
- WS 수동 다운로드 시작 이벤트는 `shortcode`, `asset_kind`, `media_index`, `meta`를 포함해 MAIN이 클릭 즉시 정확한 카드/버튼을 강조한다. 완료 뒤 버튼에는 `다운`, 카드에는 `MP4 다운/JPG 다운` 상태를 유지한다. DOM 재생성은 MAIN state map + MutationObserver가 재적용한다.
- PY/WS 미연결 원래 MAIN downloader도 start/done/error 이벤트를 직접 발생시켜 Side가 없어도 같은 수동 상태 표시를 사용한다.
- `편집폴더에 복사` 체크는 `chrome.storage.local[IGBLUE/edit_copy_enabled_v315]`에 유지한다. 수동 `MANUAL_SINGLE` MP4가 WS8771 저장을 끝낸 직후 Background가 방금 받은 파일 1개만 `ig_copy_to_edit_folder`로 보내고 WS GUI main thread picker를 띄운다. 자동 다운로드 다중 누적/자동종료 편집복사는 폐기한다.
- 편집폴더 경로는 영구 저장하지 않는다. 체크가 꺼져 있거나 PY/WS 미연결 원래 injection 다운로드면 편집폴더 복사를 실행하지 않는다.
- 버전: manifest `2.3.8.3170 / 2.3.8.317`, Auto8700·Cache8701·WS8771·RTC8790_8791 표시 `2.3.8.317 / v317`.
- 정적/단위 검증: pytest 130 passed, IG Node 52/52, RTC Node 3/3, Analyze UI 1/1, JS syntax 64, Python compile 38, JSON 6 OK. 실제 Chrome/Windows의 Side 닫힘 수동 강조·`다운` 유지·수동 MP4 완료 직후 picker/복사는 `[검증대기]`.
- 전체 진행률: 34% (완료 60 / 전체 176).


## 패키지 v316 현재 기준
- 단일 목표: 자동 다운로드 종료 후 `편집폴더에 복사`의 Windows picker를 WS8771 GUI 메인 스레드로 교정하고, PY/WS 미연결 수동 다운로드는 원래 MAIN injection downloader로만 복구한다.
- `편집폴더에 복사` 체크 상태만 `chrome.storage.local`에 유지한다. 편집폴더 경로는 저장하지 않으며 자동실행마다 종료 시 새로 선택한다.
- 새 자동 세션에서 실제 신규 저장된 MP4만 누적한다. 전체 계정 자동종료 뒤 `ig_copy_to_edit_folder`는 WS socket worker에서 직접 실행하지 않고 Tk `control_queue`로 보내 GUI main thread의 `filedialog.askdirectory()`를 1회 호출한다.
- 선택 폴더에는 MP4만 `shutil.copy2`로 복사한다. 동일 파일명은 편집본 보호를 위해 덮어쓰지 않고 SKIP한다. 취소/실패를 성공으로 처리하지 않는다.
- 선택 성공 후 Side `편집폴더 열기`가 그 실행 동안 활성화된다. 마지막 선택 경로는 메모리에만 두며 새 자동 세션 시작 때 지운다.
- PY/WS 미연결 수동 클릭은 v315 Background `chrome.downloads` 파일명 가공 fallback을 사용하지 않는다. 원래 MAIN `fetch → Blob → a.download` downloader를 사용하며 기본 이름은 기존 injection 기준 `shortcode + Content-Type 확장자`(Video=`shortcode.mp4`)다.
- 자동 다운로드는 계속 WS8771 direct 경로만 사용하고 수동 원래 downloader로 fallback하지 않는다.
- 기존 v314 방식1/2 `d-none`, v313 썸네일100/섞기/랜덤지연·예약, v312 Explorer scroll 수정은 유지한다.
- 버전: manifest `2.3.8.3160 / 2.3.8.316`, Auto8700·Cache8701·WS8771·RTC8790_8791 표시 `2.3.8.316 / v316`.
- 정적/단위 검증: pytest 130 passed, IG Node 51/51, RTC Node 3/3, Analyze UI 1/1, JS syntax 63, Python compile 38, JSON 6 OK. 실제 Windows picker/복사 및 모든 PY/WS 종료 수동 `shortcode.mp4` 저장은 `[검증대기]`다.
- 전체 진행률: 35% (완료 61 / 전체 174).


## 패키지 v314 현재 기준
- 단일 목표: Side 자동/예약 다운로드 UI를 운영 기준에 맞게 정리한다.
- 하단 썸네일 기본 가로폭은 100px. 구 기본값 200px 저장 상태는 1회 migration으로 100px로 바꾸며 다른 명시값은 유지한다.
- v311 `수집계정 목록 > 섞기`와 `자동실행 사용 > 자동섞기`는 회귀 금지 기능으로 유지한다. 섞기 이력/체크는 `chrome.storage.local` 기준이다.
- 일반 `Image 저장`은 제거하고 자동 JPG 저장은 Cache8701 자동 섬네일 저장으로 단일화한다. 수동 썸네일 직접 JPG 클릭 저장은 유지한다.
- 다운로드 지연은 `기본초 + 매 파일 0~랜덤최대초`; 랜덤최대 기본은 5초다.
- 다운로드 방식1/2는 DOM/핸들러를 삭제하지 않고 `hidden + d-none`으로 강제 비표시한다.
- 자동예약1/2는 슬롯별 `랜덤 +[00]분` 체크/입력값을 제공한다. 체크 상태는 Chrome 공통 storage에 저장하고, 실제 분 값은 Side 로딩 때 0~59 중 새로 뽑고 수동 수정 가능하며 정상 자동종료 후 다시 뽑는다.
- v312 Explorer Swipe/scroll 인젝션은 사용자 실제 Chrome 확인으로 `[완료]`다.
- v314 정적/단위 검증: pytest 128 passed, IG Node 49/49, RTC Node 3/3, Analyze UI 1/1. 실제 Chrome에서 방식1/2 d-none, 랜덤지연/예약 랜덤분은 `[검증대기]`.
- 버전: manifest `2.3.8.3140 / 2.3.8.314`, Auto8700·Cache8701·WS8771·RTC8790_8791 표시 `2.3.8.314 / v314`.
- 전체 진행률: 36% (완료 61 / 전체 170).


## 패키지 v312 현재 기준
- 이번 단일 목표는 upstream 2.4.1에서 확인된 Explorer Swipe/scroll 이후 MAIN metadata 인젝션 누락 회귀를 현재 IG_StYellow에 최소 이식하는 것이다.
- 기존 Explorer `clips` 파서의 `fill_items[].media`와 `one_by_two_item.clips.items[].media`는 그대로 유지한다.
- 추가 경로는 `layout_content.medias[].media`와 `layout_content.medias[].clips.items[].media` 두 가지다.
- `ij2.js` 전체를 덮어쓰지 않고 확인된 파서 차이만 `_St/IG_StYellow/js/instagram_inject.js`에 반영했다.
- 전용 회귀 `IG_StYellow/test_ext_explore_clips_scroll_v312.js`는 기존/신규/sparse Explorer 응답을 실제 현재 `z()` 파서로 실행한다.
- 버전: manifest `2.3.8.3120 / 2.3.8.312`, Auto8700·Cache8701·WS8771·RTC8790_8791 표시 `2.3.8.312 / v312`.
- 실제 Chrome의 Explore 첫 로딩 → Swipe/scroll → 추가 카드 overlay/metadata 연속 적용은 사용자 확인 `OK`로 `[완료]`다.
- 정적/단위 검증: pytest 128 passed, IG Node 47/47, RTC Node 3/3, Analyze UI 1/1, JS syntax 59, Python compile 38, JSON 6 OK.
- 전체 진행률: 36% (완료 60 / 전체 168).


## 패키지 v311 현재 기준
- 이번 단일 목표는 Side 수집계정 순서 섞기·첫 시작 자동섞기와 반복 `IG_DBG` 콘솔 출력 억제다.
- `수집계정 목록` 헤더의 `섞기`는 현재 목록을 Fisher–Yates 방식으로 섞고, 2개 이상인데 우연히 원래 순서가 나오면 1칸 회전해 반드시 다른 순서로 만든다.
- 섞기 직후 현재 화면 목록을 사용하며 SQLite 저장은 기존 `💾 저장` 의미를 유지한다. 섞기로 인해 저장 전 변경 상태가 되므로 저장 버튼에 `*` 표시를 유지한다.
- 섞기 이력은 Instagram 사이트 `localStorage`가 아니라 확장 공통 `chrome.storage.local`의 `IGBLUE/account_queue_shuffle_v311`에 최대 50건 보관한다. 각 기록은 시각·수동/자동 source·이전 순서·새 순서·목록 index를 가진다.
- `자동실행 사용` 다음의 `자동섞기` 체크 상태도 `chrome.storage.local`에 저장한다. 새 사용자 시작에서 화면 시작값이 정확히 `1/N`이고 목록이 2개 이상일 때만 시작 직전 1회 섞는다. `0/N`, `2/N 이상`, 다음 계정 continuation에서는 자동섞기하지 않는다.
- 자동섞기 후 시작 index는 1을 유지하며 저장된 중단 위치나 이전 resume 상태를 사용하지 않는 기존 v307 시작 규칙을 유지한다.
- Side·Content·Background·MAIN의 `[IG_DBG]` debug snapshot 저장은 유지하되 `IG_DBG_CONSOLE_ON=0`으로 콘솔 반복 출력을 끈다. 일반 체크 로그·오류 로그는 제거하지 않는다.
- 버전: manifest `2.3.8.3110 / 2.3.8.311`, Auto8700·Cache8701·WS8771·RTC8790_8791 표시 `2.3.8.311 / v311`.
- 정적/단위 검증: pytest 128 passed, IG Node 46/46, RTC Node 3/3, Analyze UI 1/1, JS syntax 57, Python compile 38, JSON 6 OK. 실제 Chrome 수동 섞기·자동섞기·Chrome storage 이력 확인은 `[검증대기]`다.
- 전체 진행률: 36% (완료 60 / 전체 167).


## 패키지 v310 현재 기준
- 이번 단일 목표는 Tray 아이콘 공통 적용과 RTC8790_8791 시작 즉시 Tray 숨김이다.
- `_St/IG_StYellow/icons/logo.png`를 기준으로 `_St/Shared/assets/ig_styellow_tray.png`와 Windows용 `ig_styellow_tray.ico`를 배포 자산으로 둔다. Auto8700·WS8771·RTC8790_8791 Tray는 모두 같은 ICO를 사용한다.
- `_St/Shared/windows_tray.py`는 선택적 `icon_path`를 받고 Win32 `LoadImageW(..., IMAGE_ICON, LR_LOADFROMFILE)`로 ICO를 읽는다. 파일이 없거나 로드 실패하면 Windows 기본 아이콘으로만 fallback한다.
- Auto8700·WS8771의 기존 `_`/`.`/복원/X 의미는 유지하고 Tray 아이콘만 프로젝트 아이콘으로 교체한다.
- RTC8790_8791은 `tk.Tk()` 생성 직후 `withdraw()`하고, Tray 아이콘 생성 성공 시 숨김 상태를 유지한다. Tray 생성 실패 시 메인창을 즉시 복원해 찾을 수 없는 상태를 금지한다.
- RTC Tray 아이콘 클릭은 메인 UI를 복원하고, UI의 `.` 버튼으로 다시 Tray에 숨길 수 있다. X는 기존 종료 의미를 유지하고 종료 시 Tray 아이콘을 제거한다.
- 버전: manifest `2.3.8.3100 / 2.3.8.310`, Auto8700·Cache8701·WS8771·RTC8790_8791 표시 `2.3.8.310 / v310`.
- 정적/단위 검증: pytest 128 passed, IG Node 45/45, RTC JS 3/3, Analyze UI 1/1. 실제 Windows 시작 숨김·공용 아이콘 표시·Tray 복원은 `[검증대기]`.
- 전체 진행률: 37% (완료 60 / 전체 164).

## 패키지 v309 현재 기준
- 최종 정적 결과: pytest 125 passed, IG Node 45/45, Analyze UI 1/1, RTC JS 3/3, JS syntax 56, Python compile 36, JSON 6 OK.
- 이번 단일 목표는 Auto8700·WS8771의 최소화/Tray UX 통일이다.
- Auto8700 `_`는 34x34의 보라색 `ㅁ` 하나만 남기며, `ㅁ` 클릭 시 전체 UI로 복원한다. 기존 520px 중간 compact bar는 사용하지 않는다.
- WS8771 `_`는 기존 34px `ㅁ` 최소창을 유지하며 enter_mode 상태색을 사용해 Auto8700 보라색과 구분한다.
- Auto8700·WS8771의 `.` 버튼은 Windows notification area Tray 아이콘 생성 성공 후 `withdraw()`로 화면에서 숨긴다. Tray 아이콘 클릭/더블클릭/우클릭 시 같은 UI를 복원한다.
- Tray 기능은 외부 pystray/Pillow 의존성 없이 `_St/Shared/windows_tray.py`의 Win32 `Shell_NotifyIconW` 공용 helper를 사용한다. Tray 생성 실패 시 화면을 숨기지 않는다.
- X/전체종료의 기존 종료 의미는 유지하며 Tray 이동과 혼동하지 않는다.
- 패키지 버전: manifest `2.3.8.3090 / 2.3.8.309`, Auto8700·Cache8701 `2.3.8.309 / v309`, WS 화면 `2.3.8.309 / v309`.
- 정적/단위 검증 후 실제 Windows에서 Auto/WS `_`, `.`, Tray 복원 확인 전에는 `[검증대기]`다.
- 전체 진행률: 37% (완료 60 / 전체 162).

## 패키지 v308 현재 기준
- CDP 최초 초기화 순서는 `Instagram Runtime 검증 → EXT 설치 여부 확인 → 현재 실행 `_St/IG_StYellow` 경로 일치 확인/필요시 재설치 → EXT reload 1회 → 현재 Instagram Page.reload 1회 → EXT/content 버전 일치 → CDP:O`다.
- EXT 경로는 `Auto8700/auto_service8700_manager.py`의 실제 실행 위치에서 부모 `_St`를 계산하고 그 아래 `IG_StYellow/manifest.json`을 단일 기준으로 사용한다. `D:\_St` 같은 절대경로를 하드코딩하지 않는다.
- `Extensions.getExtensions`에서 IG Sorter Yellow unpacked EXT가 없으면 `Extensions.loadUnpacked`으로 설치한다. 같은 이름이 다른 경로에 있으면 해당 unpacked EXT를 `Extensions.uninstall`한 뒤 현재 `_St/IG_StYellow`에서 다시 설치한다.
- 같은 경로에 정상 설치되어 있으면 재설치하지 않고 최초 reload 단계로 진행한다. 설치/경로 명령 성공만으로 CDP:O를 주지 않고 최종 manifest/content 검증까지 완료해야 한다.
- Chrome이 experimental `Extensions` 도메인을 지원하지 않거나 설치/경로 검증이 실패하면 CDP:X를 유지하고 실패 로그를 남긴다.
- 버전: manifest `2.3.8.3080 / 2.3.8.308`, Auto8700·Cache8701 `2.3.8.308 / v308`.
- 단위 검증: Auto8700 설치/경로/재설치/최초 reload 테스트 포함 38 passed. 실제 Windows `CR-01~04` 설치·경로·reload 로그는 `[검증대기]`다.
- 전체 진행률: 36% (완료 58 / 전체 160).

## 패키지 v307 현재 기준
- 이번 최종 핵심 목표는 `CDP:O`를 단순 8700 포트 상태가 아니라 실제 제어·EXT 최신 코드 적용 완료 상태로 만든다.
- `CDP:O` 조건은 ① 8700 Instagram page target 존재 ② `webSocketDebuggerUrl` 존재 ③ `Runtime.evaluate("1+1") == 2` ④ Auto8700 프로세스 최초 1회 `IG Sorter Yellow` EXT `chrome.runtime.reload()` 명령 성공 ⑤ 현재 Instagram `Page.reload` 1회 ⑥ EXT manifest 및 content 버전이 현재 로컬 manifest와 일치함을 모두 만족하는 것이다.
- `/json/version` 응답은 포트 확인일 뿐 `CDP:O` 조건으로 사용하지 않는다. 저장된 과거 `cdp_ready`도 현재 O 판정에 사용하지 않는다.
- 최초 EXT reload는 Auto8700 프로세스당 1회뿐이며 페이지 열기·탭 전환·상태 polling마다 반복하지 않는다.
- EXT refresh 검증 전 Auto8700 CDP gate는 X를 전달한다. Content는 전달된 `ok:false`를 `true`로 강제 변환하지 않는다.
- `/status`의 `schedule_storage.cdp_ready`는 상태 worker가 현재 Runtime+EXT 검증 결과로 갱신해 stale O를 남기지 않는다.
- 화면·복사 버전은 `manifest.json → chrome.runtime.getManifest() → 전역 버전값` 단일 기준을 사용한다. `탭 제어 vNNN`을 별도 하드코딩하지 않는다.
- 이전 작업의 `마지막 파일 변경 + 3초` 서비스별 재실행 worker, `Showing A` Cache 자동저장/tab_id 격리, 자동 중단 표시 보존, 화면 리스트 번호에서 시작, input 복사, Shift/Ctrl 더블클릭 계정 열기를 함께 유지한다.
- 버전: manifest `2.3.8.3070 / 2.3.8.307`, Auto8700·Cache8701 `2.3.8.307 / v307`.
- 정적 검증: pytest 118 passed, IG Node 45/45 OK, JS syntax 57 OK, Python compile 35 OK, JSON 6 OK.
- 실제 Windows에서 CR-01~03과 `CDP:X → O`, EXT/Content v307 재적용, 3초 재실행, Cache `Showing A`, 신규 Side 조작은 `[검증대기]`다.
- 전체 진행률: 36% (완료 58 / 전체 159).

## 패키지 v305 현재 기준
- 단일 목표: 현재 대상 Instagram 탭에서 `Showing A of B`의 A개 필터 결과를 Cache8701이 자동 저장하고, 다른 탭 데이터와 섞이지 않게 한다.
- `현재 채널만` 체크 시 고정 탭, 해제 시 활성 Instagram 탭의 `tab_id`를 따른다. 자동 중단은 탭 추적·DOM 감시·Cache 저장을 중단하지 않는다.
- Cache8701 관찰 키·run 상태·CI 로그에 `tab_id`를 포함하고, 같은 이미지 URL이라도 탭별로 분리한다. CDP 세션 URL의 계정과 메타데이터 계정이 다르면 매칭하지 않는다.
- 자동 시작 위치는 저장 캐시가 아니라 화면 수집계정 리스트 박스 현재 번호만 사용한다.
- 수집계정 헤더 전체 복사 시 input 현재값을 클립보드 문자열에 포함한다.
- 버전: manifest `2.3.8.3050 / 2.3.8.305`, Cache8701 `2.3.8.305`.
- 정적 결과: Cache8701 pytest 14 passed, Cache bridge Node OK, Python compile OK. 실제 Windows에서 moyasi_jm `Showing 10` → 처리 10, CI-03 미처리 0, SQLite·8780 표시 10 확인 전 `[검증대기]`.

## 패키지 v304 현재 기준
- 이번 단일 목표는 Cache8701 자동 이미지 저장 복구와 선택형 `8701에 로깅`이다. CDP 표시·자동 Swipe 코드는 변경하지 않는다.
- 사용자 실제 화면에서 Cache Image 대상 64건이 등록됐지만 처리 0건이었고, 같은 시점 OWNER는 `active:false`였다.
- 원인은 Cache8701이 OWNER 비활성을 CDP target 없음으로 오판해 이미지 감시 세션을 매초 종료한 것이다.
- OWNER active는 사용자 포커스 상태일 뿐 CDP 연결 상태가 아니다. `cdp_alive=true`이고 Instagram target이 하나 이상이면 비활성 OWNER를 포함해 감시를 계속한다.
- 자동 섬네일 설정에 `8701에 로깅`을 추가한다. 기본값은 OFF다.
- 체크 시 `Cache8701/logs/cache8701_log_YYYYMMDD_HHMMSS_pidNNNN.log` 한 파일을 실행별로 생성한다.
- 새 로그 생성 전에 기존 대상 로그가 20개 이상이면 오래된 파일부터 삭제해 생성 후 총 20개 이하를 유지한다.
- 로그는 CI-00~CI-06 기능 체크와 실패 지점 예외추적만 기록하고 초 단위 정상 반복 로그를 남기지 않는다.
- 버전: manifest `2.3.8.3040 / 2.3.8.304`, Auto8700·Cache8701 `2.3.8.304 / v304`.
- 전체 진행률: 38% (완료 58 / 전체 152). 실제 Windows 신규 저장·SQLite·8780 표시·로그 파일 생성 전에는 `[검증대기]`다.

## 패키지 v303 현재 기준
- 기준 소스는 실제 CDP:O 복구와 Explorer 기본 섬네일 이동이 확인된 v297이다.
- 이번 목표는 세 가지뿐이다: Side 중심 CDP 상태 관리, Cache Image 저장, 기존 자동 Swipe·날짜 경계 흐름 보호.
- CDP 상태 원본은 Side/Background다. Main DOM은 받은 O/X/확인중 값을 표시만 하며 새로고침·URL 이동만으로 X를 만들지 않는다.
- 새 문서 onload에서는 Background 현재 상태를 1회 조회한다. 실제 Auto8700 상태 통신이 연속 실패한 경우에만 X로 바꾼다.
- Cache Image는 Cache8701이 `{shortcode}.{실제확장자}`로 저장하고 SQLite 등록·최소 `index.htm` 생성을 수행한다. Video 저장 ON/OFF와 독립이다.
- 조건 대상 0개라도 날짜 범위 안이면 Swipe를 계속하는 동작은 v297 이전부터 있던 기존 정상 기능이다. 새로 작성하지 않고 보호 주석·회귀 테스트로 고정한다.
- 날짜 경계 또는 실제 추가 로딩 없음이 확인되기 전에는 계정 종료·다음 URL 이동·Main 카드 연결 대기로 바꾸지 않는다.
- Explorer 검색 섬네일의 원본 `href`·기본 클릭은 EXT 로딩 전후 모두 보존한다.
- UI 숨김은 `display:none`만 허용하고 DOM·href·이벤트 리스너 삭제를 금지한다.
- 버전: manifest `2.3.8.3030 / 2.3.8.303`, Auto8700·Cache8701 `2.3.8.303 / v303`.
- 전체 진행률: 39% (완료 58 / 전체 150). 실제 Windows 검증 전 세 핵심 기능은 `[검증대기]`다.


## 패키지 v297 현재 기준
- Analyze8780은 Instagram 로컬 미디어를 DB `asset_id` 주소가 아니라 `/_StDown/instagram/{account}/{shortcode}.{실제확장자}`로 제공한다.
- `/api/posts/{post_id}/media/{asset_id}`와 `/api/posts/{post_id}/open-folder`는 제거했다.
- 게시물 목록 갱신 때 `D:\_StDown\instagram\{account}`에서 `{shortcode}.{jpg|jpeg|png|webp|avif|gif|mp4}`만 직접 찾는다.
- `_thumbnail.*`, `_auto_seen.*`, 임의 원본 파일명은 8780 표준 미디어로 인식하지 않는다.
- 8780 게시물 조회는 `media_assets`를 조회하지 않고 `media_files`의 가상 URL·실제 절대경로·확장자를 파일시스템에서 만든다.
- 이미지·영상 열기는 `_StDown` 가상 URL, 폴더 열기는 Windows Explorer `/select`로 실제 파일을 선택하며 경로 복사를 제공한다.
- DB 미디어 행이 없어도 갱신 시 표준 파일을 표시하고, 게시물 삭제 시 표준 파일도 함께 삭제한다.
- 현재 구현은 Instagram만 허용한다. TikTok·YouTube·샤오홍수는 향후 동일 플랫폼 규약으로 확장한다.
- 기존 10분 통계·중복 observation·Cache8701 저장 규칙은 변경하지 않는다.
- 실제 Windows 8780 이미지·MP4·Explorer 선택·경로 복사 확인 전에는 `[검증대기]`다.

## 패키지 v296 현재 기준
- 전체 진행률: 40% (완료 58 / 전체 145).
- Cache8701 신규 이미지 파일명은 `D:\_StDown\instagram\{account}\{shortcode}.{실제확장자}`다.
- JPEG·PNG·WebP·AVIF는 변환하지 않고 응답의 실제 형식과 확장자를 유지한다.
- Shared는 `CACHE8701_CDP`, `AUTO_SEEN_JPG` source mode를 허용하고 동일 파일 재관찰 때도 누락 DB 등록을 수행한다.
- Cache8701 저장 이벤트는 shortcode·확장자·SHA256 기반 UUID로 중복 이벤트를 방지한다.
- Analyze8780은 media_assets의 실제 존재 파일을 CDN보다 우선 표시한다. DB 누락 시 shortcode 표준 파일과 기존 `_thumbnail.*`, `_auto_seen.*`를 찾아 현재 열린 DB에 복구 등록한다.
- DB 행만 있고 실제 파일이 없으면 브라우저에 깨진 로컬 URL을 주지 않고 원격 thumbnail URL로 fallback한다.
- 이미지 등록은 기존 `post_stat_10m` 10분 통계 저장·중복 정책을 변경하지 않는다.
- 정적·단위 검증 후에도 실제 Windows 신규 파일명·CI-06·8780 화면 확인 전에는 `[검증대기]`다.


## 패키지 v295 현재 기준
- Auto8700 `GET /api/cdp/connection`이 실제 Chrome CDP 포트, Instagram target 목록, 읽기 전용 owner target과 generation을 제공한다.
- Cache8701은 고정 9222와 page WebSocket 직접 연결을 사용하지 않는다.
- Cache8701은 Auto8700 연결정보의 실제 포트(기본 8700)에서 browser WebSocket을 얻고 `Target.attachToTarget(flatten=true)`로 독립 `sessionId`를 만든다.
- Cache8701은 세션별 `Network.enable`, `responseReceived`, `loadingFinished`, `getResponseBody`만 사용하며 탭 이동·종료·캐시 삭제·Fetch 가로채기는 하지 않는다.
- Side 메타데이터와 CDP 응답 URL의 120초 양방향 매칭, 추가 HTTP 재다운로드 금지, 실제 MIME 확장자 저장 기준은 유지한다.
- 정적 회귀는 pytest 96, IG Node 41, JS syntax 51, Python compileall OK다.
- 실제 Windows CI-00~CI-06, yjm_pmonit `_StDown` 생성, SQLite 등록 전까지 이미지 저장 기능은 `[검증대기]`다.


## 패키지 v294 현재 기준
- 신규 `Cache8701`은 포트 8701에서 Side의 계정·shortcode·이미지 URL·통계를 수신한다.
- Auto8700이 실행한 Chrome CDP 9222의 Instagram target에 독립 연결해 `Network.responseReceived`·`loadingFinished`·`getResponseBody`를 사용한다.
- Side URL과 CDP 응답 URL은 120초 양방향 대기표로 매칭하며, 별도 HTTP 재다운로드 fallback은 사용하지 않는다.
- JPEG·WebP·AVIF·PNG를 실제 형식으로 `D:\_StDown\instagram\{account}`에 저장하고 Shared DB 등록을 시도한다.
- Auto8700은 Cache8701의 실행·재시작만 관리하고 자동실행 역할은 유지한다. WS8771·Analyze8780 역할은 변경하지 않는다.
- 정적 테스트 통과 후에도 실제 Windows의 CI-01~CI-06 로그와 yjm_pmonit 파일 생성 기록 전까지 `[검증대기]`다.


## 패키지 v293 현재 기준
- 현재 IG 확장 버전은 manifest `2.3.8.2930 / 2.3.8.293`, Side·탭 제어·MAIN marker `v293`이다.
- Side 톱니바퀴 설정창에 `자동 섬네일` 탭을 표시한다.
- 자동 섬네일 기본값은 전체 사용 ON, Explorer·검색 OFF, 단일 게시물·계정 프로필·계정 릴스 ON이다.
- 전체 사용 OFF이면 페이지별 체크는 비활성화한다.
- Save 시 `chrome.storage.local`의 평면 키와 `ig_auto8700_devtools_settings`를 함께 갱신하고 Background의 8707 이벤트 전달을 사용한다.
- 본문 `Auto8700 DevTools`에는 섬네일 체크박스를 중복 표시하지 않고 톱니바퀴 위치 안내만 남긴다.
- 실제 JPG 자동 저장은 사용자 로그상 아직 FAIL이며, 이번 v293 목표는 설정 UI 표시·저장·복원 경로다.

- 현재 Analyze8780 기준: v290 / health 2.3.8.290 / 실제 Windows 미디어·차트 검증대기

## Analyze8780·패키지 v288 현재 기준
- 단일 목표는 Analyze8780의 계정 raw LIKE 검색·DB 번호 선택·로컬 미디어 열기·게시물 개별 DB/통계/파일 삭제 마무리다.
- 왼쪽 `↻`, `0/200`을 제거하고 계정 제목 우측에 `social_accounts · 검색 n / 전체 n`을 표시한다.
- `✓`는 현재 SQLite `PRAGMA quick_check`, 계정 테이블 존재, 전체/검색 개수를 확인한다.
- 검색어는 자동 변환 없이 `username LIKE ? COLLATE NOCASE`에 그대로 전달한다. `%`와 `_` 설명은 원형 `?` 클릭 팝업에만 표시한다.
- DB select 옆에 파일 수만큼 `1..N` 버튼을 만들고 한 번 클릭으로 활성 SQLite·select·검색 결과를 동기화한다.
- 저장 폴더는 검증된 저장 루트 내부 경로만 탐색기로 열고, MP4/JPG는 localhost inline URL로 열며 Range 재생을 지원한다.
- 게시물 개별 삭제는 posts와 FK 통계·media_assets 및 해당 실제 파일을 함께 삭제한다. 다른 게시물이 같은 경로를 참조하면 삭제하지 않는다.
- 계정 전체 삭제 버튼은 UI에서 제거하며 기존 계정 API 호환은 유지한다.
- 게시물 `50/100/200/10000`, 이미지 세로 영역, ApexCharts 최근 50개·shared tooltip은 유지한다.
- manifest·표시·Analyze health는 `2.3.8.2880 / 2.3.8.288 / v288 / 2.3.8.288`이다. Auto8700·WS8771 동작 코드는 변경하지 않는다.
- 코드 검증은 JSON 5, JavaScript syntax 47, Analyze UI 1, IG 37, RTC JS 3, Python compile 33, pytest 82를 기준으로 한다.
- 실제 Windows 번호 DB 전환·`tem%` 즉시 검색·✓·탐색기·MP4/JPG·삭제·차트 hover는 `[검증대기]`다.


## Analyze8780·패키지 v287 이전 기준
- 이번 핵심 목표는 Analyze8780의 DB 선택·전체 계정 검색·게시물 페이징·즉시 추이 차트 UI다.
- 상단 DB select는 `GET /api/database-files` 목록을 표시하고 선택 시 `POST /api/database/open`으로 활성 DB를 전환한 뒤 계정·게시물을 다시 조회한다.
- 왼쪽 계정은 페이지를 제거하고 최대 10,000개를 한 번에 읽어 스크롤 목록으로 표시한다. 검색은 `abc` 정확히, `abc%` 시작, `%abc` 끝, `%abc%` 포함 규칙이며 안내는 원형 `?` 버튼에 숨긴다.
- 오른쪽 게시물은 보기 개수 `50/100/200/10000`을 선택하고 선택 개수 기준으로 페이지를 계산한다. 정렬은 조회수·좋아요·댓글·공유/리포스트·증가량·게시일 모두 큰 값 또는 최신값부터 표시한다.
- 이미지 영역은 세로 높이를 확대하고 `object-fit:contain`으로 원본 비율을 유지한다. 카드마다 최근 통계 최대 50개를 ApexCharts CDN 선 차트로 자동 표시하며 조회수·좋아요·댓글·공유/리포스트를 색상별로 구분하고 shared hover tooltip을 사용한다.
- 게시물 수가 많을 때는 카드 DOM은 선택한 페이지 크기만 만들고 차트는 화면 근처에 들어오면 클릭 없이 자동 로드한다.
- Shared 조회 page size 상한은 10,000으로 확장했으며 기본값과 기존 내림차순 쿼리는 유지한다.
- 패키지 manifest·IG 표시 버전은 `2.3.8.2870 / 2.3.8.287 / v287`, Analyze8780 health는 `2.3.8.287`이다. Auto8700 기능 코드는 v286 기준을 유지한다.
- 최종 정적 결과: JavaScript syntax 50 OK, Analyze UI Node 1 OK, IG test 38 OK, RTC JS test 3 OK, Python compile 33 OK, pytest 80 passed.
- 실제 Windows 8780에서 DB 전환·왼쪽 전체 검색·50/100/200/10000 페이징·ApexCharts CDN/hover·대량 카드 성능은 `[검증대기]`다.


## IG·Auto v286 자동 새 탭·CDP 강제 종료 현재 기준
- 기준본은 자동 Top을 WS8771 direct로 저장하고 수동 다운로드를 Chrome/WS 경로로 분리한 v282다.
- 기존 v282 MAIN 통합 UI와 SIDE `1부터` 버튼을 유지한다.
- 자동실행을 새로 시작하거나 저장 진행상태로 재개할 때는 현재 Explore/프로필 탭을 작업 탭으로 재사용하지 않고 자동 전용 새 탭을 만든다.
- 수집계정 index가 `0/N`이면 기존 Explore snapshot을 처리하지 않고 목록의 1번 계정을 새 자동 탭에서 시작한다.
- 다음 계정도 새 관리 탭을 먼저 준비하고 Side가 OWNER를 확정한 뒤 이전 자동생성 탭을 종료한다.
- 이전·오류·마지막 자동 탭은 저장된 exact CDP `targetId`로 Auto8700 `Target.closeTarget`을 호출하고 target 제거와 Chrome tab 제거를 확인한다. 1차 실패 시 한 번 재시도하고, CDP 실패 또는 targetId 누락 때만 `chrome.tabs.remove`를 보조 수단으로 사용한다.
- 종료 대상은 `auto_created=true`로 기록된 탭뿐이다. 사용자가 직접 열어 둔 Instagram 탭은 보존하고 CLOSED 이력으로 기록하지 않는다.
- 자동 중지·오류에서도 현재 자동생성 탭을 강제 종료하며, 마지막 계정 자동종료 뒤 마지막 자동 탭도 닫는다.
- v284 같은 탭 직접 URL 이동과 v285 Instagram 검색 이동 실험은 기준에서 제외한다.
- 실행 버전 `2.3.8.2860`, 표시 버전 `2.3.8.286`, Auto8700 build `v286`.
- 정적 결과: 전체 JavaScript syntax 49 OK, IG test 38 OK, RTC JS test 3 OK, Python compile 33 OK, pytest 77 passed.
- 2026-08-04 실제 Windows에서 2계정 fresh tab, 이전·마지막 target 제거, 자동 WS 12/12 파일과 자동종료는 OK다. 오류 탭 의도 재현과 수동 다운로드 회귀는 `[검증대기]`다.

## IG v282 MAIN 진단 통합·SIDE 1부터 현재 기준
- MAIN 우상단의 별도 CDP, 탭, DOM 진단복사, MAIN 눈 표시를 화면에서 숨기고 `CDP:O · 탭 N · OWNER tab 000000` 한 줄로 통합한다.
- 통합 상태줄 클릭은 전체 CDP·OWNER·현재 탭·최근 20개 이력을 클립보드에 복사한다. 긴 진단 툴팁은 사용하지 않는다.
- `_`는 통합 상태줄 축소·복원을 토글하고 `□`는 상세 패널을 연다. 축소 상태는 `localStorage`에 유지한다.
- 상세 패널은 열 때 한 번, `새로고침` 클릭 때 한 번만 렌더링한다. 700ms 반복 `textContent` 갱신은 제거해 드래그 선택과 `Ctrl+C`가 풀리지 않게 한다.
- 상세 패널 내부에는 `새로고침`, `전체 복사`만 둔다.
- SIDE 수집계정 헤더에 `1부터` 버튼을 추가한다. 목록이 있고 현재 index가 0이며 자동실행이 정지 상태일 때만 활성화한다.
- `1부터` 클릭은 summary 축소·확장을 건드리지 않고 실제 계정 시작 index를 1로 저장한다. index가 1 이상이거나 자동실행 중이면 비활성화한다.
- 실행 버전 `2.3.8.2820`, 표시 버전 `2.3.8.282`.
- 정적 결과: JavaScript syntax 48 OK, IG test 37 OK, Python compile 33 OK, pytest 74 passed.
- 실제 Chrome의 통합 표시 위치·복사·축소/복원·정적 패널 선택 유지·SIDE 1부터 버튼은 `[검증대기]`다.

## IG v281 CDP 수명주기 선삽입 이전 기준
- YELLOW 관리 replacement 탭은 `http://127.0.0.1:8707/cdp-bootstrap?token=...` 빈 페이지로 먼저 생성한다.
- Background가 Auto8700 `/api/cdp/ig/install_lifecycle_preload`를 호출하면 Auto8700이 해당 CDP target을 정확한 bootstrap URL로 찾는다.
- Auto8700은 `Page.addScriptToEvaluateOnNewDocument`를 먼저 등록하고 현재 빈 문서에도 `Runtime.evaluate`로 같은 코드를 설치한 뒤 Instagram URL 이동을 허용한다.
- 선삽입 코드는 `window.addEventListener("unload", ...)`와 `window.onunload = ...`만 차단한다. `beforeunload`, `pagehide`, `visibilitychange`는 차단하지 않고 등록·실행 시각·source URL·stack을 기록한다.
- 페이지 콘솔은 `[YELLOW][CDP-LIFECYCLE]`, Auto8700·Background 콘솔은 `[CDP수명주기][CL-01~04]` 형식을 사용한다.
- 페이지 수명주기 이벤트는 Content bridge를 거쳐 Background `chrome.storage.local`의 `YELLOW/cdp_lifecycle_diag_v281` 최근 50개에도 저장한다.
- CDP 선삽입 실패 시 실패를 숨기지 않고 `[체크실패]`를 기록한 뒤 기존 새 탭 경로로 계속한다.
- 실행 버전 `2.3.8.2810`, 표시 버전 `2.3.8.281`.
- 정적 결과: JavaScript syntax 47 OK, IG test 36 OK, Python compile 33 OK, pytest 74 passed, 생성 CDP JavaScript 문법·행동 모형 OK.
- 실제 Chrome에서 unload 차단 로그·3계정 연속 자동실행·먹통 재발 여부는 `[검증대기]`다.

## IG v280 이전 기준
- 자동 탭 제어는 현재 기록 OWNER → 같은 `run_seq`의 높은 generation `READY/OWNER` → 다른 관리 `READY/OWNER` → 활성 Instagram → 큰 `tab_id` 보조 기준으로 선택한다.
- 최근 탭 이력은 `chrome.storage.local`에 최대 20개를 유지하며 `CREATED/READY/OWNER/RETIRED/CLOSED/FAILED` 수명 상태와 인계 이벤트를 분리한다.
- 새 탭은 생성·Content/Main ready·활성화까지만 Background가 수행한다. 이때 이전 탭은 닫지 않고 `handoff_token`을 Side에 반환한다.
- Side가 새 `tab_id`를 세션 OWNER로 갱신한 뒤 `IGBLUE_COMMIT_TAB_HANDOFF`를 보내면 Background가 OWNER를 확정하고 이전 탭을 닫는다.
- Background 서비스 워커가 인계 사이에 재시작해도 저장된 pending token과 이벤트 수명 상태를 `READY/OWNER/RETIRED/CLOSED/FAILED`로 복원한다.
- Main 우상단 `igblue_main_diag_toggle`은 마우스 진단 대신 `탭 N · OWNER`, 인계중, OWNER 없음 상태를 표시한다. 상세 패널과 DOM 진단복사에는 OWNER·generation·run_seq·최근 20개 이력을 포함한다.
- 실행 버전 `2.3.8.2800`, 표시 버전 `2.3.8.280`.
- v279c WS8771 direct MP4/JPG 저장 경로는 변경하지 않는다.
- 정적 결과: JavaScript syntax 46 OK, IG test 35 OK, Python compile 33 OK, pytest 71 passed.
- 실제 Chrome의 CDP:X→O 복구·다중 탭 OWNER 인계는 `[검증대기]`다.


## IG v279d 현재 기준
- 자동 계정 이동은 기존 Instagram 탭의 URL을 `chrome.tabs.update()`로 교체하지 않는다.
- 현재 자동 대상 탭과 같은 창에 새 Instagram 탭을 `active:false`로 생성하고 새 `tab_id`를 확보한다.
- 새 탭의 목표 URL, 탭 `complete`, Content 응답, MAIN ready marker가 모두 확인된 뒤 새 탭을 활성화한다.
- 새 탭 활성화 후에만 이전 자동 대상 탭에 `unload` 전용 종료 guard를 제한적으로 시도하고 이전 탭을 닫는다.
- 이전 탭 guard가 실패하거나 900ms 안에 응답하지 않아도 새 탭 준비가 완료된 상태이므로 이전 탭 종료를 계속한다.
- 새 탭 생성·준비·활성화가 실패하면 새 탭만 제거하고 이전 탭을 유지·재활성화한다.
- MAIN 문서 전체 수명에 적용하던 document_start unload monkey patch는 제거했다. 현재 탭 클릭·일반 이벤트에 지속 후킹을 남기지 않는다.
- 실제 자동 MP4/JPG 저장은 사용자 검증이 끝난 v279c WS8771 direct 경로를 그대로 유지한다.
- 실행 버전 `2.3.8.27910`, 표시 버전 `2.3.8.279d`.
- 정적 결과: JavaScript syntax 45 OK, IG test 34 OK, Python compile 33 OK, pytest 71 passed.
- 실제 Chrome CDP:O에서 2계정·3계정, 3계정×Top3, 파일 18/18, 자동종료가 확인돼 `[완료] / OK`다.
## IG v279ccc 현재 기준
- 기준본은 사용자가 실제 Chrome에서 자동 MP4/JPG 저장을 확인한 v279c와 URL 이동 진단을 추가한 v279cc이다.
- 첫 계정과 다음 계정 모두 URL 변경 전에 현재 탭 MAIN world에서 `unload` guard 설치를 시도한다.
- 페이지 콘솔에는 `CALL_BEFORE_URL`, `INSTALL_OK/REUSED`, `BLOCKED_ADD/BLOCKED_PROPERTY`가 기록되고 Background/Side 콘솔에는 `GUARD_REQUEST`, `URL_UPDATE_BEFORE`, `URL_UPDATE_CALLED`, `READY`가 기록된다.
- guard 설치 실패는 `GUARD_FAILED_CONTINUE`로 기록하되 URL 이동 자체를 막지 않는다.
- `instagram_inject.js`는 `document_start`에서 guard를 설치하고 전체 MAIN 스크립트 실행이 끝난 뒤 `data-yellow-main-injection-ready=v279ccc` marker를 남긴다.
- URL 이동 준비 확인은 `#IGBLUE_NoTop` 화면 생성만 기다리지 않고 목표 URL·탭 complete·Content 응답·MAIN ready marker를 확인한다.
- 다운로드 상태의 `No---` 강제 표시는 제거했다. No가 실제 있을 때만 표시한다.
- 자동 진행 막대는 개별 파일 완료 100%가 아니라 전체 계정×Top 목표의 완료 게시물 비율을 사용하고 자동종료에서만 100%가 된다. 발견된 실제 자산은 `파일 완료/예정`으로 별도 표시한다.
- 종료예상은 최근 성공 세트 20개의 전체 처리시간 이동평균과 설정 대기시간을 사용한다. 표본이 없으면 `계산중`, 완료 후에는 `00시간 00분 00초` 형식으로 표시한다.
- 실행 버전 `2.3.8.2799`, 표시 버전 `2.3.8.279ccc`.
- 정적 결과: JavaScript syntax 44 OK, IG test 33 OK, Python compile 33 OK, pytest 71 passed.
- 정적 회귀는 OK이며 실제 Chrome 첫 URL·다음 URL guard 로그, 자동 재개, 진행률·ETA는 `[검증대기]`다.

## IG v279c 현재 기준
- 실제 자동 다운로드 정상 기준은 사용자가 2026-08-02 CDP:O에서 재확인한 v262이다.
- v262의 `teAutoPyDownloadableKindsFromCache`, `teAutoPyDownloadMediaPairFromCache`, `teAutoPyDirectDownloadAsset`, `teAutoPyDownloadMediaPairDirect` 자동 실행부를 v279bb 기준본에 이식했다.
- 자동 Top은 저장 선택이나 `metadata-only`를 완료 자산으로 사용하지 않고 snapshot에 있는 MP4/JPG 원본 URL을 WS8771로 직접 저장한다.
- 실제 MP4/JPG 저장 완료 또는 실제 중복 파일·JSON·보고서 확인이 있어야 게시물 완료 수를 증가시킨다.
- 수동·통계용 metadata 수집, Side 계정 클릭 선택, MAIN hover 비활성, v279b 다운로드 상태 보존은 유지한다.
- 실행 버전 `2.3.8.2797`, 표시 버전 `2.3.8.279c`.
- 실제 Chrome v279c 자동 실행은 사용자가 두 차례 연속 MP4/JPG 실제 저장과 WS8771 direct 완료를 확인해 `[완료]`다.
- `IGBLUE_` → `YELLOW` 전체 변경은 안정화 완료 후 별도 작업으로 보류한다.

## IG v279bb 현재 기준
- v279b의 다운로드 상태 보존 기능을 유지한다.
- MAIN hover 진단 별도 IIFE는 첫 MAIN IIFE의 지역변수를 직접 참조하지 않고 `window.__IGBLUE_MAIN_HOVER_DIAGNOSTIC_ENABLED__` 전역 상태만 안전하게 검사한다.
- hover가 비활성 또는 전역값 미정의여도 `ReferenceError` 없이 뒤쪽 코드가 계속 실행돼 자동 Top 다운로드 초기화가 완료돼야 한다.
- 당시 v279aaaa 비교만으로 자동 복구를 판단했으나 실제 v279bb에서 MP4/JPG 0개가 확인돼 v279c에서 v262 기준으로 교체했다.
- 실행 버전 `2.3.8.2796`, 표시 버전 `2.3.8.279bb`.
- 전체 `IGBLUE_` → `YELLOW` 변경은 최종 안정화 후 별도 작업으로 보류한다.

## IG v279b 현재 기준
- 실제 다운로드가 시작되기 전에는 다운로드 상태 영역을 새 값으로 초기화하거나 `No---`, `대기중`, `auto_account_continue`로 덮어쓰지 않는다.
- 이전 다운로드 표시가 있으면 다음 실제 `ndy_down_progress status=start`가 도착할 때까지 그대로 보존한다.
- 이전 표시가 없으면 다운로드 시작 전 상태 문구를 만들지 않는다.
- 실제 다운로드 시작 이벤트가 도착한 순간에만 이전 파일·shortcode·asset·경로·진행률을 초기화하고 새 다운로드 표시를 시작한다.
- 실제 `done/already_done/error` 결과는 다음 다운로드 시작 전까지 보존하며 자동종료에서는 마지막 실제 파일 상세를 유지한다.
- 실행 버전 `2.3.8.2795`, 표시 버전 `2.3.8.279b`.

### 2026-08-02 사용자 로그 확인
- 11:31 실행은 3개 계정에서 각 2개 게시물의 `AD-06 파일 저장·JSON·보고서 완료`가 기록됐다.
- 11:33 실행은 `dukong_meal` 2개와 `seoyoung_644` 2개의 완료가 기록됐다.
- 중간 `housej.jihye` 실행에는 `AS-03 2/3` 이후 `AD-02`가 없고 다음 계정으로 넘어갔다. 제공 로그에는 후보 없음·기간 종료 등 구체 사유가 남지 않아 다운로드 실패로 단정할 수 없다.
- Auto8700·WS8771 에러 로그에는 시작 헤더 외 실제 오류가 없다.
## IG v279aaaa 이전 기준
- v279aaa에서 Auto8700 ON/OFF Main JPG·MP4 수동 다운로드와 unload 오류 해소를 실제 확인했다.
- MAIN hover 계통은 비활성 상태를 유지한다.
- 수집계정 추가 대상은 Side 카드의 `IG 계정명` 클릭으로만 선택하며 400ms 자동 확인과 Main 계정 응답 조회는 제거했다.
- 실행 버전 `2.3.8.2794`, 표시 버전 `2.3.8.279aaaa`.

## IG v279aaa 진단 결과(완료)
- v279aa 비다운로드·다운로드 기능 유지.
- MAIN hover 계정 선택·200ms 확정·버튼 hover 색상/좌표 기록만 비활성.
- MAIN hover 비활성 후 Auto8700 ON/OFF에서 Main JPG·MP4 저장과 unload 오류 해소를 실제 확인.

## v279aa 기반 유지 기능
- 기존 v279 비다운로드 기능 유지
- 수동/Filtered는 WS 미연결 시 Background Chrome 다운로드 fallback
- 자동 Top은 v262 WS8771 MP4/JPG 직접 저장 경로를 사용하고 metadata-only 완료는 금지
- Auto8700 미실행 안내가 CDP 옵션보다 우선

# sort 현재 기준서 — 패키지 v314

전체 진행률: 36% (`test_list.md` 완료 61 / 전체 170)
문서 체계: 100%
마우스 제어 폐기: 100%
공통 MEDIA_SAVED 로컬 처리: 100%
SQLite 기본·다운로드 구조: 100%
1단계-A 로컬 분석 기반·자동실행·명시저장: 100%
IG 현재 페이지 observation 연결: 0% 완료 (`코드반영·실행로그 검증대기`)
중앙 1시간 통계 이력 송수신: 0% 완료 (`코드반영·운영서버 검증대기`)
Ext 대량 게시물 메모리 보호: 0% 완료 (`코드반영·전체 카드 DOM 고정·화면 밖 이미지 src/srcset 해제·실제 Chrome 128~800개 검증대기`)
로컬 10분 통계 변경분 저장: 0% 완료 (`코드반영·실제 SQLite 장시간 검증대기`)
동일 1시간 신규 통계 증분 전송: 0% 완료 (`코드반영·실제 WS 로그 검증대기`)
PHP/MySQL 시간 이력 집계: 0% 완료 (`코드반영·운영서버 검증대기`)
수집자·그룹·연구 기능: 0%
P2P·포인트: 0%

## 0. 최종 목적 — Instagram 크리에이터 도우미

- 이 프로젝트의 핵심은 단순 다운로드가 아니라, Instagram 기본 화면이 제공하지 않는 공개 게시물 순위·성과·변화·비교 정보를 인젝션으로 가공해 보여주는 크리에이터 도우미다.
- 대박이 난 영상과 성장 가능성이 높은 콘텐츠를 기간·조회수·좋아요·댓글·리포스트·참여점수 기준으로 빠르게 찾게 한다.
- Instagram에는 더 좋은 콘텐츠와 활동이 늘고, 크리에이터는 성과를 이해해 성장하며, 사용자는 재미있고 유익한 콘텐츠를 쉽게 찾는 상생 구조를 목표로 한다.
- 사용자가 많아지고 공개 관측 데이터가 쌓일수록 더 빠르고 세밀한 정보를 제공하되, 공개 정보·요청 절제·출처와 수집시각 보존·실패의 명확한 표시 원칙을 지킨다.
- 다운로드는 필요한 원본을 보관하는 보조 기능이다. 통계 수집과 분석은 다운로드 여부와 독립적으로 동작해야 한다.
- IG 핵심 기능이 실제 로그로 안정화된 뒤 TikTok 도우미를 `TT_*` 독립 모듈로 추가한다. Instagram과 TikTok 전용 DOM·API 코드는 섞지 않고 공통 저장·통계·로그·분석만 `Shared`에서 공유한다.

최종 흐름:

```text
수집 계정 선택
→ 현재 Instagram 페이지 공개 게시물만 인식
→ 기간·정렬·최소 조건 적용
→ 성과 게시물과 수집 당시 순위 표시
→ 로컬 10분 통계 저장
→ 선택 시 서버 1시간 통계 연동
→ 필요한 미디어만 WS8771 단일 경로로 저장
→ 계정 HTML·Analyze8780에서 다시 조회·비교
```

IG 핵심 완료는 다계정 전환 혼입 없음, 조건·순위·통계 정확성, WS 단일 다운로드, JSON·HTML·SQLite·MySQL 일치, 중단·재개·대상 0개 처리, 실제 로그와 `test_*` OK가 모두 확인됐을 때 선언한다.

## 1. 절대 원칙

- 자동·수동 다운로드는 OS 마우스, 원점, hover 검증을 사용하지 않는다. 자동 Top은 WS8771 저장 경로를 사용한다. 수동·Filtered는 WS 연결 시 로컬 저장 경로를 사용하고 WS 미연결 시 Background Chrome 다운로드 fallback을 사용한다.
- 다운로드 시작 방식은 유지하되 파일 저장 완료 이후에는 공통 `MEDIA_SAVED` 흐름을 사용한다.
- 통계 수집은 다운로드 여부와 분리한다. 파일을 받지 않은 게시물도 계정·게시물·통계 저장 대상이다.
- Injection의 수동 Image/Video 다운로드는 현재 shortcode의 전체 수집 객체를 공통 메타 규격으로 전달한다. shortcode·asset_kind·media_index가 같은 파일·JSON·SQLite·서버 항목에 조회수·좋아요·댓글·리포스트·게시시각·썸네일·caption을 병합한다.
- 다운로드 성공 표시는 한글 문구 대신 해당 shortcode·미디어 종류 버튼 하나의 재생 아이콘을 `✅`로 교체한다. 성공 응답에 shortcode가 없으면 완료 표시하지 않는다.
- 버전은 실제 수정된 모듈만 변경한다. 관련 없는 파일과 테스트의 버전 문자열만 바꾸지 않는다.
- 완료된 일회성 테스트는 누적하지 않고 현재 기능을 보호하는 고정 이름 최소 회귀 테스트만 유지한다.
- `test_*`는 코드 문법·정적·단위 회귀 검증이다. 실제 기능 완료는 사용자가 전달한 Windows·Chrome·운영 MySQL 실행 로그로 확인하며, 그 로그가 없으면 `[코드반영]` 또는 `[검증대기]`로 둔다.
- 삭제는 우선 soft-delete로 처리한다. 계정·게시물을 분석과 수집 목록에서 제외하지만 MP4/JPG 실제 파일은 자동 삭제하지 않는다.
- 서버에는 MP4/JPG 원본과 사용자 로컬 절대경로·상대경로를 저장하지 않는다.
- 실패·불확실 상태를 성공으로 기록하지 않는다.
- Top sorted 자동 완료는 후보 배열의 마지막 No가 아니라 **WS8771 저장·JSON·보고서 완료가 확인된 고유 shortcode 수가 Top목표에 도달했을 때**만 성립한다. 목표 미달이면 swipe를 계속한다. 기간 필터는 새로 로딩된 알려진 날짜가 모두 범위 밖일 때, All time은 게시물 처음과 추가 로딩 없음이 확인될 때만 `더이상없음`으로 판정하고 있는 대상만 처리한 뒤 `자동종료`를 표시한다.
- Top sorted 자동은 Top목표 전체 후보를 먼저 모으지 않는다. 아직 완료하지 않은 후보 snapshot의 shortcode·owner·원본 URL이 준비되면 그 후보를 즉시 다운로드하고, 저장 완료 뒤 목표가 남으면 다음 후보를 찾기 위해 swipe를 계속한다. 후보 0개 상태는 날짜 범위나 게시물 끝 근거가 생길 때까지 탐색하며, 끝 근거가 생기면 현재 완료 수를 유지하고 다음 계정 URL로 이동한다.
- 조회수 비공개·미제공은 내부 후보·메타에서 `views=-1`, `views_available=0`으로 구분한다. 조회수 0은 공개된 실제 0이며 비공개와 합치지 않는다.
- 조회수 기준이 활성인데 조회수가 비공개이면 좋아요·댓글·리포스트 중 설정값이 1개 이상일 때만 후보가 된다. 활성 조건은 모두 AND로 통과해야 하며 순위 점수는 활성 지표 원값의 **단순합산**이다. 입력값 0인 지표와 공유 수는 합산하지 않는다.
- 자동실행은 Main 시각 No DOM을 요구하지 않는다. 시작 시 만든 불변 snapshot의 `No/shortcode/owner/원본 URL`을 내부 Current 대상으로 복원할 수 있다.
- 자동 다운로드는 Main active 대상이 정확하면 `AD-03 [체크완료]` 뒤 진행한다. Main active rank·shortcode가 순간적으로 비어도 불변 snapshot의 요청 No·shortcode·owner·선택 자산 직접 URL이 정확하면 `AD-03 [복구완료]`로 기록하고 기존 WS8771 직접 다운로드를 계속한다. Main의 실제 shortcode가 snapshot과 다르거나 snapshot owner·URL이 부족하면 `AD-03 [체크실패]`로 중단한다. `[체크실패]`를 기록한 뒤 다운로드를 계속하는 방식은 금지한다.
- 계정 목록 자동 완료는 실제 목록 index가 n/n에 도달했을 때만 성립한다. `automatic_end` 상태만으로 n/n을 강제 표시하지 않는다. 완료 결과에는 n/n을 남기고, 다음 실행을 위한 시작 index만 완료 처리 뒤 0/n으로 초기화한다. 중간 이동 실패나 대상 0개는 n/n 도달 전 정상 완료로 처리하지 않는다.
- 인젝션 썸네일은 Base64로 변환·누적하지 않고 원본 URL만 유지한다. 전체 미디어 목록은 내용이 바뀌었을 때 또는 30초 keepalive 때만 전달하며 매초 전체 직렬화를 반복하지 않는다.
- Side는 전체 메타데이터·전체 순위와 현재 목록의 모든 `.post-item` DOM을 문서 순서대로 유지한다. 스크롤 중 카드 추가·삭제·행 높이 측정·spacer·`scrollBy` 보정을 실행하지 않는다. 썸네일 영역은 16:9로 고정하고 `IntersectionObserver` 범위 밖에서는 `img`의 `src`·`srcset`만 제거해 이미지 디코딩 메모리 회수 기회를 제공한다. 다시 화면 근처에 들어오면 보관 URL을 복원하며 `img_small` 실패 시 `img_origin`을 한 번 fallback한다. `content-visibility:auto`로 화면 밖 카드의 레이아웃·페인트 부담을 줄인다.
- Side 설정의 인젝션 표시 모드는 `detail / download / none` 3개를 유지한다. isolated content bridge가 storage·runtime 메시지를 MAIN world의 `ndy_ig_overlay_mode`로 전달해야 하며, `detail`은 통계+다운로드 버튼, `download`는 다운로드 버튼만, `none`은 해당 인젝션 카드 전체를 숨긴다. 설정 UI만 남고 전달 함수가 누락된 상태를 정상으로 보지 않는다.
- `Visual settings`의 `Use / Side / Main`은 서로 독립된 저장값이다. Metric underline bars와 Date border 모두 `main`을 `side` 값으로 덮어쓰거나 Side 체크값으로 Main을 저장하지 않는다. Side 렌더링은 `side`, Instagram Main 렌더링은 `main`을 검사한다.
- Visual settings 기본값은 막대 3px, 항목간 4px, 글자-바 2px, 항목높이 18px, 썸네일 200px, Views 1,000,000, Likes 100,000, Comments 10,000, Repost 10,000이다.
- 기존 `다운버튼 크게 표시` 설정은 폐기하고 같은 저장영역을 `다운로드 상태 강조`로 사용한다. 정확한 shortcode·asset_kind가 있는 실제 다운로드 시작·진행·성공·중복·부분·실패만 Main DOM 카드와 MP4/JPG 버튼에 표시하며 Glow·색상·테두리·radius·투명도·확대 설정을 적용한다. 실패를 성공 표시로 바꾸지 않는다.
- 다운로드 결과 상태는 background의 `tabs.sendMessage` → isolated content → MAIN world 순서로 전달한다. v279a 전송 core는 v262 방식이며 background에서 별도 `progressIdentity`를 합성하지 않는다.
- 수동 MP4/JPG 클릭 시작 표시는 Side/Main 클릭 지점에서 한 번만 발생시킨다. v279a content relay는 중복 `manual_click/start` 이벤트를 추가하지 않고 v262 방식으로 background에 전달한다.
- v279a에서는 v276 응답 없음 진단을 폐기한다. `IGBLUE/runtime_diag_v276`, 10초 DOM·heap 표본, Long Task/V scan/Observer 누적, `pagehide` 표본 저장을 실행하지 않는다. 해당 코드를 정상 기능으로 복원하지 않는다.
- CDP는 JavaScript 로딩 조건이 아니라 자동·예약 실행 활성화 조건이다. CDP:X에서는 `자동실행 사용`·`시작`·예약 입력을 비활성화하고 “자동실행을 사용하려면 Auto8700 CDP를 실행하세요. 수동 다운로드는 사용할 수 있습니다.”를 표시한다. 수동 MP4/JPG 다운로드는 CDP와 무관하게 유지한다.
- Side의 `↗`는 자동실행 중에는 실행하지 않는다. 대기 상태에서는 popup이 기존 Instagram `tab_id`와 동일한 대상 연결을 확인한 뒤에만 기존 Side Panel을 닫는다. popup 생성·연결·tab 일치·Side close 실패 시 기존 Side를 유지한다.
- 수동·Filtered 다운로드 선택은 `Video 저장 / Image 저장`으로 분리한다. 선택하지 않은 파일은 직접링크·metadata를 보존하고 둘 다 해제하면 수동·통계 경로에서 metadata·caption·통계·퍼머링크·직접링크를 JSON·HTML·Analyze8780에 남긴다. 자동 Top은 이 선택과 metadata-only 정책을 사용하지 않고 v262처럼 snapshot의 MP4/JPG 원본 URL을 직접 저장한다. 상태는 `metadata_collected / video_requested / video_downloaded / image_requested / image_downloaded`로 구분한다.
- v278은 v262 실동작 로그에서 확인된 snapshot/Side 직접 URL 다운로드 회복 경로를 복원한다. `allow_cache_fallback:false`로 자동 경로를 전면 차단하지 않으며, 요청 No와 snapshot shortcode가 정확히 일치하고 owner·선택 자산 URL이 준비된 경우에만 복구한다. 수동 다운로드 경로는 변경하지 않는다.
- v279 Auto8700은 `/json/version` 응답만으로 CDP 정상 판정을 하지 않는다. 8700 포트와 Instagram page target·webSocketDebuggerUrl을 구분해 `CDP:PORT`를 표시하고, 수동 `자동실행`·`CDP모드 실행`은 Instagram Runtime 응답을 확인한 뒤 기존 탭을 앞으로 표시한다. 탭이 없으면 `PUT /json/new` 또는 browser `Target.createTarget`으로 생성한다. 응답이 끊긴 경우 일반 Chrome은 건드리지 않고 Auto8700 전용 `cdp_user_data_dir` 프로세스만 종료·재실행한다.
- Side의 `탭 새로읽기`는 탭 새로고침과 분리한다. 현재 Instagram 탭의 Main Injection에 재읽기 요청을 보내 동일 payload 30초 전송 억제를 초기화하고 현재 DOM·캐시를 다시 스캔한다. 이 기능은 swipe·페이지 reload·계정 이동을 실행하지 않는다.
- 사용자 직접 중단은 진행상태 삭제와 분리한다. 중단 시 계정 queue index·현재 No·현재 계정 집계·전체 실행 집계·완료 shortcode를 보존한다. 다음 `시작`은 보존 상태가 있으면 이어서 실행하고, 상태가 없으면 0부터 시작한다. 처음부터 다시 실행할 때만 `진행상태 리셋`을 사용한다.
- 통계 수집 스냅샷은 처리 중 최신 1개만 유지한다. 로컬은 10분 구간이 바뀐 시점에 마지막 수치와 비교해 실제 조회수 제공 여부·조회수·좋아요·댓글·리포스트가 변경된 shortcode만 최대 100개씩 저장한다. Analyze8780·Shared 저장소도 마지막 정상 수치와 게시물·계정 메타가 모두 동일하면 계정·게시물·latest·history·10분 테이블에 아무 쓰기도 하지 않아 Side 재시작 후 중복과 불필요한 디스크 쓰기를 막는다. 메타만 변경된 경우에는 메타만 갱신하고 통계 이력은 추가하지 않는다. 중앙은 같은 1시간 신규·변경 shortcode를 최대 100개 단위로 처리하며 성공한 chunk만 완료 signature로 기록한다.
- 자동·수동 다운로드 전후에 Instagram 탭, Side Panel, Chrome 창, Chrome 프로세스를 닫지 않는다. Auto8700 종료 시 WS8771·RTC8790_8791은 종료하지만 Chrome/CDP와 Analyze8780은 유지한다.
- Instagram 다운로드는 IG가 원본 미디어 URL·owner·원본 파일명을 WS8771에 전달하고, WS가 `_StDown/instagram/{계정}/{원본파일명}`에 직접 기록한다. Chrome 다운로드 API·CDP download behavior·Save As·Enter는 주 흐름에서 사용하지 않는다.
- Analyze8780 `/api/health` HTTP 확인은 서비스가 열린 뒤 1회만 수행하고 기동 확인 뒤에는 8780 HTTP·TCP health를 반복하지 않고 프로세스 종료만 감시한다.
- Auto8700이 실행 중인 동안 WS8771·Analyze8780·RTC8790_8791 등 관리 서비스가 X 종료·정상 종료·비정상 종료·포트 down 상태가 되면 다시 실행한다. Auto8700 종료 시 Analyze8780은 상시 조회용으로 유지하고, Analyze8780 소스 변경 시에는 manager가 해당 서비스만 재시작한다.
- 수집계정 저장 표시는 `@account`로 통일하고 중복을 제거하되, 일반 `💾 저장`은 화면 순서를 그대로 보존한다. 새 계정은 목록 끝에 추가하며 `정렬` 버튼을 눌렀을 때만 username 오름차순으로 정렬한다. `🗑️ @account`는 삭제 tombstone이며 수집·카운팅·Instagram URL 생성·재시도에서 절대 제외한다.
- Side의 별도 `표시계정추가` 일괄 버튼은 제거한다. Side 카드의 `IG 계정명` 클릭은 기존 계정추가 버튼의 대상만 바꾸며, 실제 목록 추가와 SQLite 확정은 기존 계정추가·`💾 저장` 절차를 따른다.
- 수집계정 자동 시작은 `0/N`과 `1/N 이상`을 분리한다. `0/N`은 현재 웹페이지가 Instagram일 때만 시작하며, `1/N 이상`은 현재 페이지 종류와 관계없이 지정 index의 계정 URL을 background `tabs.update`로 먼저 연 뒤 content/Main 준비를 확인한다.
- `0/N`에서 현재 페이지가 Instagram이 아니면 자동 실행 상태·Main 대기 상태로 진입하지 않는다. "Instagram 페이지에서 시작을 눌러주세요"를 표시하고 시작은 사용 가능, 중지는 사용 불가인 대기 상태를 유지한다. `Goto Instagram` 새 탭을 기존 자동 세션 tab_id로 간주하지 않는다.
- 사용자 중지 완료와 자동종료 완료는 `자동실행 사용` 설정을 끄지 않는다. 두 경우 모두 `시작`은 다시 누를 수 있고 `중지`는 누를 수 없는 대기 상태로 복귀한다. `stopping`은 WS 중지 응답이 없어도 제한 시간 뒤 해제하며 이전 실행의 늦은 응답이 새 실행 상태를 덮지 못하게 run sequence로 차단한다.
- 자동실행 대상 `tab_id`는 `다운로드 방식 3 · Top sorted 자동`의 개수 앞에 한 번만 표시한다. 유효한 자동실행 Instagram 탭이 있으면 숫자, 미확정·종료·무효 대상이면 `tab_id: -`로 표시한다. `자동실행 사용 / 시작 / 중지` 사이에는 넣지 않는다.
- 전체 주소 종료예상은 현재 계정 남은 Top목표와 이후 계정 수×Top목표를 합친 남은 게시물 수를 기준으로 한다. 성공한 게시물의 MP4+JPG 실제 소요시간을 `download/auto_eta_stats_v274` JSON에 누적하고 평균 게시물 다운로드 시간에 `다운 지연`을 더해 계산한다. 실패·중복·미실행 시간을 성공 평균으로 기록하지 않으며 전체 완료 시 00시간 00분으로 표시한다.
- 계정 URL이 Instagram의 명시적인 페이지 없음 문구와 동일 username URL로 확인될 때만 `🗑️` tombstone으로 저장한다. 로그인·challenge·비공개·네트워크·content/Main 지연은 삭제로 판정하지 않는다. tombstone 저장 뒤 같은 계정을 다시 열지 않고 축소된 활성목록의 같은 index에 있는 다음 URL로 진행한다.
- 수집계정 원본 SQLite 기본 파일은 `_St/Shared/data/sort_local.sqlite3`이다. Side에서 안전한 파일명으로 `파일열기 / 새 파일 / 다른이름 저장`을 수행하며 선택 파일명은 `_St/Shared/data/sort_database_selection.json`에 저장한다. 새 파일과 다른이름 저장은 기존 파일을 덮어쓰지 않는다.
- 선택된 SQLite 파일은 수집계정 목록뿐 아니라 Analyze8780 조회·통계와 WS8771의 공통 저장 경로가 함께 사용한다. DB 전환 시 Side 진행상태와 계정 index는 0으로 초기화하며, Analyze8780을 `--db` 명시 경로로 실행한 경우 런타임 파일 전환을 허용하지 않는다.
- Explore 게시물 팝업은 새 문서가 아니라 같은 SPA 문서 위의 전환이다. `/explore/ → /p|reel|reels/{code} → /explore/` 동안 전체 Explore 미디어 맵을 비우지 않는다. 상세 주소 하나만 carry하고 나머지 Explore 데이터를 삭제하는 방식은 금지한다.
- Explore 계정 hover는 0.2초 유지 후 확정한다. 포인터가 Side 버튼으로 이동하며 다른 썸네일을 잠깐 통과해도 기존 확정 계정을 유지하고, 다른 카드가 0.2초 유지됐을 때만 교체한다.
- Instagram 홈의 같은 `article + shortcode`에는 시각 인젝션 카드 하나만 유지한다. 현재 보이는 video가 같은 shortcode이면 `D_VIDEO_LINK`를 기준 카드로 사용하고 `H_IMAGE_LINK` poster 경로는 차단한다. 서로 다른 article의 같은 shortcode를 전역 하나로 합치지 않으며 숨겨진 video 때문에 이미지 카드를 제거하지 않는다.
- 홈→Explore SPA 이동은 초기 HTML JSON에 의존하지 않는다. fetch clone과 `Response.json/text`에서 Instagram GraphQL·`/api/v1/` 응답을 수집하고 0~8초 재스캔한다. `INJ-01~INJ-05`는 localStorage 체크 로그와 `DOM 진단복사`의 `injection_check_log`에 남기며 `media_map_count`, `capture_counters`, 카드 수로 실패 단계를 구분한다.
- 현재 실제 확인 순서는 루트 `검증목록_v279.md` 01~61을 기준으로 한다. 로그·DB로 확인 가능한 항목과 사용자 화면 확인 항목을 구분하며 확인되지 않은 항목을 완료로 올리지 않는다.
- Side의 `No000` 순위 행·Rank badge·`순위` 설정 탭은 정상 기능으로 유지한다. Side 순위 설정은 Side에만 적용하며 Main 체크나 Main 순위 payload를 만들지 않는다.
- Main의 `No000` 순위 행·Rank badge·미디어 종류 순위 행·우측 `1/Prev/Next/마지막` 버튼은 제거한다. 자동실행은 화면 DOM 버튼을 클릭하지 않고 `IGBLUE/internal_no_nav_state`, 내부 Current/Next handler, shortcode 기반 `.ndy_ins_info[data-code]` 탐색만 사용한다.


## 1-1. 기능 체크형 로그 운영 절대 기준

### 목적

- 로그는 상세 실행문을 계속 쌓는 용도가 아니라, 기능별 체크리스트가 어디까지 통과했는지 확인하는 용도로 사용한다.
- 사용자가 해당 기능의 로그만 전달하면 다음 단계 진행 가능 여부와 실패 지점을 판단할 수 있어야 한다.
- 모든 기능명, 체크 항목, 결과와 오류 원인은 한국어를 우선한다.

### 허용 상태

```text
[체크시작]
[체크완료]
[체크실패]
[예외추적]
[복구완료]
```

- `[체크시작]`: 해당 기능 단계에 실제 진입했을 때 1회 기록한다.
- `[체크완료]`: 단계의 실제 결과가 확인됐을 때 1회 기록한다.
- `[체크실패]`: 다음 단계 진행 조건을 충족하지 못했을 때 기록한다. `blocking=true`이면 해당 실패에 의존하는 후속 단계만 중단하고, `blocking=false`이면 독립 단계는 계속하며 실패 항목을 별도 추적한다.
- `[예외추적]`: 기존 체크 항목만으로 원인이 확인되지 않을 때 해당 실패 항목 아래에 임시 세부 항목을 추가해 기록한다.
- `[복구완료]`: 실패 상태가 실제로 정상 복구됐을 때 기록한다.

### 로그 형식

```text
[기능명][체크ID][상태] 한국어 체크 내용 / 핵심 결과
```

예시:

```text
[자동다운로드][AD-01][체크시작] 자동 시작 상태 확인
[자동다운로드][AD-01][체크완료] 자동 시작 활성화
[자동다운로드][AD-02][체크완료] 대상=No003 계정=4eu_pick
[자동다운로드][AD-03][체크실패] 원본 미디어 URL 없음 / 다음점검=게시물 데이터 추출
```

### 중복 방지

- 같은 실행·같은 기능·같은 체크 ID·같은 결과는 한 번만 기록한다.
- 상태 또는 핵심 값이 바뀔 때만 다시 기록한다.
- 정상 health, 대기, 감시 결과를 초·주기 단위로 반복 기록하지 않는다.
- 동일 오류 반복은 최초 1회만 기록하고, 복구 또는 실행 종료 시 누적 횟수를 한 줄로 요약한다.
- 게시물·다운로드처럼 반복 작업은 `cycle_id`를 사용하되, 같은 cycle 안에서는 중복 기록하지 않는다.

### 단계 진행 기준

- 의존 관계가 있는 다음 체크는 이전 체크가 `[체크완료]`일 때만 실행한다. 독립 체크는 `blocking=false`로 계속할 수 있다.
- `[체크실패]` 이후 의존 단계를 성공처럼 기록하지 않으며, 독립 진행 결과와 실패 항목을 분리해 기록한다.
- 코드 존재 확인은 실제 정상동작 확인과 구분한다.
- 문서의 `[완료]`는 사용자가 전달한 실제 실행 로그에서 정상 동작이 확인된 뒤에만 사용한다. `test_* OK`만으로는 완료 처리하지 않는다.

### 문서 연결

- `ai_read.md`: 기능별 전체 체크리스트와 절대 진행 기준
- `ai_read_패치.md`: 현재 실패 체크 ID, 예외추적 항목, 다음 작업
- `ai_read_완료.md`: 실제 테스트·실행 로그로 완료된 체크 ID
- `ai_read_삭제.md`: 반복 로그, 폐기 체크, 다시 사용하면 안 되는 진단 방식
- `test_list.md`: 체크 ID와 테스트 ID의 대응, 현재 결과, 다음 작업

### 기능별 체크리스트 작성 기준

- 새 기능 또는 회귀 수정 전에 먼저 한국어 체크 ID 목록을 정의한다.
- 처음부터 과도한 세부 로그를 넣지 않는다.
- 실패가 발생한 항목에만 `-E01`, `-E02` 형식의 예외추적 체크를 추가한다.
- 예외 원인이 해결되면 임시 추적 로그의 유지 필요성을 검토하고, 불필요하면 제거한다.


## 1-2. 프로젝트 진행로그·상세로그 연결 기준

- 프로젝트 진행로그 파일명은 `ws_project_progress_log_YYYYMMDD_HHMMSS_pidNNNN.log`로 한다.
- 프로젝트 진행로그만 보고 기능별 성공·실패·다음 단계 진행 가능 여부를 판단할 수 있어야 한다.
- 기존 `ws_run_log_*`는 상세 실행 근거로 유지하되 프로젝트 진행로그와 같은 내용을 반복 복사하지 않는다.
- `[체크실패]` 발생 시 기존 상세 실행로그에 `*** 상세로그 *** {검색키워드}` 표식을 남긴다.
- 프로젝트 진행로그에는 상세 실행로그 전체 경로, 검색키워드, 실패 발췌로그 경로를 기록한다.
- 실패 발췌로그 파일명은 `ws_failure_log_YYYYMMDD_HHMMSS_{체크ID}_{검색키워드}.log`로 한다.
- 실패 발췌로그에는 해당 실패 판정과 관련 상세 항목만 기록하고 전체 실행로그를 복사하지 않는다.
- 같은 cycle에서 후속 파일 저장이 성공하면 이전 중간 실패를 `[복구완료]`로 기록한다.
- 서버 등록 실패처럼 로컬 저장을 막지 않는 항목은 `다음단계=로컬처리계속·실패항목별도추적`으로 기록한다.
- 실행로그·에러로그·프로젝트 진행로그·실패로그는 각 접두사별 최근 10개만 유지한다.
- 로그 줄 번호로 연결하지 않고 실행ID·체크ID·시간·검색키워드로 연결한다.

## 2. 현재 실행 구조

- `Auto8700`: 실행 버전 `2.3.8.250`; UI를 main thread에서 생성하고 시작 위치를 주 모니터 안으로 보정한다. 종료 시 WS8771·RTC8790_8791은 종료하고 Chrome/CDP·Analyze8780은 유지한다.
- `IG_StYellow`: 실행 버전 `2.3.8.275`; `자동실행 사용 / 시작 / 중지` 한국어 UI, 저장값 없음일 때 자동실행 기본 사용, 방식 3 개수 앞 `tab_id`, 설정 톱니바퀴 우측 정렬, Side 카드 IG 계정 클릭 대상 선택, 수동 MP4/JPG 클릭 즉시 시작 강조를 반영한다. v274 자동실행 제어·전체 ETA, v273 계정목록·SQLite 전환, v272 Visual settings·다운로드 상태 강조를 유지한다.
- `WS8771`: 실행 버전 `2.3.8.250`; Side의 `보고서 재생성` 요청을 처리하고 계정 HTML에 Analyze8780 링크를 보장한다. 계정별 UTC 1시간 bucket을 한 batch로 `media_stat_hour_upsert`에 원자 저장한 뒤 같은 bucket을 `media_stat_hour_list`로 수신한다. 요청 개수와 saved_count가 다르거나 수신이 실패하면 전체 실패로 반환하며 시간 완료키를 기록하지 않는다. 공유값은 항상 0이다. 기존 원본 URL을 `_StDown/instagram/{계정}`에 직접 저장하고 해시·JSON·ECharts·Shared `MEDIA_SAVED`까지 완료한다. 정적 계정 HTML/index.htm 보고서 v4는 게시 시각을 표시하고 320px 정사각 썸네일과 Analyze8780 `전체보기` 링크를 제공한다. 별도 프로젝트 진행로그에 AD 체크 결과를 기록하고, 실패 시 기존 실행로그 검색 표식과 실패 발췌로그를 연결한다. Save As 감지·Enter는 보조 진단 기능
- `Analyze8780`: 실행 버전 `2.3.8.273`; 기존 observation·계정·게시물 API와 UI를 유지하면서 활성 SQLite 파일 목록·열기·새 파일·다른이름 저장 API를 제공한다. 기본 실행은 Shared 선택 DB를 사용하며 `--db` 고정 실행은 런타임 전환을 금지한다.
- `Shared/storage`: SQLite 스키마 v7. 기본 `sort_local.sqlite3`와 선택 파일명 JSON을 유지하고, 안전한 파일명 기준의 open/new/save-as를 제공한다. Analyze8780과 WS8771은 같은 선택 DB를 사용한다. Side 수집계정 순서·삭제 우선, views_available와 동일 통계 생략, MySQL v47 기준은 유지한다.
- `RTC8790_8791`: 기존 v141 유지
- `PHP Yellow API`: `/var/www/html/i`, API v47.0.0; `media_stat_hour_upsert`는 최대 500개를 transaction으로 master+1시간 이력에 저장하고 `media_stat_hour_list`는 동일 shortcode/hour의 서버 전체 최신 관측 1건만 반환한다; `schema_check`의 테이블 확인은 빈 이름을 차단한 뒤 단순 `SHOW TABLES LIKE`로 처리하고, 호환 SQL은 동적 SQL 없이 필요한 `ALTER TABLE` 문장만 제공

실행 주소:

```text
http://127.0.0.1:8780/
http://127.0.0.1:8780/?account=계정명
```

실행 파일:

```text
_St/Analyze8780/run_Analyze8780.bat
```


## 2-0. 플랫폼 탭 연결·자동 실행 범위 기준

- 새 자동 세션 시작 시 **현재 활성 Instagram 탭 한 개만** 시작 `tab_id`로 보존한다. 열린 Instagram 탭 전체 검색, 마지막 연결 탭 복구, 후보 탭 순회는 사용하지 않는다.
- 계정 목록 내부의 다음 계정 이동은 새 세션이 아니므로 같은 시작 `tab_id`와 전체 실행 집계를 유지한다.
- 시작 탭이 다른 탭·Chrome 창 때문에 비활성화되면 `사용자가 다른 탭 또는 Chrome 창을 사용 중`으로 무한 대기한다. 원래 탭이 다시 활성화되면 같은 단계에서 자동 재개한다.
- 같은 시작 `tab_id`가 Instagram 외 주소·`about:blank`·로딩 화면으로 바뀌면 중단하지 않고 Instagram 주소 복귀를 무한 대기한다. 시작 탭 자체가 닫힌 경우만 실제 자동중단한다.
- WS8771, Main Injection, rank label, `NoNav_Current`가 일시 미준비이면 `_St.zip 다운로드·실행` 안내 영역을 경고 상태로 전환해 사유와 계정 위치를 표시하고 무한 대기한다. 정상화 시 `자동 재개됨`을 표시한 뒤 이어서 진행한다.
- 기간 필터 끝, All time 하단, 여러 swipe 후 증가 없음, 조건 대상 0개가 확정된 경우는 대기·중단이 아니라 현재 계정을 정상 종료하고 다음 URL로 이동한다.
- 계정 URL 이동은 v249 방식으로 명령 전송 후 4.5초 대기하고 현재 상태를 다시 읽는다. v250의 전체 탭 검색·마지막 탭 복구·18초 owner 강제 gate는 되살리지 않는다.
- No 이동 불일치가 발생하면 v249 수집 캐시 보완 경로를 사용할 수 있다. 실제 rank·shortcode 불일치로 잘못된 게시물을 받을 위험이 확인된 경우만 중단한다.

## 2-1. 수집계정 저장·삭제 기준

- 입력은 `account`, `@account`, Instagram 프로필 URL을 허용하되 저장 표시를 `@account`로 통일한다.
- 일반 `💾 저장`은 현재 화면 순서를 그대로 저장하고 대소문자와 입력 형식이 다른 중복을 하나로 합친다. username 오름차순 정렬은 별도 `정렬` 버튼에서만 실행한다.
- 삭제 계정은 `🗑️ @account`로 보존하며 동일 계정의 활성 입력보다 삭제 상태가 우선한다.
- 삭제 계정에서는 Instagram 프로필 URL을 만들지 않고 자동 계정 이동 대상으로 사용하지 않는다.
- PC 공통 원본은 Analyze8780·Shared SQLite이며 localStorage는 임시 캐시일 뿐 원본이 아니다.


## 2-2. Injection 수동 다운로드 메타 기준

- `Download Image`·`Download Video`는 Injection이 이미 수집한 shortcode 전체 객체를 그대로 기준으로 한다. Side rank label은 순위·정렬 스냅샷 보강값일 뿐 통계 원본을 대체하지 않는다.
- 공통 필드는 `shortcode`, `asset_kind`, `media_index`, `owner_username`, `thumbnail_url`, `video_url`, `upload_at`, `caption`, `views`, `likes`, `comments`, `reposts`, `sort`, `stable_identity`다.
- 파일과 목록의 식별키는 `shortcode + asset_kind + media_index`다. carousel은 media_index를 각각 전달한다.
- WS가 기존 로컬 JSON 항목을 갱신할 때 새 payload에서 실제로 제공되지 않은 통계·URL은 기존 정상값을 유지한다. `metric_presence=true`인 명시적 0은 실제 0으로 반영한다.
- 이미 다운로드된 파일을 다시 누르면 파일은 받지 않아도 현재 shortcode 메타를 기존 JSON·보고서에 병합한다.
- 성공 응답은 shortcode·asset_kind·media_index·원본 meta를 MAIN으로 되돌리고, 정확히 일치하는 Injection 버튼만 `✅`로 바꾼다.

## 3. IG 표시 기준

### 메인 주입 화면

```text
IG double_king0.1
```

- 계정명이 없으면 `IG`만 표시한다.
- Side에는 `No000`, Rank badge와 Side 전용 순위 설정을 유지한다.
- Main에는 `No000`, Rank badge, 순위용 미디어 종류 행, `1/Prev/Next/마지막` 버튼을 표시하지 않는다.
- Main 순위 표시·Main Rank badge 설정·Main 순위 체크값을 저장하거나 복원하지 않는다.
- 자동실행의 현재 순번은 화면 텍스트가 아니라 내부 상태와 shortcode 카드 연결로만 관리한다.
- Main 날짜 색상과 통계별 하단 막대 등 순위와 무관한 기존 시각 설정은 유지한다.
- 자동실행 현재 대상의 노란 강조와 Explore hover의 청록 강조는 유지한다.
- Main 우측 원점·HOVER·DEV·RTC 상시 영역은 숨기고 진단 패널과 `DOM 진단복사`만 유지한다.

### Side 게시물 카드

```text
IG double_king0.1
No003
```

- `IG account`는 항상 별도 줄이다.
- `No000`은 Top 조건이 활성화되고 해당 게시물이 조건에 포함될 때만 별도 줄로 표시한다.
- 계정명과 `No000`을 하나의 박스로 묶지 않는다.
- 순위는 Side 정렬 시점의 스냅샷이며 실시간 재계산 대상이 아니다.

### Analyze8780 게시물 카드

- 기존 게시물 새 창 링크는 `rel="noopener noreferrer"`를 사용한다. 기존 `noopener`를 제거하지 않는다.
- 게시물 이미지 위에 로컬 저장 완료 종류를 `JPG`, `MP4`, `JPG · MP4` 배지로 직접 표시한다.
- 미다운로드 게시물은 배지 없이 표시하며 목록·통계 수집은 그대로 유지한다.
- 이미지 요소만 16:9와 `object-fit:cover`를 적용한다. 카드 전체·텍스트·통계·버튼 영역 비율은 변경하지 않는다.


## 3-1. Instagram URL·수집 범위 절대 기준

- `/explore/`, `/reels/`, 계정 프로필, Instagram 홈은 게시물 URL이 아니다.
- 특별관리 URL은 수집 화면 `source_scope`이며 계정 username으로 저장하지 않는다.
- 게시물 계정은 현재 화면 URL이 아니라 게시물 데이터의 `username`·owner 정보로 판별한다.
- 게시물 링크는 shortcode가 포함된 `/p/{shortcode}/`, `/reel/{shortcode}/`, `/tv/{shortcode}/`만 허용한다.
- 잘못 저장된 기존 scope URL은 Analyze8780 조회 시 shortcode 기반 링크로 안전하게 표시하며 영상은 `/reel/`, 그 외는 `/p/`를 사용한다.
- 순위 `No000`은 단독 값이 아니라 EXT의 현재 `source_scope + 기간 옵션 + sort_by + 기준값 + Top 개수` 조합별 스냅샷이다. Explore·Reels·계정 화면의 순위를 서로 합치지 않는다.
- 조회수 정렬에서 `play_count`가 실제로 있는 게시물은 조회수 내림차순을 유지한다. 조회수가 비공개·미제공인 게시물은 활성화된 좋아요·댓글·리포스트 최소 조건을 모두 AND 통과한 경우에만 후보로 유지하며, 순위 점수는 활성 지표 원값의 단순합산이다. 입력값 0인 지표와 공유 수는 참여점수에서 제외한다. 조회수 기준 입력 자체가 0이면 조회수를 순위에서도 제외하고 활성 참여점수만 사용한다.
- 조회수 기준값이 양수인데 게시물 조회수가 미제공인 경우, 하트·댓글·리포스트 최소 입력 중 하나 이상이 활성화되고 활성 조건을 모두 AND 통과할 때만 후보로 유지한다. 자동 Top 대상에는 최종 순서대로 `No001~Top목표`를 정상 부여한다.
- 공유 수는 검증 전까지 입력값·표시값·통계 payload·서버 payload를 모두 0으로 고정하고 Side UI와 Sort by에서 `d-none/disabled` 처리한다. 공유는 필터와 참여점수에 사용하지 않는다.

## 4. 저장 폴더 기준

```text
상위폴더/
├─ _St/
│  ├─ Analyze8780/
│  ├─ Auto8700/
│  ├─ IG_StYellow/
│  ├─ WS8771/
│  ├─ RTC8790_8791/
│  └─ Shared/
└─ _StDown/
   └─ instagram/
      ├─ account_a/
      │  ├─ 원래파일명.mp4
      │  └─ 원래파일명.jpg
      ├─ account_a.html
      └─ 사용자집계명.html
```

- 기존 `index.htm`·계정별 HTML·JSON 표시 기능은 유지한다.
- Analyze8780은 기존 정적 HTML을 삭제하지 않고 SQLite 조회용 새 로컬 화면을 제공한다.
- 실제 파일 정체성은 `platform + shortcode + asset_kind + media_index`와 해시로 판단한다.

## 5. 다운로드와 observation 분리

다운로드 source mode:

```text
MANUAL_SINGLE
BULK_ALL
BULK_FILTERED
TOP_SORTED
TRANSCRIPT_REQUEST
```

다운로드 완료 흐름:

```text
IG 원본 미디어 URL·owner·원본 파일명
→ WS8771 `ig_media_download_direct` 작업 큐
→ `..\_StDown\instagram\{계정}\{원본파일명}.part`
→ 다운로드 완료 후 원본 파일명으로 atomic rename
→ WS 파일·해시 확인
→ JSON·ECharts 갱신
→ Shared MEDIA_SAVED(folder_name, file_name, relative_path)
→ Analyze8780 저장 폴더·원본 파일 표시
→ sync_queue
```

통계 observation 현재 흐름:

```text
실제 ndy_side_shop 인젝션 수신
→ 현재 source URL 기준 계정/shortcode scope 필터·중복 제거
→ Analyze8780 `/api/observations/batch`
→ SQLite v7 latest/history/10분 bucket 저장
→ 통계 단독: 로컬 처리 종료
→ 통계 연동: 계정별 UTC 1시간 bucket 최초 1회 WS8771 batch 요청
→ PHP `media_stat_hour_upsert` transaction으로 ig_media 최신값+ig_media_stat_hour 저장
→ PHP `media_stat_hour_list`로 같은 계정·bucket의 shortcode별 최신 관측 수신
→ `source_kind=SERVER`로 로컬 10분 저장소에 병합
→ 송신·수신 전체 성공 때만 시간 완료키 저장
```

- URL 변경 시 인젝션 미디어 캐시를 초기화하고 Side에서도 현재 프로필 계정 또는 단일 shortcode를 다시 확인한다.
- 같은 collector+shortcode+UTC 1시간 bucket은 UPSERT하고 같은 observed_at 재시도는 sample_count를 올리지 않는다. 더 최신 observed_at만 수치와 정렬값을 갱신한다.
- `views_available`로 실제 0회와 조회수 미제공을 구분한다.
- Side 재실행·마지막 화면 복원만으로 observation을 다시 저장하지 않는다.
- 통계 연동은 로컬 파일 경로·직접 thumbnail/video CDN URL을 전송하지 않는다.
- 과거 전체 backfill·페이지네이션·통계 차트·영구 재시도 큐는 후속 작업이다.

v237 자동 다운로드는 실제 Windows 로그에서 No001·No002의 MP4/JPG·JSON·HTML 저장까지 확인됐다. v248 현재 페이지 10분 수집·중앙 1시간 이력 송수신은 코드·단위 테스트만 통과했으며 실제 Windows/Chrome/운영 PHP/MySQL 로그 전에는 완료가 아니다.

## 5-1. 브라우저·저장창 기준

- 주 다운로드는 `chrome.downloads.download`를 호출하지 않는다.
- 실제 확장 Chrome과 8700 CDP Chrome이 다를 수 있으므로 `Browser.setDownloadBehavior`를 다운로드 보장 수단으로 사용하지 않는다.
- WS8771이 원본 URL을 직접 내려받아 `_StDown/instagram/{계정}/{원본파일명}`에 저장한다.
- `.part` 임시 파일을 사용하고 완료 후 원본 파일명으로 교체한다.
- 다운로드·완료 처리 실패는 성공으로 기록하지 않는다.
- Save As Enter는 다른 환경의 보조 진단 기능이며 자동 다운로드 완료 조건이 아니다.
- 다운로드 완료 또는 popup 전환을 이유로 Instagram 탭, Side Panel, Chrome 창을 닫지 않는다.
- Auto8700 종료·재시작은 Chrome/CDP 프로세스를 유지한다.


## 5-1-1. 서버 schema_check v241 기준

- 2026-07-24 실제 Windows 로그에서 HTTP 500 응답 body 보존이 확인됐다.
- 실패 단계는 `schema_check`, 원인은 `SHOW TABLES LIKE ?`의 `?` 문법 오류였다.
- 테이블명은 빈 문자열을 먼저 차단하고 `yellow_safe_table()` 검증 후 `SHOW TABLES LIKE '테이블명'`으로 직접 조회한다.
- `yellow_schema_v46_compat.sql`에는 `PREPARE`, `EXECUTE`, `information_schema` 기반 동적 SQL을 사용하지 않는다.
- 운영 MySQL의 세 테이블 구조는 사용자가 제공한 `SHOW CREATE TABLE` 기준으로 v46 필수 컬럼과 일치한다.
- 실제 `media_insert` HTTP 200은 v241 서버 파일 배포 후 재검증한다.

## 5-2. 자동 다운로드 기능 체크리스트

```text
AD-01 자동 시작 활성화 확인
AD-02 조건에 맞는 No000 대상 게시물 선정
AD-03 게시물 owner·shortcode·원본 URL 확인
AD-04 background → WS8771 중복 확인 연결
AD-05 WS8771 직접 다운로드 큐 등록
AD-06 파일 저장·JSON·ECharts·SQLite 완료
AD-07 현재 게시물 완료 후 다음 게시물 진행 가능
```

- 같은 실행·cycle·체크 ID·상태·자산 종류는 한 번만 기록한다.
- 기존 `S00~S99` 상세 cycle 상태는 내부 상태 보존용이며 v237부터 정상 상태를 사용자 로그에 반복 출력하지 않는다.
- `[체크실패]`가 발생하면 다음 단계는 성공으로 기록하지 않는다.
- v237 실제 Windows 로그로 자동 다운로드 파일 저장은 확인됐다. v238에서는 동일 실행의 프로젝트 진행로그에서 `AD-01 → AD-07` 판정과 실패 연결 경로를 추가 확인한다.
- v237 수정 원인: 자동 다운로드의 background `ig_media_exists` 호출이 WS 미연결 상태에서 `force_probe` 없이 즉시 오프라인 반환되어 파일 저장 시작 전에 차단됐다. 자동 경로에만 `priority + force + force_probe`를 적용한다.

v238 프로젝트 진행로그 기준:

```text
[자동다운로드][AD-01][체크완료] ... / 다음단계=진행가능
[자동다운로드][AD-06][체크실패] ... / 다음단계=중단 / 상세로그=... / 검색어=*** 상세로그 *** ... / 실패로그=...
```

## 5-3. v240 서버 등록 진단 기준

2026-07-22 실제 Windows 로그 판정:

- `syanjajsmartcleaning`, `lotor_food_studio`의 게시물 4개에서 MP4 4개와 JPG 4개, 총 8개 로컬 저장 성공
- `DIRECT_DOWNLOAD_OK`, `MEDIA_SAVED_OK`, JSON·MD5·HTML 보고서 갱신 확인
- 서버 `media_exists`는 `ok=1`, `exists=0`으로 정상
- 서버 `media_insert`만 모든 자산에서 HTTP 500 실패

v240 진단 흐름:

```text
WS HTTPError body 보존
→ PHP schema_check
→ master_upsert
→ user_download_upsert
→ collect_event_upsert
→ 실패 단계·테이블·누락 컬럼·SQL 오류 코드 반환
```

- 운영 MySQL에는 `yellow_schema_reset.sql`을 적용하지 않는다.
- `yellow_schema_v46_compat.sql`은 DROP/TRUNCATE/DELETE 없이 `ig_media_collect_event`의 누락 컬럼과 `event_key` unique key만 보완한다.
- 실제 운영 DB `media_insert OK` 로그가 오기 전 서버 등록은 `[검증대기]`다.
- AD-03 복구로그와 AD-06·AD-07 최종 판정은 이번 v240 목표에서 제외하고 다음 패치로 유지한다.

## 6. SQLite v5 기준

### 기존 v1·v2

- 계정·수집 조건·게시물·최신 통계·통계 이력
- 미디어·대본·후킹·카테고리·평가·댓글
- 다운로드 batch/job·`MEDIA_SAVED`
- P2P 보유 캐시·sync_queue

### v3 추가

- `post_stat_10m`
  - 게시물별 10분 버킷 통계
  - `UNIQUE(post_id, bucket_at)`
  - 같은 10분 구간 재수집은 새 행이 아니라 UPSERT
  - `sample_count`로 같은 구간 수집 횟수 기록
- `deletion_events`
  - 계정·게시물 soft-delete tombstone
  - 서버 DELETE 동기화 대기 근거
- `social_accounts.deleted_at`
- `posts.deleted_at`


### v4 추가

- `collection_account_queue`
  - Side 수집계정 username, 순서, enabled, is_deleted 저장
  - 특별관리 URL은 저장 거부
  - SQLite가 PC 기준 원본이며 localStorage는 캐시

### v5 추가

- `media_assets.folder_name`
- `media_saved_events.folder_name`
- `file_name`은 Instagram 원본 파일명을 유지한다.
- `relative_path`는 `계정/원본파일명`으로 로컬 SQLite에만 저장한다.
- 서버 sync payload에는 로컬 폴더명·경로를 포함하지 않는다.

10분 버킷 예:

```text
14:02:37 → 14:00:00
14:09:59 → 14:00:00
14:10:01 → 14:10:00
14:27:20 → 14:20:00
14:59:59 → 14:50:00
```

한 게시물은 시간당 최대 6개 버킷을 가진다.

## 7. Analyze8780 API

```text
GET    /api/health
GET    /api/accounts?page=1&page_size=50
GET    /api/accounts/{id}/posts?page=1&page_size=50&sort_by=views
GET    /api/posts/{id}
GET    /api/posts/{id}/stats?limit=144
GET    /api/deleted/accounts
GET    /api/collection-accounts
PUT    /api/collection-accounts
POST   /api/observations
DELETE /api/accounts/{id}
DELETE /api/posts/{id}
```

정렬 기준:

```text
views
likes
comments
reposts
views_delta
views_per_hour
published_at
```

기본 분석값:

- 현재 조회수·좋아요·댓글·리포스트
- 최근 10분 조회수 증가량
- 시간당 환산 증가량
- 좋아요율·댓글률
- 계정별 게시물 수·최고 조회수·합계 조회수

떡상 점수 공식은 아직 확정하지 않는다. 실제 10분 데이터가 누적된 뒤 가중치를 결정한다.

## 8. 삭제 기준

### 게시물 삭제

- Analyze8780 목록에서 숨김
- `posts.deleted_at` 기록
- `deletion_events`와 `sync_queue DELETE` 기록
- 실제 MP4/JPG 파일은 유지

### 계정 삭제

- `social_accounts.active=0`, `deleted_at` 기록
- 해당 계정 수집 규칙 `enabled=0`
- 해당 계정 게시물을 분석 목록에서 숨김
- Side 수집계정 목록의 해당 username 앞에 `🗑️` 표시
- `🗑️` 항목은 수집 순회와 계정 카운팅에서 제외
- 실제 파일은 유지

Side 목록 예:

```text
account_a
🗑️ account_b
account_c
```

실행할 때만 `https://www.instagram.com/{username}/` 프로필 URL을 생성한다.

표시 카운트는 2개이며 삭제 1개는 별도 안내한다. 단순 전체 줄 수에서 이모지 개수를 빼는 방식이 아니라 각 항목의 삭제 상태를 판별해 제외한다.

## 9. Side 수집계정 저장 기준

- 수집계정 원본은 URL이 아니라 게시물 주입 데이터의 `username`이다.
- `/explore/`, `/reels/`, `/p/`, `/reel/` 같은 현재 화면 URL을 계정으로 해석하지 않는다.
- `표시계정추가` 버튼은 사용하지 않는다. Side 카드의 `IG 계정명` 영역을 클릭하면 그 카드의 owner username이 기존 `<계정명> 계정추가` 버튼의 대상으로 선택된다. 클릭만으로 목록에 저장하지 않으며 계정추가 버튼과 `💾 저장` 절차를 유지한다.
- `💾 저장`은 Analyze8780 `PUT /api/collection-accounts`를 호출해 SQLite `collection_account_queue`에 저장한다.
- Side 시작 시 `GET /api/collection-accounts`로 복원하므로 Chrome 프로필이 바뀌어도 같은 PC 목록을 공유한다.
- localStorage는 8780 연결 실패 때 보여줄 캐시일 뿐 기준 저장소가 아니다.
- 계정목록은 포커스 이탈로 자동 저장하지 않으며 미저장 변경은 `💾 저장*`으로 표시한다.
- 삭제 계정은 `🗑️ username`, `enabled=0`, `is_deleted=1`로 유지하고 수집·카운팅에서 제외한다.

## 10. 기존 index.htm 유지와 향후 역할

현재 만족 확인된 기능:

- 개별 수동 다운로드
- JSON 연동 index.htm 표시
- 계정별 HTML·ECharts 보고서

유지 원칙:

- 기존 index.htm 생성·JSON 연동을 깨지 않는다.
- Analyze8780은 데이터가 많아질 때 페이지 조회·삭제·분석을 담당한다.
- 향후 index.htm 또는 8780 화면에서 대본 추출 버튼을 누르면 Python이 처리한다.
- 대본 완료 후 본문 표시와 `📋 복사` 버튼을 제공한다.

## 10-1. 게시물 지표·그룹 예정 기준

- 게시물 지표는 조회수, 좋아요, 댓글, 공유, 저장, 리포스트를 각각 독립 저장한다.
- 원본 응답에 없는 공유·저장·리포스트는 `0`이 아니라 `NULL/UNKNOWN`으로 둔다.
- 그룹 수집 조건은 임의의 "관찰 기간"을 만들지 않고 EXT의 실제 `1day`, `2day` 등 기간 옵션, `sort_by`, 기준값, Top 개수를 그대로 저장한다.
- 한 사용자는 여러 그룹에 가입할 수 있으며 자료 공개 범위는 PRIVATE, 복수 GROUP, PUBLIC으로 구분한다.
- PRIVATE 사용자는 타인의 자료를 받아 포인트 차감은 가능하지만 비공개 자료 제공으로 포인트 적립은 하지 않는다.
- P2P·포인트는 파일 해시·크기 검증 후 원장 거래로만 확정한다.

## 11. 단계별 우선순위

### 1단계-A — v231 코드·자동검증 완료

- Side `IG account`·`No000` 분리
- Analyze8780 서비스와 Auto8700 시작 시 자동 실행
- 8780 포트와 `/api/health` 상태 확인
- SQLite 계정·게시물 repository
- 10분 통계 UPSERT·조회
- 계정·게시물 soft-delete
- Side `🗑️` 삭제 계정 제외
- 계정목록 `💾 저장` 명시 버튼
- username-only 수집목록을 Analyze8780 SQLite에 저장·복원
- Chrome 프로필별 localStorage는 캐시로만 사용
- 포커스 이탈 자동 저장 폐기
- 미저장 변경은 `💾 저장*`으로 표시
- 다운로드 최종 저장 위치를 `_StDown/instagram/계정`으로 고정

실제 Windows Auto8700→Analyze8780 기동과 실제 Chrome 저장 버튼 동작은 `[검증대기]`다.

### v240 바로 다음 검증

- 서버에 `/var/www/html/i` 파일 배포
- 토큰 인증 후 `mode=schema_check` 실행
- `schema_ok=0`이면 `yellow_schema_v46_compat.sql` 적용
- 동일 게시물 `media_insert` 재실행 후 HTTP 200·`ok=1` 확인
- 그 다음 AD-03 복구·AD-06/07 최종 판정 패치 진행

### 1단계-B — 다음 우선순위

- IG 주입 통계를 다운로드와 무관하게 8780으로 실시간 전송
- observation 전송량 제어·중복 방지·재시도
- SQLite 10분 bucket UPSERT와 observation sync_queue 등록
- 8780 미실행·일시 장애 시 실패를 숨기지 않고 재시도 대기

### 2단계

- PHP/MySQL 10분 통계 중앙 저장
- 같은 게시물·같은 10분 버킷 UPSERT
- 중앙 계정별·게시물별 집계
- 서버 최신값·이력 조회

### 3단계

- 수집자 회원·컴퓨터·토큰

### 4단계

- 그룹·공개 범위·공동 수집

### 5단계

- 대본 추출·카테고리·평가·후킹·검색

### 6단계

- P2P·포인트 정산

## 12. 완료 선언 기준

- 코드만 반영된 상태는 완료가 아니다.
- 관련 고정 테스트가 존재하고 실행 결과가 `OK`여야 한다.
- 실제 Windows·Chrome·서버 동작은 실제 로그 확인 전 `[검증대기]`다.
- 다음 채팅에서도 `ai_read*`와 `test_list.md`만 읽으면 현재 기준과 다음 작업을 알 수 있어야 한다.

## 5-1. 다음 대형 작업 — 사용자 간 통계 공유·10분 집계

- 이 기능은 Side 단독 수정이 아니라 IG→WS8771→Shared SQLite→PHP/MySQL→Analyze8780/Side의 다단계 작업으로 분리한다.
- 1단계: 한 계정 처리가 끝난 뒤 게시물별 `account, shortcode, source_scope, sort_by, No, views, likes, comments, reposts, ER, published_at, collected_at`을 한 배치로 WS8771에 전달한다.
- 2단계: WS8771은 `batch_id + 수신 개수 + 실패 개수` ACK를 반환한다. ACK가 OK일 때만 로컬 SQLite에 전달 완료를 기록한다.
- 전달 완료 시각은 현재 시각을 10분 단위로 내림 처리한 bucket으로 저장한다. 예: `14:27 → 14:20`. 브라우저 localStorage만을 원본으로 사용하지 않는다.
- 3단계: WS8771이 PHP API로 전송하고 서버는 `계정 + shortcode + sort_by + 10분 bucket + collector/device` 고유키로 UPSERT한다.
- 4단계: 서버는 일자별·계정별·sort_by별 집계를 제공하고 Analyze8780 또는 Side가 이를 조회해 시각화한다.
- 공유 데이터에는 Instagram 쿠키·세션·토큰, 로컬 절대경로, JPG/MP4 원본 URL을 포함하지 않는다.
- 대량 기능이므로 `배치 수집 → WS ACK → 로컬 전달 flag → PHP 저장 → 서버 집계 → UI 시각화` 순서로 각각 별도 버전과 실제 운영 로그를 받아 완료 처리한다.

## IG v279a 분기

- Auto8700·WS8771·Analyze8780·RTC는 v279 상태를 유지한다.
- IG만 `v279a` 분기로, 다운로드 전송 core를 v262 방식으로 복원했다.
- v279a 이후 aa·b·c·d 분기를 거쳐 현재 v280 OWNER 인계 구조로 승격했다.

- 사용자 `자동 중단` 표시 절대 기준: 중단 직전 `igblue_auto_py_stats` 진행 정보를 덮어쓰지 않는다. 기존 표시를 그대로 보존하고 마지막 줄에 `⛔ 자동 중단`만 추가한다. 중단 사유·보존 진행상태 이어하기 문구는 운영자 전달용 화면에 넣지 않는다.

## v313 Side 자동/예약 다운로드 현재 기준
- IG_StYellow manifest `2.3.8.3130 / 2.3.8.313`.
- Side 썸네일 기본 100px, 섞기/자동섞기 유지, 일반 JPG 저장은 Cache8701로 단일화한다.
- 다운로드 지연은 기본초 + 매 파일 0~랜덤최대초. 예약1/2 랜덤분은 로딩/정상 자동종료 때 재추첨하고 수동 수정 가능하다.
- 다운로드 방식1/2는 삭제하지 않고 UI만 숨긴다. 실제 Chrome 실동작은 검증대기다.