virtual-insanity
← 리포트 목록

24h+ stale 크론 복구 REVISE 보완 조사

2026-04-14 stale [openclaw, cron, stale, revise]

24h+ stale 크론 복구 REVISE 보완 조사

결론: 조사·문서화만 수행했다. 크론 수정/재실행/rollback 없음.

Critic 지적은 유효하다.

  1. daily-context-snapshot은 실제 stale 전체를 저장하지 않고 stale[:40]으로 잘라서, 보고서에는 40개만 노출됐다.
  2. 현재 직접 계산한 활성 24h+ stale은 146개다.
  3. pipeline-self-healer 수동 실행은 범위 밖 job 14건의 상태/배송 필드를 변경했다.
  4. 즉시 전체 rollback은 권장하지 않는다. 현재 jobs.json에는 이후 다른 작업의 변경도 섞여 있어, 필요 시 선택적 rollback만 해야 한다.
  5. 플레이북과 회귀 경보 샘플을 새로 저장했다.

1. 참조한 원본/비평 보고서

  • 원본 daily snapshot 보고서: ~/knowledge-agent/400-reports/260414_pipeline_daily-context-snapshot.md
  • 원본 stale 복구 보고서: ~/knowledge-agent/400-reports/260414_stale_cron_audit.md
  • Critic 보고서: ~/knowledge-agent/400-reports/260414_critic_stale_by_A.md
  • self-healer 실행 로그: ~/.openclaw/workspace/memory/cron-runs/stale-cron-audit-260414/pipeline-self-healer.log
  • jobs patch report: ~/.openclaw/workspace/memory/cron-runs/stale-cron-audit-260414/jobs_patch_report.json

2. 40개로 숨겨진 이유

확인 코드: ~/.openclaw/workspace/scripts/pipeline/daily_context_snapshot.py

문제 지점:

  • daily_context_snapshot.py:57: stale = []에 전체 stale 누적
  • daily_context_snapshot.py:69: enabled이고 lastRunAtMs가 없거나 24h 초과면 stale 추가
  • daily_context_snapshot.py:85: "enabled_stale_over_24h": stale[:40]
  • daily_context_snapshot.py:157: markdown에서 len(cron['enabled_stale_over_24h'])를 출력

즉, JSON에는 전체 개수가 아니라 앞 40개 샘플만 저장하고, markdown은 그 샘플 길이 40을 전체 수처럼 출력한다.

정확한 구조는 아래처럼 분리해야 한다.

  • enabled_stale_over_24h_total: 전체 개수
  • enabled_stale_over_24h_sample: 잘린 샘플
  • markdown에는 total을 출력하고 sample cap을 명시

이번 작업에서는 코드 수정 금지 조건 때문에 수정하지 않았다.

3. 숨긴 stale 146개 전수 추출 결과

전수 산출물:

  • CSV: ~/knowledge-agent/400-reports/260414_stale_cron_revise_full_stale_146.csv
  • JSON: ~/knowledge-agent/400-reports/260414_stale_cron_revise_full_stale_146.json
  • Markdown 전체표: ~/knowledge-agent/400-reports/260414_stale_cron_revise_full_stale_146.md

검증:

  • CSV line count: 147줄 = header 1 + stale 146
  • Markdown 표: 146개 전체 행 저장
  • 계산 기준: enabled=true이고 lastRunAtMs 없음 또는 마지막 실행 후 24h 초과

요약 카운트

구분 개수
전체 jobs 295
활성 jobs 157
활성 24h+ stale 146
nextRunAtMs가 이미 과거인 실제 overdue 140
다음 실행이 미래라 단순 24h 기준에만 걸린 것 4
nextRun 없음 2

기간별 분류

기간 개수
1일 이상 ~ 1주일 미만 128
1주일 이상 11
never-run 7

원인별 분류

요구된 4종(never-run, paused, error, killed)만으로는 전체를 정직하게 설명할 수 없었다. 146개 중 136개는 마지막 상태가 ok인데도 다음 실행이 밀린 상태라 ok-but-stale로 별도 분류했다. 이를 강제로 errorkilled로 넣으면 거짓 분류가 된다.

원인 개수 설명
ok-but-stale 136 마지막 실행은 ok. 그러나 24h+ 미실행이고 대부분 nextRunAtMs도 과거. scheduler/ticker 정지, due-run 누락, agentTurn 실행 경로 문제 가능성이 큼
never-run 7 활성인데 lastRunAtMs 없음
schema-skipped 2 payload/sessionTarget 규격 불일치로 skip
error 1 마지막 상태가 error
paused 0 146개는 활성 stale 기준이라 paused 없음
killed 0 jobs 상태만으로 OOM/SIGKILL/timeout 확정 흔적 없음

payload/sessionTarget 분포

항목 개수
agentTurn 124
systemEvent 19
command 1
shell 1
script 1
isolated target 126
main target 20

4. 146개 중 즉시 주의할 stale 유형

never-run 7개

전체 목록은 full list 파일에 있다. 대표적으로:

  • id 없음 — 방법론 피드백 수신
  • id 없음 — GitNexus 재인덱싱
  • urea-price-tracker
  • blueprint-updater
  • morning-briefing
  • memory-weekly-report
  • chroma-mcp-recycle

schema-skipped 2개

  • cron-alert: sessionTarget=isolated, payload.kind=script, lastError=isolated job requires payload.kind=agentTurn
  • domain-wiki-compiler: sessionTarget=main, payload.kind=shell, lastError=main job requires payload.kind="systemEvent"

error 1개

  • price-history-collector 가격 이력 수집 (3회/일): lastError=Delivering to Telegram requires target <chatId>

ok-but-stale 136개

