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.
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 withcodex:. - Omit
sourcefilters 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-onlySELECT/WITH; use--attunefor memory mutation.tool_callsandtool_resultsdo not have timestamps. Joinmessages.- For normal user/assistant evidence, filter
COALESCE(m.is_meta, 0) = 0. summaries.sourceis a summary kind, not provider provenance. Provider source is onsessions.sourceandmessages.source.sessions.projectis a slug/fuzzy scope;sessions.project_pathis the absolute path when known;messages.cwdis 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()fromreferences/api-reference.mdwhen a specific message needs the original JSONL line. - Prefer SQL-side
COUNT,GROUP BY,MAX,ORDER BY, andLIMITover returning large row sets and hand-counting in the final answer.