Files
obelisk/references/schema.md
T
tommy0103 d5d5df46fa docs: split schema.md into focused references (api-reference.md + compact SQL map)
Extract helper API documentation into a standalone api-reference.md and
  reduce schema.md to a compact field/join/table map for raw SQL work.
  Add a Reference Map table to SKILL.md for quick routing by task type.
2026-06-18 01:52:44 +08:00

10 KiB

Obelisk -- Raw SQL Quick Reference

Read this before writing non-trivial sql() queries. It is a compact field and join map for raw SQL, not the full helper API manual.

  • Executable DDL: scripts/schema.sql
  • Helper signatures and return shapes: references/api-reference.md
  • Query recipes and synthesis patterns: references/query-patterns.md
  • FTS, alias, ordering, and compactness traps: references/pitfalls.md

Database path: ~/.obelisk/obelisk.sqlite. Older ~/.claude/obelisk.sqlite databases are copied forward on first open when the new database does not exist.

Source Model

Obelisk stores Claude Code and Codex transcripts in the same schema.

  • Claude rows use source='claude'.
  • Codex rows use source='codex'; root session and message IDs are prefixed with codex:.
  • Omit source filters unless provider provenance matters.
  • Codex child threads are represented through subagents; Codex may not have Claude-style workflow rows.

Scope Fields

Use the narrowest scope before text search.

Field Meaning Raw SQL note
sessions.project Provider-normalized project slug Use LIKE ? for fuzzy project filters
sessions.project_path Absolute project path inferred from cwd Use for exact local project identity
messages.cwd Working directory at message time Useful when a session spans directories
sessions.source / messages.source Transcript provider Use only when provider matters
messages.is_meta Injected/control-plane transcript material Ordinary evidence should filter it out

For ordinary conversation evidence in raw SQL, add:

COALESCE(m.is_meta, 0) = 0

Do not add that filter when investigating injected context, command envelopes, or transcript structure.

Tables

sessions

One row per root session.

Column Meaning
id Session ID (codex:<thread-id> for Codex roots)
title AI/session title
project Provider-normalized project slug
project_path Absolute project path when known
started_at, ended_at ISO timestamps
git_branch Branch at session time
version Provider CLI/app version
message_count Indexed user + assistant messages
jsonl_path Source JSONL path
source claude or codex

messages

Core evidence table.

Column Meaning
uuid Message ID
session_id FK to sessions.id
type, role User/assistant role fields
parent_uuid Conversation tree parent
timestamp ISO timestamp
text Extracted text, truncated to 10k chars
content_type text, thinking, tool_use, tool_result, or unknown
is_meta 1 for injected/control-plane messages
model Assistant model name
is_sidechain Retry/branch marker
agent_id Subagent/workflow agent ID
input_tokens, output_tokens Assistant token usage
cwd Working directory at message time
skill Skill that generated the response, if known
turn_duration_ms Wall-clock duration for the turn
source claude or codex

content_type='tool_use' is only a marker. Tool-call details live in tool_calls. content_type='tool_result' marks provider-emitted tool-result messages; structured tool-result rows live in tool_results.

tool_calls

One row per assistant tool invocation.

Column Meaning
id Tool-use ID
message_uuid Assistant message containing the call
session_id Denormalized session ID
name Tool name (Read, Edit, Bash, etc.)
input_json JSON-serialized input, truncated to 10k chars
file_path Extracted file path for file tools

tool_calls does not have timestamps. Join through messages.

tool_results

One row per tool result.

Column Meaning
tool_use_id FK to tool_calls.id
message_uuid User/tool-result message carrying the result
session_id Denormalized session ID
content Result text, truncated to 10k chars
file_path Tool result file path metadata, if any
is_error 1 when the provider marks the result as an error

tool_results does not have timestamps. Join through messages.

summaries

Session summary rows.

Column Meaning
id Summary ID
session_id FK to sessions.id
timestamp Summary timestamp
source Summary kind, such as away_summary; not provider source
content Summary text

subagents

Metadata for non-workflow subagent spawns.

Column Meaning
agent_id Subagent ID
session_id Parent session
parent_tool_use_id Tool call that spawned the subagent
agent_type Agent type label
description Assigned task
duration_ms Wall-clock duration
total_tokens Sum of indexed agent tokens

workflows

Workflow execution records.

Column Meaning
run_id Workflow run ID
session_id Parent session
task_id Task identifier
script Workflow script content, truncated
result_json JSON-serialized workflow result
timestamp Execution timestamp
agent_count Number of workflow agents
duration_ms, total_tokens Aggregate run cost
status Run status
workflow_name Name from workflow metadata

workflow_agents

Individual agents inside a workflow run.