대부분 여기에 몰려 있다. 마지막 실행은 성공이지만 nextRunAtMs가 과거인 job이 많다. 이는 개별 스크립트 실패보다 scheduler/ticker due-run 경로가 멈췄거나, agentTurn 정규 실행이 밀린 문제일 가능성이 크다.

5. 범위 외 14개 self-healer 변경 diff

산출물:

  • ~/knowledge-agent/400-reports/260414_stale_cron_self_healer_out_of_scope_14.csv

비교 기준:

  • before: ~/.openclaw/cron/jobs.json.healer-bak.1776128971
  • after 근거: ~/.openclaw/cron/jobs.json.bak 및 self-healer stdout
  • 현재 파일: ~/.openclaw/cron/jobs.json

14건 요약:

index id 변경 현재 판단
50 technical-data-collector delivery chatId/to 보정 기능상 유해해 보이지 않음. rollback 낮음
85 goal_alignment_daily delivery chatId/to 보정 rollback 낮음
99 analyst-quality-tracker delivery chatId/to 보정 rollback 낮음
109 notion-analyst-sync delivery chatId/to 보정 rollback 낮음
140 commodity-spike-watch delivery chatId/to 보정 rollback 낮음
153 hypothesis_lifecycle delivery chatId/to 보정 rollback 낮음
177 economic-calendar-collect delivery chatId/to 보정 rollback 낮음
236 price-history-collector state error → idle, consecutiveErrors 3→0, lastError 제거 audit/alert 의미상 선택적 rollback 검토
237 evening-briefing delivery chatId 보정 rollback 낮음
244 6h-content-summary delivery chatId 보정 rollback 낮음
269 technical-stat-models 기술통계모델 state error → idle, consecutiveErrors 3→0, lastError 제거 선택적 rollback 검토
273 technical-stat-models state error → idle, consecutiveErrors 3→0, lastError 제거 선택적 rollback 검토
274 technical-stat-models 기술통계모델 state error → idle, consecutiveErrors 3→0, lastError 제거 선택적 rollback 검토
285 price-history-collector delivery chatId 보정 rollback 낮음

6. 원상복구 가능 여부 판단

즉시 전체 rollback: 비권장

이유:

  • jobs.json.healer-bak.1776128971로 통째로 되돌리면 10:09 이후의 다른 변경까지 같이 날아간다.
  • 현재 jobs.json은 10:32 이후 oil cron 관련 변경도 반영된 상태다.
  • 이번 작업 지시는 크론 수정 금지이므로 실제 rollback은 하지 않았다.

선택적 rollback: 가능

가능한 대상:

  • index 236, 269, 273, 274state 필드

이 4건은 self-healer가 consecutiveErrors를 0으로 만들고 lastError를 지워서, 실패 이력/경보 의미가 흐려졌다. 운영상 “이미 ok라 놔둬도 됨”은 아니다. 다만 현재도 일부는 lastRunStatus=error가 남아 있으므로, 무조건 긴급 rollback보다는 승인 후 선택적 state 복구가 맞다.

권장 판단:

  • delivery 보정 10건: 유지 가능. Telegram 대상 보정이라 기능상 유해 가능성 낮음.
  • state reset 4건: 감사/경보 정확성을 중시하면 선택적 rollback 권장. 단, 해리 승인 전에는 실행 금지.

수동 rollback 절차는 플레이북에 저장했다.

7. 플레이북 저장

저장 완료:

  • ~/knowledge-agent/500-signals/stale-cron-recovery-playbook.md

포함 내용:

  • 조사 전 백업 순서
  • 40개 cap 우회 방식
  • schema drift 검사
  • self-healer 실행 금지/격리
  • 복구 우선순위
  • 선택적 rollback 절차
  • 회귀 경보 샘플 사용법

8. 회귀 테스트 샘플

저장 완료:

  • ~/knowledge-agent/500-signals/stale-cron-regression-check.py

검증 실행:

python3 ~/knowledge-agent/500-signals/stale-cron-regression-check.py --threshold 20 --hours 24 --json

결과:

  • exit code: 2 — threshold 초과 경보
  • active stale count: 146
  • cause counts: ok-but-stale=136, never-run=7, schema-skipped=2, error=1

이 스크립트는 read-only다. jobs.json 수정, 크론 실행, 재시작을 하지 않는다.

9. 다음 수정이 승인될 경우 우선순위

이번 작업에서는 금지 조건 때문에 실행하지 않았다. 실제 복구가 승인되면 순서는 다음이 맞다.

  1. daily_context_snapshot.py의 cap 버그 수정: total/sample 분리.
  2. schema-skipped 2개 우선 수정.
  3. never-run 7개 중 id 없는 2개와 nextRun 없는 2개 정리.
  4. ok-but-stale 136개는 개별 job 문제가 아니라 scheduler/ticker 경로 문제로 보고, Hermes cron due-run 로그를 먼저 확인.
  5. self-healer state reset 4건은 승인 후 선택적 rollback 여부 결정.
  6. 회귀 경보 스크립트를 운영화하려면 먼저 기존 동일 목적 cron 존재 여부를 ~/.openclaw/cron/jobs.json에서 확인.

10. 자체 평가

  • 정확성: 4.8/5 — 원본/Critic/코드/jobs 상태를 직접 대조했고 146개 전수 파일을 남김.
  • 완성도: 4.8/5 — 플레이북, 회귀 샘플, diff CSV, full stale CSV/JSON/MD까지 저장.
  • 검증: 4.8/5 — 회귀 샘플을 실제 실행해 threshold 초과 exit code 2 확인.
  • 최소 변경: 5.0/5 — 크론/코드 수정 없음. knowledge-agent 문서와 샘플만 작성.

종합: 4.85/5