virtual-insanity
← 리포트 목록

주간 크레딧 요약본 일요일 크론 — 주말 수집 공백·주차 덮어쓰기 방지안

2026-09-26 cowork [weekly-credit, cron, 5c45c26045be, house-daily, proposal, W39]

주간 크레딧 요약본 일요일 크론 — 주말 수집 공백·주차 덮어쓰기 방지안

조사 시각: 2026-09-26 20:10~20:25 KST. 읽기만 했고 어떤 파일·크론도 수정하지 않았다.

[권고]

→ 1순위 (a) 일요일 08:00 "주간 크레딧 사전준비" 단계 신설 + 본 크론(09:00)이 그 결과를 확인한 뒤에만 빌드 - 무엇을: 일요일 08:00(KST)에 스크립트 전용 크론(에이전트 없음) 1개를 추가해 ① 최근 7일 수집분 볼트 노트 내보내기(노트 전용, 발송·Drive 동기화·편집 LLM 없음) ② 외사별일일을 일요일 날짜로 재생성(토·일 수집분, 업로드 없음) ③ 결과를 data/weekly_credit_prep_<일요일>.json(ok, 노트 수, 외사별 reports 수, exit code)으로 남긴다. 09:00 본 크론 프롬프트에 "0) 이 파일이 오늘 날짜·ok=true인지 확인, 없으면 같은 준비 스크립트를 먼저 1회 실행"을 추가한다. - 왜: 주말 수집분은 구조적으로 요약본 입력에 못 들어간다(아래 [현황] 수치). 외사별일일은 평일 08:15에만(15 8 * * 1-5), 노트 내보내기는 평일 06:20(20 6 * * 1-5)과 토 08:00 주간 브리프에서만 돈다. 게다가 요약 스크립트는 한 하우스에 그 주 외사별일일이 하나라도 있으면 원문 노트 보충을 하지 않는다(if not chunks: 122행) — 그래서 JP모건 3건은 노트가 생겨도 09-21 일일요약 1개에 가려져 빠졌다. 노트 내보내기와 외사별일일 재생성이 둘 다 필요한 이유다. - 기대효과: 최근 5주 주말 include 수집분 248건(전체 include 657건의 37.7%) 중 일요일 요약본 생성 전에 들어온 분량이 입력 후보가 된다. W39 기준 토요일 09-26 수집 38건이 자동 반영 대상(이번엔 수동 재생성으로만 반영됨, jpm_sources 1→2).

→ 2순위 (b) 날짜 인자 가드 — 일요일이 아닌 날짜로 돌려 이전 주차를 덮어쓰지 못하게 막기 - 무엇을: build_weekly_credit_house_summary.py에 ① 비일요일 인자 거부(기본) 또는 --week 2026-W39 형식 인자 도입 ② 주차 번호를 target 기준으로 계산하고 start와 같은 ISO 주차인지 불일치 시 즉시 종료 ③ 기존 PDF의 근거목록 target과 새 target이 다르면 --force 없이는 덮어쓰기 거부 + 교체 전 자동 백업 ④ 근거목록 JSON을 검증 통과 뒤에만 쓰기. - 왜: start = target - 6일(301행), week_id = start의 ISO 주차(302행)라서 토요일 09-26을 넣으면 start=09-20(일, ISO W38) → 2026-W38 폴더의 PDF·근거목록을 덮어쓴다. 월요일을 넣어도 start=화요일이 되어 W39 창이 하루 밀린 채 W39를 덮어쓴다. 또 근거목록 JSON은 검증 실패 여부와 무관하게 먼저 써진다(339행 write → 340행 이후 실패 판정) — 검증에 떨어져도 지난 주차 근거목록은 이미 망가진다. - 기대효과: 잘못된 날짜 실행 시 덮어쓰기 0건(exit 3로 조기 종료), 사고 복구용 수동 cp -p 백업 절차(이번 W38·W39 .bak-jpmgap-20260926 3개) 불필요화.

→ 3순위(보강) 요약 스크립트의 "하루 단위 원문 보충" - 무엇을: collect_house_material()에서 원문 노트 보충을 "하우스 전체에 일일요약 0개일 때"가 아니라 "그 날짜에 일일요약이 없는데 노트는 있는 날"마다 수행. - 왜: 1순위가 실패하거나 09:00 이후 수집분이 생겨도 볼트 노트만 있으면 반영되는 이중 안전망. 1순위가 돌면 거의 발동하지 않는다.

