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_output이 cron_jobs와 cron_runs 양쪽에 존재하는 건 의도적 중복이다.
cron_jobs의 last_ 필드* → 빠른 조회용 캐시. 목록 화면에서 join 없이 최근 상태를 즉시 표시cron_runs테이블 → 전체 실행 이력 보관.max_run_history개수로 오래된 row pruning
expression도 마찬가지 — schedule JSON이 primary source지만, 사람이 읽기 쉬운 문자열을 캐싱하고 레거시 row 호환을 유지하기 위해 별도 컬럼으로 남겨둔 것.