research

zeroclaw cron 내부 구조

zeroclaw cron 내부 구조

소스: zeroclaw-labs/zeroclaw src/cron/ 직접 조사. 관련 파일: types.rs, store.rs, scheduler.rs


SQLite 스키마

zeroclaw는 {workspace_dir}/cron/jobs.db 경로에 SQLite를 열고 최초 연결 시 CREATE TABLE IF NOT EXISTS로 초기화한다.

cron_jobs 테이블

CREATE TABLE IF NOT EXISTS cron_jobs (
  id               TEXT PRIMARY KEY,
  expression       TEXT NOT NULL,
  command          TEXT NOT NULL,
  schedule         TEXT,            -- JSON: Schedule 태그드 유니온
  job_type         TEXT DEFAULT 'shell',
  prompt           TEXT,
  name             TEXT,
  session_target   TEXT DEFAULT 'isolated',
  model            TEXT,
  enabled          INTEGER DEFAULT 1,
  delivery         TEXT,            -- JSON: DeliveryConfig
  delete_after_run INTEGER DEFAULT 0,
  allowed_tools    TEXT,            -- JSON array
  created_at       TEXT NOT NULL,
  next_run         TEXT NOT NULL,
  last_run         TEXT,
  last_status      TEXT,
  last_output      TEXT
);
CREATE INDEX idx_cron_jobs_next_run ON cron_jobs(next_run);

cron_runs 테이블

CREATE TABLE IF NOT EXISTS cron_runs (
  id          INTEGER PRIMARY KEY AUTOINCREMENT,
  job_id      TEXT NOT NULL,
  started_at  TEXT NOT NULL,
  finished_at TEXT NOT NULL,
  status      TEXT NOT NULL,
  output      TEXT,
  duration_ms INTEGER,
  FOREIGN KEY (job_id) REFERENCES cron_jobs(id) ON DELETE CASCADE
);

ERD


Schedule 타입 (JSON 태그드 유니온)

// Cron (반복)
{ "kind": "cron", "expr": "*/5 * * * *", "tz": "Asia/Seoul" }

// At (일회성 — 특정 시각)
{ "kind": "at", "at": "2024-03-22T09:00:00Z" }

// Every (주기 — ms 단위)
{ "kind": "every", "every_ms": 60000 }

핵심 쿼리 패턴

due_jobs — 실행할 job 가져오기

SELECT ... FROM cron_jobs
WHERE enabled = 1 AND next_run <= ?now
ORDER BY next_run ASC
LIMIT ?max_tasks

reschedule_after_run — 실행 후 처리

  • At 스케줄: enabled = 0 (자동 비활성화, one-shot)
  • Cron / Every: next_run 재계산 후 업데이트

run history pruning

DELETE FROM cron_runs
WHERE job_id = ?id
  AND id NOT IN (
    SELECT id FROM cron_runs WHERE job_id = ?id
    ORDER BY started_at DESC LIMIT ?keep
  )

설계 패턴 요약

| 패턴 | 내용 | |------|------| | 타임스탬프 저장 방식 | RFC3339 문자열 (UNIX int 아님) | | 복잡한 타입 | JSON 직렬화해서 TEXT 컬럼에 저장 | | 스키마 마이그레이션 | add_column_if_missing — PRAGMA table_info → ALTER TABLE | | 출력 크기 제한 | 16KB truncate + marker | | 폴링 기반 스케줄링 | DB를 single source of truth로, next_run 인덱스 폴링 | | one-shot 처리 | At 타입 → 실행 후 enabled=0 |


zeroclaw가 없는 것 (Naran이 추가해야 할 것)

  • 캘린더 연동 — GCal sync, iCal UID
  • 마감일(due date) 개념 (deadline vs scheduled time 구분)
  • 우선순위, 에너지 레벨 같은 GTD/time-blocking 메타데이터
  • 반복 이벤트 전개 — RRULE 기반 인스턴스 생성
  • 태그/컨텍스트 분류

Attribute 상세 설명

cron_jobs

| attribute | 의미 | | ------------------ | ------------------------------------------------------------------------------------------------------------------- | | id | UUID v4. job 고유 식별자 | | expression | cron 표현식 문자열 (*/5 * * * *). 표시용 + 레거시 fallback — schedule JSON이 있으면 그게 우선, NULL인 구버전 row 읽을 때 이 필드로 Schedule 복원 | | command | shell 타입 전용 실행 명령. agent 타입이면 '' 저장 | | schedule | JSON blob. kind 필드로 분기되는 tagged union (cron / at / every) | | job_type | 'shell' = command 실행, 'agent' = LLM에게 prompt 전달 | | prompt | agent 타입 전용 LLM 프롬프트. shell이면 NULL | | name | 사람이 읽기 쉬운 이름. optional, UI 표시용 | | session_target | 'isolated' = 독립 새 세션, 'main' = 메인 대화 세션에 붙여서 실행 | | model | agent 타입에서 쓸 LLM 모델명. NULL이면 기본 모델 | | enabled | 1 = 활성, 0 = 비활성. due_jobs 쿼리에서 enabled = 1만 조회 | | delivery | JSON blob. 실행 결과 전달 설정 — mode (none | announce), channel, to, best_effort | | delete_after_run | 1이면 실행 후 job 자동 삭제. At 스케줄 등록 시 자동으로 1 세팅 | | allowed_tools | JSON array. agent job이 쓸 수 있는 tool 이름 목록. NULL이면 모든 tool 허용 | | created_at | RFC3339. job 최초 등록 시각 | | next_run | RFC3339. 스케줄러 핵심 컬럼 — 다음 실행 예정 시각. 인덱스 있음. 스케줄러가 이 값 기준으로 폴링 | | last_run | RFC3339. 마지막 실행 완료 시각. 미실행이면 NULL | | last_status | 'ok' | 'error'. 마지막 실행 결과 | | last_output | 마지막 실행 출력. 16KB 초과 시 자동 truncate + ...[truncated] 마커 |

cron_runs

| attribute | 의미 | | ------------- | ----------------------------------------------------------------- | | id | AUTOINCREMENT integer. 실행 이력 고유 식별자 | | job_id | cron_jobs.id FK. ON DELETE CASCADE — job 삭제 시 관련 run 전부 자동 삭제 | | started_at | RFC3339. 실행 시작 시각 | | finished_at | RFC3339. 실행 완료 시각 | | status | 'ok' | 'error' | | output | 실행 출력. 16KB truncate 동일 적용 | | duration_ms | 실행 소요 시간 (밀리초) |

설계 의도 — 의도적 중복 구조

last_run / last_status / last_outputcron_jobscron_runs 양쪽에 존재하는 건 의도적 중복이다.

  • cron_jobs의 last_ 필드* → 빠른 조회용 캐시. 목록 화면에서 join 없이 최근 상태를 즉시 표시
  • cron_runs 테이블 → 전체 실행 이력 보관. max_run_history 개수로 오래된 row pruning

expression도 마찬가지 — schedule JSON이 primary source지만, 사람이 읽기 쉬운 문자열을 캐싱하고 레거시 row 호환을 유지하기 위해 별도 컬럼으로 남겨둔 것.