[현황]

읽은 원본과 수치(조회 2026-09-26 20:10~20:25 KST):

  1. 공백 기록 — /Users/ron/.hermes/workspace/logs/w39_jpm_gap_20260926.json(3,223바이트, 20:11 생성)
  2. 4행 gap_cause: JP모건 3건 collected_at 2026-09-26T19:07(수동 재수집), routing=include, "research-brief-prepare-0620(06:20 평일)와 build_house_daily_digest 모두 그 이전/주말 미실행이라 미반영. 라우팅 제외 아님."
  3. 6~10행 actions(9행이 요약본 재생성): 노트 190건 재내보내기, 외사별일일 8개 외사 재생성, 요약본은 인자 2026-09-27로 재생성("인자 2026-09-26은 start=09-20=W38이 되어 W38을 덮어쓰므로").
  4. jpm_sources 1개 → 2개, covered 9 → 9, 페이지 1, 로드맵 0건, 채널 발송 없음.
  5. 일요일 본 크론 — /Users/ron/.hermes/cron/jobs.json 7964행 id 5c45c26045be, 7966행 prompt, 7982행 "expr": "0 9 * * 0". last_run_at: null, repeat.completed: 0 → 아직 한 번도 안 돌았다(첫 실행 2026-09-27 09:00). 프롬프트 2)단계가 "오늘 날짜(YYYY-MM-DD)를 인자로" 요약 스크립트 하나만 부르고, 사전 준비 단계가 없다. 전용 timeout 필드 없음.
  6. 06:20 내보내기 크론 — 같은 파일 6807행 id research-brief-prepare-0620, 6816행 script research-sales-engine/prepare-daily-research.sh, 6818행 "expr": "20 6 * * 1-5"(평일만). 스크립트 본문은 deliver_research_link_brief.py --mode daily(발송 없이) 1줄. 최근 2회 연속 3600초 시간초과 실패(last_status=error, failure_streak=2, last_run 2026-09-25 07:20). 즉 평일 노트 내보내기 자체도 불안정하다.
  7. 외사별일일 크론 — 7289행 research-house-daily-0815, 7300행 "expr": "15 8 * * 1-5"(평일만), 스크립트 send-house-daily.sh → build_house_daily_digest.py --date <오늘> --send.
  8. 노트 내보내기 함수 — scripts/deliver_research_link_brief.py
  9. 1133행 _write_obsidian_notes(target, mode): 수집일(first_collected_at) 기준 폴더 외사 리서치/<수집일>/<분류>/에 노트 작성.
  10. 240~251행 _publication_window: daily=target-7일~target, weekly=그 주 월요일~target.
  11. 1611행 run(), 1623~1626행: 일요일+--send면 skipped_sunday로 즉시 종료(노트도 안 씀). --send 없이면 진행하지만 Drive 원문 동기화(최대 180초 폴링)·카탈로그·편집 LLM까지 전부 돈다 → 06:20처럼 1시간 초과 위험. 노트만 쓰는 경로(1667행 호출)가 따로 없다.
  12. 외사별일일 창 — scripts/build_house_daily_digest.py 396~405행 _collection_window: 직전 평일 다음날~target. 일요일 target이면 토~일(2일)을 이미 올바르게 본다 → 함수 로직은 맞고, 일요일에 불러주는 크론이 없는 것이 문제. 416~428행 main(): --date, --send, --no-upload.
  13. 요약 스크립트 — scripts/build_weekly_credit_house_summary.py(352행)
  14. 99~130행 collect_house_material: 1순위 00_외사별일일/*_{house}_외사별일일.md, 122행 if not chunks:일 때만 원문 노트 4건 보충.
  15. 299~310행 build_pdf: 300행 target = target or date.today(), 301행 start = target - timedelta(days=6), 302행 week_id = start.isocalendar() 주차, 305~310행 출력 경로가 week_id 폴더.
  16. 339행 근거목록 JSON 쓰기 → 340~343행 실패 시 종료, 344행 tmp_path.replace(out_path)로 덮어쓰기(백업 없음).
  17. 351~352행 __main__: sys.argv[1]을 그대로 date.fromisoformat, 요일 검사 없음.
  18. 주말 수집 분량 — data/archive_index.json 실측(2026-08-24 이후, routing=include 657건):
  19. 토·일 수집 248건(37.7%): 08-29 25 / 08-30 21 / 09-05 30 / 09-06 11 / 09-12 32 / 09-13 35 / 09-19 24 / 09-20 32 / 09-26 38.
  20. 이 중 일요일 09:00 이전 수집: 08-30 21 / 09-06 11 / 09-13 35 / 09-20 14.
  21. 외사별 주말 수집: 바클레이즈 65, 골드만 65, HSBC 31, 씨티 22, 노무라 21, BofA 17, JP모건 16, TD 7, 모건스탠리 4.
  22. 평일 08:15 이후(그날 외사별일일 생성 뒤) 수집 30건(목 25, 금 5).
  23. 폴더 실측: 외사 리서치/2026-09-19, 2026-09-20에 00_외사별일일 폴더 없음(주말 외사별일일 0건). 2026-09-20(일) 노트 32개의 가장 이른 생성시각이 2026-09-21 06:20:46 — 일요일 수집분 노트는 월요일 06:20에야 생긴다. 2026-09-19(토) 노트 25개는 토 08:07(토 08:00 주간 브리프가 씀).

[리스크]

  1. 준비 단계 시간 초과(가장 큼): 06:20 크론이 run() 전체를 돌다 2회 연속 3600초 초과. 일요일 준비 단계가 같은 run()을 쓰면 09:00 전에 못 끝난다. → 노트 전용 경로(--notes-only)가 필수이며, 이게 없으면 1순위 안 자체가 무효.
  2. 외사별일일 LLM 호출: 재생성은 외사당 LLM 요약(8개 외사, 이번 수동 실행 기준)을 부른다. LLM 장애 시 폴백 카드가 나오고 요약 품질이 떨어질 수 있다. 준비 단계는 실패해도 본 크론을 막지 말고, 준비 결과 JSON에 실패 외사를 남겨 본 크론 보고에 붙인다.
  3. 이중 반영: 일요일 창을 금~일로 넓히면 금요일 08:15 이전 건이 금요일 md와 일요일 md에 중복된다. 기본은 현재 로직(토~일)을 유지하고, 금요일 08:15 이후 5건(최근 5주) 같은 늦은 금요일분은 3순위 보강(하루 단위 원문 보충)이나 "이미 금요일 캐시에 있는 key 제외" 방식으로 처리한다.
  4. 잔여 공백(설계상 남는 것): 일요일 09:00 이후 수집분은 이번 주 창(월~일)에 속하지만 요약은 이미 끝났고, 다음 주 창(다음 월요일~)에서는 빠진다. 2026-09-20 기준 32건 중 18건이 09:00 이후 수집 → 영구 누락 후보. 해결책은 주차 창을 "직전 요약 실행시각 이후 수집분"으로 바꾸는 것이지만 설계 변경 폭이 크므로 이번 범위 밖, 수치만 기록.
  5. 단일 장애점: 요약 스크립트 입력이 사실상 00_외사별일일 md 하나에 달려 있다. 수집→노트→외사별일일→요약의 4단계 중 하나만 늦어도 조용히 빠진다(이번 JP모건 건이 에러 없이 빠진 사례). 3순위 보강이 이 단일 장애점을 완화한다.
  6. 덮어쓰기: 수동 재실행·점검 때 토요일/월요일 날짜를 넣는 실수는 이미 발생 직전까지 갔다(gap 로그 9행). 근거목록 JSON이 검증 전 먼저 써지는 순서(339행) 때문에 실패 실행도 지난 주차 기록을 훼손한다.
  7. 발송 경로(참고, 이번 범위 밖): 스킬 기록상 Slack 업로드는 MEDIA: 경로의 공백("외사 리서치")에서 잘려 반복 실패했다. 첫 실행(09-27)에서 이 문제가 해결됐는지 별도 확인 필요.

codex 위임용 수정 지점 (코드 작성은 codex, 크론 등록·프롬프트 변경은 ron)

# 파일 함수/행 수정 내용
A1 hermes-research-sales-engine/scripts/deliver_research_link_brief.py main() 1850~1862행, run() 1611행 --notes-only 플래그 추가: _write_obsidian_notes(target, "daily")(1133행)만 호출하고 결과 JSON 출력 후 종료. 일요일 스킵(1623행)·Drive 동기화·편집 LLM을 거치지 않음
A2 신규 ~/.hermes/scripts/research-sales-engine/prepare-weekly-credit-sunday.sh — 06:20·08:15 스크립트와 같은 PYTHONPATH//usr/bin/python3 고정 패턴. ① deliver_research_link_brief.py --mode daily --notes-only --date <오늘> ② build_house_daily_digest.py --date <오늘> --no-upload ③ data/weekly_credit_prep_<오늘>.json 기록(각 단계 exit code, 노트 수, 외사별 reports 수)
A3 /Users/ron/.hermes/cron/jobs.json 신규 job 스크립트 전용(no_agent) 0 8 * * 0, timeout 2400초, deliver local, 실패는 DM
A4 /Users/ron/.hermes/cron/jobs.json 7966행 5c45c26045be prompt 절차 0) 추가: 준비 결과 JSON이 오늘·ok면 진행, 없거나 실패면 A2 스크립트 1회 실행 후 진행하고 그 사실을 보고에 한 줄 포함. 2)단계 인자를 TZ=Asia/Seoul date +%F로 명시하고 "오늘이 일요일이 아니면 실행 중단" 문구 추가
B1 hermes-research-sales-engine/scripts/build_weekly_credit_house_summary.py __main__ 351~352행 argparse 전환: 위치 인자 날짜 + --week YYYY-Www + --force. 비일요일 날짜는 exit 3(메시지: "일요일 날짜 또는 --week 사용")
B2 같은 파일 build_pdf 299~302행 week_id를 target 기준 ISO 주차로 계산, start.isocalendar()[:2] != target.isocalendar()[:2]면 exit 3
B3 같은 파일 build_pdf 305~310행, 344행 기존 out_path가 있고 기존 근거목록의 target이 새 target과 다르면 --force 없이는 exit 3. 교체 직전 기존 PDF·근거목록을 .bak-<시각>으로 자동 보존
B4 같은 파일 build_pdf 337~344행 근거목록 JSON 쓰기(339행)를 검증 통과 뒤로 이동, 실패 시에는 …_근거목록.failed.json으로 따로 저장
C1 같은 파일 collect_house_material 99~130행(122행 분기) 날짜별로 해당 하우스 외사별일일이 없고 분류 노트는 있는 날은 원문 노트 보충(하루 최대 2건)

[검증방법]

  1. (a) 사전 확인 — 2026-10-04(일) 08:00 이후
  2. 명령: ls "/Users/ron/knowledge-agent/400-reports/외사 리서치/2026-10-04/00_외사별일일/" | wc -l → 기대값 ≥ 2(목록 파일 + 외사 1개 이상), cat data/weekly_credit_prep_2026-10-04.json → ok=true, 각 단계 exit 0.
  3. 명령: find "외사 리서치/2026-10-03" -name '*.md' -exec stat -f '%SB' {} \; → 토요일 노트 생성시각이 일요일 08:xx 이전(월요일 06:20이 아님).
  4. (a) 반영 확인 — 같은 날 09:00 이후
  5. 2026-W40_크레딧_리포트요약_근거목록.json의 sources에 2026-10-04/00_외사별일일/ 경로가 1개 이상 포함.
  6. KPI: 주말 include 수집분 중 해당 주 요약본 입력 경로에 잡힌 비율 — 현재 W39는 수동 조치 전 0%(토요일 38건 미반영) → 목표 일요일 09:00 이전 수집분 100%.
  7. 준비 단계 소요시간 ≤ 40분(09:00 전 종료), 시간초과 0회.
  8. (b) 가드 확인 — codex 반영 직후, 반드시 W38·W39 폴더 cp -p 백업 후
  9. python3 scripts/build_weekly_credit_house_summary.py 2026-09-26 → exit 3, W38 폴더 PDF·근거목록 mtime 불변(현재 W38 PDF 53,304바이트, 9월 20 20:36).
  10. python3 scripts/build_weekly_credit_house_summary.py --week 2026-W39 → target=2026-09-27로 해석, W39만 갱신.
  11. 검증 실패를 유도했을 때 정식 근거목록 JSON mtime 불변.
  12. 롤백 조건
  13. 준비 단계가 2주 연속 09:00을 넘기거나 외사별일일 재생성이 평일 산출물(평일 날짜 폴더)을 건드리면 신규 크론을 일시정지(해리 확인 후)하고 본 크론 프롬프트 0)단계만 남긴다.
  14. B1~B4 반영 후 일요일 정상 실행(일요일 날짜 인자)이 exit 3로 막히면 즉시 이전 버전으로 되돌린다(codex가 수정 전 .bak 보존).