GEODE의 관측성은 목적별 저장소를 사용합니다. query/filter/aggregate가 필요한 이력과 훅 이벤트는 SQLite, 순서대로 읽고 내보내는 실행 artifact는 versioned JSONL, 평가는 immutable trajectory, 프로세스 진단은 rotating log입니다. raw prompt와 tool 결과를 운영 event table에 복제하지 않습니다.
저장소 지도
| 렌즈 | 저장소 | 용도 |
|---|---|---|
| Resume checkpoint | sessions.db:sessions/messages | 다음 model request와 compaction 상태 |
| Session record | sessions.db:session_events | 사용자·assistant·tool·sub-agent 실행 순서 |
| Hook events | sessions.db:hook_events | 세션/이벤트/status/action 조회와 보존 정책 |
| Run projection | events.jsonl | 활성 run timeline, tail, portable artifact |
| Trajectory | geode.trajectory@1 | 재생·비교·verifier 연결용 immutable export |
| Public release | geode.trajectory-release@1 | 검토된 trajectory와 SHA-256 manifest |
| Evidence ledger | ~/.geode/evidence/<session>.jsonl | session/turn/call로 연결된 claim·approval·verdict |
| Session metrics | 메모리 + run summary | 토큰, 비용, latency percentile |
| Usage ledger | ~/.geode/usage/YYYY-MM.jsonl | LLM 호출별 비용 time series |
| Scheduler job tail | .geode/scheduler_logs/*.jsonl | job별 portable bounded history |
| Process logs | ~/.geode/logs/ | traceback과 외부 시스템 진단 |
직렬화 정본은 packaged Draft 2020-12 schema인 geode.session-event@1, geode.run-event@1, geode.trajectory@1입니다.
캐시·토큰·비용: 같은 범위를 비교합니다
UI는 입력·출력과 캐시 읽기·쓰기 수를 구분합니다. OpenAI의 입력에는 캐시가 포함되지만 Anthropic의 일반 입력·캐시 읽기·생성은 분리돼 있습니다. cache_hit_rate는 읽기 / (읽기 + 생성) 비율이지 프롬프트 전체의 캐시 적중률이 아닙니다. API 단가 추정은 구독 계정 청구액이 아닙니다.
Durable 호출 기록은 명시적인 0과 미보고 값을 구분합니다. Harbor는 session과 attempt ID로 시작·종료를 결합하며, 불완전한 합계는 null, 관측된 부분합은 observed_sum으로 남깁니다. 새 기록은 action loop뿐 아니라 연결된 reflection·judge·hosted search·text 호출에 purpose, credential source, requested effort를 남깁니다. 범위는 recorded-runtime-llm-attempts-only이며 whole_runtime_complete=false입니다. 과거 loop-only 기록의 범위나 누락값은 바꾸지 않습니다.
Native Harbor 실행은 종료 시 새 background 작업을 막고, 이미 시작한 Dreaming 작업은 원래 agent deadline 안에서만 마무리합니다. 기한 이후의 정리 시간에는 새 모델 실행을 허용하지 않습니다. worker와 background 작업을 정리한 뒤 각 세션의 관측 상태와 canonical trajectory를 내보냅니다. 저장 장애, 미종료 작업, 불완전한 export는 관측 검증을 통과할 수 없습니다.
읽기 전용 검증기는 동결된 실행·task·source 식별자, 호출 합계, session 종료 상태, ATIF와 derived replay의 해시를 대조합니다. 필수 cache 값의 부재와 관측 파일의 손상은 별도 판정합니다. 이 결과만으로 다음 실행을 승인하지는 않습니다. 자원·timeout·재시도·인증 preflight와 해당 runtime의 호출 생산자 및 종료 경로 검증이 함께 필요하며, 검증 실패가 task verifier의 원래 reward를 바꾸지는 않습니다.
Legacy 월별 원장과 일부 UI 합산은 필드 부재를 끝까지 보존하지 않습니다. 과거 캐시 0은 미사용 증거가 아니며, 기록 범위는 벤치마크의 성공률 분모와도 다릅니다. 보정 자료는 원본 결과를 덮어쓰지 않고 별도 증거로 연결합니다.
Session record 운영과 migration
# Inspect first; this does not write SQLite. geode session migrate-records --source old/transcript.jsonl --dry-run # Import is digest-idempotent and leaves the source unchanged. geode session migrate-records --source old/transcript.jsonl # Export before pruning canonical history. geode session list geode session export-trajectory <session-id> --out trajectory.json geode session prune-records --retention-days 180
migrate-records는 파일·디렉터리를 받고 source SHA-256으로 같은 입력의 중복 삽입을 막으며 원본을 수정하지 않습니다.export-trajectory는 SQLite 정본에서 검증된geode.trajectory@1을 만들고 event가 없으면 실패합니다.prune-records는 보존 기간보다 오래된 명시적 terminal session만 삭제하고 active/stale session은 남깁니다.- 삭제한 canonical row는 run projection에서 복구한다고 가정하지 마세요. 보존할 실행은 prune 전에 export합니다.
SessionTranscript/RunTranscript writer와 alias는 v1.0.12 grace release 이후 제거됐습니다. 기존transcript.jsonl/dialogue.jsonl은 명시적 migration 입력으로 계속 읽을 수 있습니다. 새 연동은SessionTimeline, RunTimeline,events.jsonl을 사용합니다.
Trajectory 품질과 외부 루프
exporter는 event ID uniqueness, ordinal 연속성, session/turn/call correlation, tool call/result pairing, orphan, truncated/corrupt payload를 다시 계산해 integrity.quality에 기록합니다. public staging은 producer가 적은 count와 quality를 신뢰하지 않고 재검산하며 privacy review, secret scan, trajectory ID uniqueness, file digest, read-back을 모두 통과해야 합니다.
| 표시 | 의미 | 공개 admission |
|---|---|---|
scope_complete | event 순서, correlation, tool pair가 실행 범위를 온전히 표현 | 항상 true |
replay_complete | 공개 payload만으로 완전 재생 가능 | 기본 true; 검토된 private body digest만 명시적 완화 |
complete | 이전 reader용 보수적 alias | replay_complete와 동일 |
SIL의 events.jsonl 실행 타임라인, mutation/attribution 원장, Inspect .eval assay와 Crucible의crucible.evidence.v3는 계속 각자의 정본입니다. trajectory는evidence_refs와 source artifact SHA-256으로 이를 연결하는 replay sidecar이며 verdict를 대체하거나 승격 권한을 갖지 않습니다. 과거geode.trajectory@YYYY-MM-DD 공개 파일은 수정하지 않고 메모리에서@1으로 정규화합니다.
| 외부 정본 | trajectory reference | GEODE 권한 |
|---|---|---|
SIL Inspect .eval | kind=sil_eval, schema_id=inspect_ai.eval@native, source SHA-256 | scored archive를 digest로 연결; judge 결과를 대체하지 않음 |
tau2 results.json | kind=native_receipt, schema_id=tau2.results@native | native score receipt를 그대로 정본으로 유지 |
| tau2 runtime profile / attempt manifest | snapshot v4의 sibling path + SHA-256, trajectory artifact_digests | 실행 표면과 retry 선택을 증명하며 native reward를 대체하지 않음 |
| Crucible frozen contract | identity preflight가 끝난 경우에만 kind=crucible_evidence | verdict나 promotion authority를 얻지 않음 |
로컬 export를 privacy-reviewed public candidate로 승격하고 append-only artifact PR로 게시하는 절차는 trajectory 게시 가이드를 따릅니다.
한 trigger, 한 durable row
RuntimeEventBus는 handler chain이 끝난 뒤HookDispatch를 sink에 한 번 보냅니다. 그래서 sync/async, emit 경로마다 writer를 반복하지 않습니다. legacy 실패나 승인 이벤트처럼 canonical 이벤트와 의미가 겹치는 신호는 외부 handler에는 전달하지만 SQL과 JSONL projection에는 중복 기록하지 않습니다.
이벤트 조회
from core.observability.event_store import HookEventStore
store = HookEventStore()
try:
for row in store.read(limit=50, event_filter="tool_exec_ended"):
print(row.session_key, row.status, row.action, row.occurred_at)
finally:
store.close()row는 event, dispatch mode, status, handler error count, actor/action/entity, bounded payload hash를 가집니다. payload의 문자열·collection·깊이·전체 bytes에 상한이 있고 secret pattern을 redaction합니다.
보존과 수명주기
- high-volume 7일, standard 30일, audit 180일
- project database 전체 100,000행 상한
- append 중 incremental prune + 명시적
prune_events() session_events는 명시적으로 종료된 세션만 180일 후 pruneevents.jsonl은 16 MiB에서 명시적 truncation marker와 함께 compact- runtime shutdown이 producer를 멈춘 뒤 hook sink와 SQLite connection을 닫음
- latency percentile sample과 model cardinality도 bounded
실패 가시성
handler 실패는 다른 handler를 막지 않으며 row의handler_error_count에 반영됩니다. sink 실패는 event 종류별로 한 번 WARNING하고 agentic loop는 계속합니다. 멈춘 실행의 조사 순서는 멈춘 실행 디버깅을 따릅니다.