Column Meaning
agent_id Workflow agent ID
run_id FK to workflows.run_id
session_id Parent session
agent_type, description Agent task metadata
phase, label Workflow positioning
model, state Runtime state
duration_ms, tokens, tool_calls Per-agent cost

memories

Human-approved markdown memory records. The markdown file at path is the durable memory; summary is the compact retrieval surface.

Column Meaning
id Memory ID
session_id Source session, if known
project Project slug for scoped recall
message_start, message_end Source message UUID range
path Normalized absolute markdown path
anchors Optional JSON array of recall anchors
summary English retrieval summary
created_at Registration timestamp
deleted_at Archive timestamp
deleted_reason Archive reason

Active memory means deleted_at IS NULL. Recall helpers omit archived rows. When using raw SQL for memory recall, include memories.deleted_at IS NULL.

index_state

Indexer progress and sentinel state.

Column Meaning
jsonl_path Source path or synthetic sentinel key
mtime Last indexed mtime
lines_processed Incremental line cursor

Sentinel keys include __last_build__, __app_heartbeat__, __app_last_successful_build__, __indexer_owner_app__, and __last_source_mtime__.

FTS Tables

Table Search surface Use
messages_fts messages.text Usually through search()
memories_fts memories.path, memories.summary Usually through memories({ query })

Prefer helpers for FTS. Raw MATCH syntax is easy to get wrong; see references/pitfalls.md before debugging FTS behavior.

Key Relationships

sessions.id        <--  messages.session_id
sessions.id        <--  tool_calls.session_id
sessions.id        <--  tool_results.session_id
sessions.id        <--  subagents.session_id
sessions.id        <--  workflows.session_id
sessions.id        <--  memories.session_id
messages.uuid      <--  tool_calls.message_uuid
messages.uuid      <--  tool_results.message_uuid
messages.uuid      <--  memories.message_start / memories.message_end
messages.agent_id  -->  subagents.agent_id
messages.agent_id  -->  workflow_agents.agent_id
tool_calls.id      <--  tool_results.tool_use_id
workflows.run_id   <--  workflow_agents.run_id

Safe SQL Joins

Tool calls with timestamps:

SELECT tc.id, tc.name, tc.file_path, m.timestamp, s.title
FROM tool_calls tc
JOIN messages m ON m.uuid = tc.message_uuid
JOIN sessions s ON s.id = tc.session_id
WHERE s.project LIKE ?
ORDER BY m.timestamp DESC
LIMIT 20;

Tool failures with timestamps:

SELECT tr.tool_use_id, tc.name, m.timestamp, substr(tr.content, 1, 200) AS error
FROM tool_results tr
JOIN tool_calls tc ON tc.id = tr.tool_use_id
JOIN messages m ON m.uuid = tr.message_uuid
WHERE tr.is_error = 1
ORDER BY m.timestamp DESC
LIMIT 20;

Ordinary message evidence:

SELECT m.uuid, m.role, m.timestamp, substr(m.text, 1, 220) AS snippet
FROM messages m
JOIN sessions s ON s.id = m.session_id
WHERE s.project LIKE ?
  AND COALESCE(m.is_meta, 0) = 0
ORDER BY m.timestamp DESC
LIMIT 20;

Active memories:

SELECT id, path, anchors, summary, session_id, created_at
FROM memories
WHERE project LIKE ?
  AND deleted_at IS NULL
ORDER BY created_at DESC
LIMIT 20;

Indexes

Common indexed filters:

  • messages(session_id)
  • messages(agent_id)
  • messages(session_id, timestamp)
  • sessions(source)
  • messages(source)
  • tool_calls(session_id, name)
  • tool_calls(file_path)
  • subagents(session_id)
  • workflows(session_id)
  • workflow_agents(run_id)
  • summaries(session_id)
  • memories(project)
  • memories(session_id)
  • memories(created_at)

Raw SQL Pitfalls

  • Start with helpers. Use raw sql() for exact joins, grouping, aggregation, or fields helpers do not expose.
  • sql() accepts only read-only SELECT/WITH; use --attune for memory mutation.
  • tool_calls and tool_results do not have timestamps. Join messages.
  • For normal user/assistant evidence, filter COALESCE(m.is_meta, 0) = 0.
  • summaries.source is a summary kind, not provider provenance. Provider source is on sessions.source and messages.source.
  • sessions.project is a slug/fuzzy scope; sessions.project_path is the absolute path when known; messages.cwd is per-message working directory.
  • Memory rows are archived with deleted_at; do not recall archived memories.
  • Indexed text and JSON fields are truncated to 10k chars. Use raw() from references/api-reference.md when a specific message needs the original JSONL line.
  • Prefer SQL-side COUNT, GROUP BY, MAX, ORDER BY, and LIMIT over returning large row sets and hand-counting in the final answer.