실행 순서는 project-local sessions.db:session_events, 조회 가능한 lifecycle은 hook_events, portable view는 run-bound events.jsonl에 남습니다. 같은 시각의serve.log까지 맞추면 “가드가 정상 종료했는지”와 “외부 호출에서 실제로 매달렸는지”를 구분할 수 있습니다.
1. 최근 이벤트를 조회합니다
HookEventStore()는 현재 workspace의 database를 해석합니다. session key를 알고 있으면 아래처럼 최근 이벤트를 시간순으로 봅니다. 저장 payload는 raw prompt, user input, tool input/result를 포함하지 않습니다.
uv run python - <<'PY'
from core.observability.event_store import HookEventStore
store = HookEventStore()
try:
rows = store.read(limit=20, session_key="<session_key>")
for row in reversed(rows):
print(row.occurred_at, row.event, row.status, row.action)
finally:
store.close()
PY2. lifecycle pair를 확인합니다
llm_call_start뒤llm_call_end가 없으면 model adapter 대기를 확인합니다.tool_exec_start뒤tool_exec_end가 없으면 tool 실행 또는 process 종료 경계를 확인합니다.status=blocked는 공개 훅 또는 정책이 의도적으로 막은 실행입니다.status=failed는 canonical terminal row에 실패가 반영된 경우입니다.
3. session record와 daemon log를 맞춥니다
정본 실행 순서는 sessions.db:session_events, run_dir가 있는 실행의 portable view는 events.jsonl에서 확인합니다. 마지막tool.called에 대응하는 tool.completed가 없거나 동일 호출이 반복되면~/.geode/logs/serve.log에서 같은 timestamp의 traceback, timeout, credential 오류를 찾습니다.
4. 복구 후 확인합니다
원인을 고치고 재실행한 뒤 새 timeline이 canonicalsession.ended까지 이어지는지 확인합니다. 이벤트 table은 보존 등급과 전체 행수 상한으로 자동 prune되므로 장기 보존이 필요한 증거는 별도 run artifact로 내보냅니다.