스케줄러는 자연어 일정 표현을 ScheduledJob으로 바꾸고, 디스크에 영속화하고, serve 데몬의 루프에서 발화하는 세 단계로 움직입니다. 코드는 core/scheduler/ 아래 평면 구조입니다.
파싱: 규칙 기반, LLM 없음
NLScheduleParser(core/scheduler/nl_scheduler.py)가 일정 표현을 해석합니다. 패턴 매칭만 쓰고 LLM을 호출하지 않으므로 결과가 결정적이고 비용이 없습니다. 결과는 세 가지 ScheduleKind(core/scheduler/models.py) 중 하나입니다.
| kind | 의미 | 필드 |
|---|---|---|
every | 고정 간격 반복 | every_ms |
cron | cron 표현식 매칭 | cron_expr (분 시 일 월 요일, 요일 0=일요일) |
at | 1회 실행 | 실행 시각 |
cron 매칭은 core/scheduler/triggers.py의 최소 구현 CronParser가 담당합니다. 5필드 형식이고 요일은 cron 표준 관례(0=일요일)를 따릅니다. 트리거 타입은 manual, scheduled(cron), event-driven(HookSystem 이벤트 구독) 셋입니다.
영속화
SchedulerService(core/scheduler/service.py)가 작업 목록을 .geode/scheduled_tasks.json에 저장합니다. 쓰기는 임시 파일 작성 후 os.replace로 원자적으로 바꾸고, 같은 디렉터리의 scheduled_tasks.lock으로 다중 프로세스 경합을 막습니다(core/scheduler/lock.py). 데몬 시작 시 저장된 작업을 다시 읽고, 꺼져 있던 동안 놓친 발화를 복구합니다.
발화
serve 데몬의 비동기 루프가 매 주기 스케줄러 큐를 drain합니다 (core/cli/typer_serve.py). 발화 시각에는 결정적 jitter가 더해집니다.
# core/scheduler/jitter.py frac = sha256(job_id)[:4] / 2^32 # [0, 1) 고정값 jitter = min(frac * interval * 0.1, 15min) # 전방 오프셋 fire_at = nominal + jitter
같은 작업은 재시작 후에도 항상 같은 오프셋에 발화하고, 같은 정각을 공유하는 다른 작업들은 시간상 분산됩니다. 발화된 작업의 액션 프롬프트는 SCHEDULER 모드 세션(wall-clock 300초 상한, headless 도구 차단)으로 실행되고, 트리거 시점에 HookEvent.TRIGGER_FIRED가 발화됩니다 (core/scheduler/triggers.py).
설정 손잡이
| Settings 필드 | 기본값 | 의미 |
|---|---|---|
scheduler_auto_start | true | 데몬 부팅 시 스케줄러 자동 시작 |
scheduler_interval_s | 1.0 | 발화 검사 주기 |
scheduler_jitter_enabled | true | jitter on/off |
scheduler_max_jitter_ms | 900000 | jitter 상한 15분 |
전체 필드는 core/config/_settings.py가 SoT입니다.
보조 모듈
core/scheduler/calendar_bridge.py. 캘린더 어댑터(core/mcp/apple_calendar_adapter.py,core/mcp/google_calendar_adapter.py)의 이벤트를 스케줄 소스로 연결합니다.core/scheduler/predefined.py./schedule목록에 참고용으로 표시되는 템플릿입니다. 활성 작업이 아닙니다.core/scheduler/timezone.py,core/scheduler/serialization.py. 시간대 정규화와 JSON 직렬화.
실패 모드
| 증상 | 원인 | 해법 |
|---|---|---|
| 발화가 전혀 없음 | 데몬 정지 또는 scheduler_auto_start=false | pgrep -f "geode serve" 확인 후 데몬을 띄우고 설정을 점검합니다. |
| 요일 cron이 하루 어긋남 | 요일 관례 혼동 | 이 파서는 0=일요일입니다. 월요일은 1입니다. |
| 작업 파일이 깨짐 | 외부에서 JSON을 직접 편집 | .geode/scheduled_tasks.json은 손으로 고치지 말고 /schedule로 관리합니다. |