Generated from the code by attempt schema --format markdown. Do not edit by hand: cargo test -p attemptdb-query --test catalog fails when this file and the schema disagree, and UPDATE_GOLDEN=1 regenerates it.
AttemptDB records what coding agents tried. A hook on each agent (Claude Code, Codex, Cursor, Gemini CLI) appends immutable events; a deterministic projector derives sessions, turns, tool calls, attempts, work units, decisions, handoffs and a causal graph from them. Queries run over both layers at once: the facts in events, and the inferences everywhere else, each carrying the event ids it was built from.
-
Two languages share one surface. AttemptQL verbs (SHOW, WHY, TRACE, STATE, DIFF, WHAT IS, EXPLAIN) answer the common questions in one line; plain SQL in the DataFusion dialect answers everything else over the same tables. Prefer the verb when one fits: it applies the retraction and scope rules for you.
-
The engine is read-only. Only SELECT, WITH, VALUES, EXPLAIN, DESCRIBE, SHOW and the AttemptQL verbs are accepted, one statement per call. There is no INSERT, no CREATE, no GRANT: history is appended by capture, never by a query.
-
eventsis fact; every other table is inference. An event was observed and is immutable. A session, turn, tool call, attempt, work unit, decision, handoff, edge or conflict was derived by the projector, and each carriesevidence(the event ids it was built from),confidence(0.0-1.0) and usuallyalgorithm_version. Never present an inferred row as something the agent did; say what it was inferred from. -
Ids are readable prefixed strings, not UUIDs:
ev_event,ses_session,trn_turn,tc_tool call,att_attempt,wu_work unit,dec_decision,cmt_commit,prj_project,dev_device. Compare them as text.events_rawis the same stream with the storage types instead (16-byte UUIDs, dictionary-encoded strings); readeventsunless you need the raw layout. -
Times are
timestamp(microsecond, UTC).observed_atis when the agent did it,captured_atwhen the hook recorded it,ingested_atwhen the database accepted it. Order history byobserved_at; measure capture lag with the other two. -
Retracted rows are hidden by AttemptQL and visible to SQL.
SHOWdrops them unless the statement saysINCLUDING RETRACTED; a bareSELECTdoes not, so filterretracted = falseyourself onevents,sessions,turns,tool_callsandattempts. -
Content may be absent by design. Under
capture_mode = 'metadata_only'the columns that carry text —objective,rationale,note,content_json,raw_json— are null for every row, and that is a privacy setting, not missing data. Checkevents.capture_modebefore concluding an agent had no objective. -
Counts belong to the projection, not to SQL aggregates you re-derive.
sessions.turn_count,attempts.tool_call_countand the rest are computed with the retraction rules applied; recomputing them with COUNT(*) over the child table gives a different (and usually wrong) number. -
Scope is a filter, not a mode. There is one database per install holding every project; a question about one repository is
WHERE project_name = '…'or the--projectflag, never a different connection.
fact · one row per observed event
The log. Every row was written by a hook (or reconstructed from an agent's own transcript) and is immutable: nothing in AttemptDB ever updates an event. Start here when a derived row looks wrong, and finish here when a claim needs proof — every inference cites these ids in its evidence.
Joins: session_id → sessions.session_id · span_id → tool_calls.tool_call_id
| column | type | null | meaning |
|---|---|---|---|
event_id |
text | UUIDv7 of the event (ev_…). Immutable, unique, and the only thing an inference ever cites. |
|
schema_version |
uint16 | Event schema version this row was written under (spec/event-v1.schema.json). |
|
device_id |
text | The machine that captured it (dev_…). One database can hold several devices after a sync or an import. |
|
source_seq |
uint64 | Per-device monotonic sequence. Together with device_id it makes the event's arrival order total. |
|
hlc |
uint64 | Hybrid logical clock: orders events across devices when wall clocks disagree. | |
observed_at |
timestamp | When the agent did the thing. Order history by this. | |
captured_at |
timestamp | When the hook recorded it. captured_at - observed_at is capture lag. |
|
ingested_at |
timestamp | yes | When the database accepted it. Null for events read straight from a segment. |
provider |
text | Which coding agent. attemptdb marks events AttemptDB wrote itself (corrections, retractions, capture tests); any other adapter contributes its own identifier, so match the values you know rather than assuming the list is closed. Common values: claude_code, codex, cursor, gemini_cli, attemptdb (open vocabulary — others appear). |
|
provider_version |
text | yes | The agent's own version string, when it reported one. |
adapter_version |
text | The AttemptDB adapter that normalised the payload. Changes here can change every derived row. | |
hook_version |
text | yes | The hook binary that captured it. Null for events reconstructed from a transcript. |
capture_mode |
text | The privacy mode in force when the event was written. Under metadata_only every content column below is null by design. Values: metadata_only, local_semantic, full_sync. |
|
provider_event_name |
text | The provider's own name for the hook that fired, before normalisation. | |
kind |
text | The canonical event kind. This is the column to filter on; provider_event_name is provider-specific. Values: session_started, session_ended, prompt_submitted, tool_call_started, tool_call_finished, tool_call_failed, permission_requested, permission_denied, notification, agent_message, turn_stopped, turn_failed, subagent_started, subagent_stopped, task_created, task_completed, compaction_started, compaction_finished, config_changed, cwd_changed, file_changed, worktree_created, worktree_removed, correction, retraction, capture_test, unknown. |
|
project_id |
text | Stable id of the repository (prj_…), derived from its root path and remote. |
|
project_root |
text | Absolute path of the repository root on the capturing machine. | |
project_name |
text | owner/repo when a git remote is known, otherwise the directory name. This is what a human filters on. |
|
repo_remote |
text | yes | Git remote URL, when the repository has one. |
branch |
text | yes | Git branch at the time, when the provider reported one. |
head |
text | yes | The commit HEAD pointed at when the event was observed. |
session_id |
text | The agent session (ses_…), derived from the provider's own session id. |
|
provider_session_id |
text | The provider's own session identifier, as it wrote it. | |
provider_turn_id |
text | yes | The provider's own turn identifier, when it has one. |
span_id |
text | yes | The tool call this event belongs to (tc_…): started and finished events of one call share it. |
parent_span_id |
text | yes | The enclosing span, for nested calls. |
agent_id |
text | Which agent instance acted (agt_…): a subagent has its own. |
|
agent_type |
text | yes | The agent's role as the provider named it — the main loop, or a named subagent. |
parent_agent_id |
text | yes | The agent that spawned this one. |
model |
text | yes | Model name the provider reported for the turn. |
provider_agent_id |
text | yes | The provider's own agent identifier. |
tool_name |
text | yes | The tool as the provider named it (Bash, Edit, shell, …). |
tool_category |
text | yes | The normalised category. Filter on this to compare providers. Values: shell, file_read, file_write, file_edit, search, web, mcp, subagent, plan, notebook, other. |
tool_call_id |
text | yes | The provider's own call id, when it issues one. |
path_logical |
text | yes | The path as the agent wrote it, absolute or not. |
path_relative |
text | yes | The same path relative to the repository root. Filter on this: it is stable across machines. |
paths_json |
text | yes | Every path the call touched, as a JSON array of repository-relative strings. |
outcome_status |
text | yes | How the call ended. Values: success, failure, denied, cancelled, unknown. |
outcome_class |
text | yes | A coarser reason, when the provider gave one. Common values: exit_code, denied, timeout, cancelled (open vocabulary — others appear). |
exit_code |
int32 | yes | Process exit status, when the provider reported one. |
duration_ms |
uint64 | yes | Wall-clock milliseconds. |
attrs_json |
text | The metadata allowlist as JSON (RFC 0006 §4). OTel observations use source=otel and x_otel_signal with typed x_otel_* fields; these complement hooks and do not advance work state. Cumulative metric samples must not be summed as separate usage. | |
content_json |
text | yes | Prompt, message and command text. Content: null under metadata_only, and moved to an encrypted blob when a key exists. |
raw_json |
text | yes | The provider's original payload. Content, same rules as content_json. |
content_ref |
text | yes | Blob id holding content_json when it was written out of line and encrypted. |
raw_ref |
text | yes | Blob id holding raw_json, same. |
unknown_json |
text | yes | Fields the adapter did not recognise, kept verbatim so an upgrade can read them. Never silently dropped. |
retracted |
bool | True when a Retraction covers this event or its session. |
fact · one row per observed event, in storage types
The same stream as events with the on-disk types instead of readable ones: 16-byte UUIDs rather than ev_… strings, dictionary-encoded providers and kinds. Read it when you are checking the storage layer or comparing against a segment; read events for everything else.
| column | type | null | meaning |
|---|---|---|---|
event_id |
uuid | UUIDv7 of the event (ev_…). Immutable, unique, and the only thing an inference ever cites. |
|
schema_version |
uint16 | Event schema version this row was written under (spec/event-v1.schema.json). |
|
device_id |
uuid | The machine that captured it (dev_…). One database can hold several devices after a sync or an import. |
|
source_seq |
uint64 | Per-device monotonic sequence. Together with device_id it makes the event's arrival order total. |
|
hlc |
uint64 | Hybrid logical clock: orders events across devices when wall clocks disagree. | |
observed_at |
timestamp | When the agent did the thing. Order history by this. | |
captured_at |
timestamp | When the hook recorded it. captured_at - observed_at is capture lag. |
|
ingested_at |
timestamp | yes | When the database accepted it. Null for events read straight from a segment. |
provider |
dict | Which coding agent. attemptdb marks events AttemptDB wrote itself (corrections, retractions, capture tests); any other adapter contributes its own identifier, so match the values you know rather than assuming the list is closed. Common values: claude_code, codex, cursor, gemini_cli, attemptdb (open vocabulary — others appear). |
|
provider_version |
text | yes | The agent's own version string, when it reported one. |
adapter_version |
text | The AttemptDB adapter that normalised the payload. Changes here can change every derived row. | |
hook_version |
text | yes | The hook binary that captured it. Null for events reconstructed from a transcript. |
capture_mode |
dict | The privacy mode in force when the event was written. Under metadata_only every content column below is null by design. Values: metadata_only, local_semantic, full_sync. |
|
provider_event_name |
dict | The provider's own name for the hook that fired, before normalisation. | |
kind |
dict | The canonical event kind. This is the column to filter on; provider_event_name is provider-specific. Values: session_started, session_ended, prompt_submitted, tool_call_started, tool_call_finished, tool_call_failed, permission_requested, permission_denied, notification, agent_message, turn_stopped, turn_failed, subagent_started, subagent_stopped, task_created, task_completed, compaction_started, compaction_finished, config_changed, cwd_changed, file_changed, worktree_created, worktree_removed, correction, retraction, capture_test, unknown. |
|
project_id |
uuid | Stable id of the repository (prj_…), derived from its root path and remote. |
|
project_root |
dict | Absolute path of the repository root on the capturing machine. | |
project_name |
dict | owner/repo when a git remote is known, otherwise the directory name. This is what a human filters on. |
|
repo_remote |
text | yes | Git remote URL, when the repository has one. |
branch |
text | yes | Git branch at the time, when the provider reported one. |
head |
text | yes | The commit HEAD pointed at when the event was observed. |
session_id |
uuid | The agent session (ses_…), derived from the provider's own session id. |
|
provider_session_id |
text | The provider's own session identifier, as it wrote it. | |
provider_turn_id |
text | yes | The provider's own turn identifier, when it has one. |
span_id |
uuid | yes | The tool call this event belongs to (tc_…): started and finished events of one call share it. |
parent_span_id |
uuid | yes | The enclosing span, for nested calls. |
agent_id |
uuid | Which agent instance acted (agt_…): a subagent has its own. |
|
agent_type |
text | yes | The agent's role as the provider named it — the main loop, or a named subagent. |
parent_agent_id |
uuid | yes | The agent that spawned this one. |
model |
text | yes | Model name the provider reported for the turn. |
provider_agent_id |
text | yes | The provider's own agent identifier. |
tool_name |
dict | yes | The tool as the provider named it (Bash, Edit, shell, …). |
tool_category |
dict | yes | The normalised category. Filter on this to compare providers. Values: shell, file_read, file_write, file_edit, search, web, mcp, subagent, plan, notebook, other. |
tool_call_id |
text | yes | The provider's own call id, when it issues one. |
path_logical |
text | yes | The path as the agent wrote it, absolute or not. |
path_relative |
text | yes | The same path relative to the repository root. Filter on this: it is stable across machines. |
paths_json |
text | yes | Every path the call touched, as a JSON array of repository-relative strings. |
outcome_status |
dict | yes | How the call ended. Values: success, failure, denied, cancelled, unknown. |
outcome_class |
text | yes | A coarser reason, when the provider gave one. Common values: exit_code, denied, timeout, cancelled (open vocabulary — others appear). |
exit_code |
int32 | yes | Process exit status, when the provider reported one. |
duration_ms |
uint64 | yes | Wall-clock milliseconds. |
attrs_json |
text | The metadata allowlist as JSON (RFC 0006 §4). OTel observations use source=otel and x_otel_signal with typed x_otel_* fields; these complement hooks and do not advance work state. Cumulative metric samples must not be summed as separate usage. | |
content_json |
text | yes | Prompt, message and command text. Content: null under metadata_only, and moved to an encrypted blob when a key exists. |
raw_json |
text | yes | The provider's original payload. Content, same rules as content_json. |
content_ref |
text | yes | Blob id holding content_json when it was written out of line and encrypted. |
raw_ref |
text | yes | Blob id holding raw_json, same. |
unknown_json |
text | yes | Fields the adapter did not recognise, kept verbatim so an upgrade can read them. Never silently dropped. |
inference · one row per agent session
One run of a coding agent, from the first event that named a session id to the last. Whether it is still open is state; whether it is still alive is last_event_at, because agents are killed far more often than they exit.
| column | type | null | meaning |
|---|---|---|---|
session_id |
text | The session (ses_…). |
|
provider |
text | Which coding agent produced the underlying events. Common values: claude_code, codex, cursor, gemini_cli, attemptdb (open vocabulary — others appear). |
|
provider_session_id |
text | The provider's own session id, for cross-checking against its logs. | |
project_id |
text | Stable id of the repository (prj_…), derived from its root path and remote. |
|
project_name |
text | owner/repo when a git remote is known, otherwise the directory name. This is what a human filters on. |
|
state |
text | Whether an end event was observed. open also covers a session that was killed without one. Values: open, closed. |
|
started_at |
timestamp | When the row's first evidence was observed. | |
ended_at |
timestamp | yes | When the row's last evidence was observed. Null while it is still open. |
end_reason |
text | yes | Why it ended, as the provider reported it. |
start_source |
text | yes | How the session began, as the provider reported it — a fresh start, a resume, a compaction. |
event_count |
int64 | Events in the session. | |
turn_count |
int64 | Turns in the session. | |
prompt_count |
int64 | Prompts the human submitted. | |
tool_call_count |
int64 | How many tool calls this row contains. Computed with the retraction rules applied — do not re-derive it with COUNT(*). | |
failure_count |
int64 | Tool calls that ended in failure. | |
agents |
list | Every agent instance that acted in the session, main loop and subagents. | |
coverage |
text | How complete the capture is. full means hooks recorded everything; partial and minimal mean some of this session was reconstructed from a transcript, so absence of a row is not evidence of absence. Values: full, partial, minimal, unknown. |
|
first_event_id |
text | First event of the row, in observation order. | |
last_event_id |
text | Last event of the row, in observation order. | |
last_event_at |
timestamp | When the newest event of the session was observed. This, not ended_at, is what tells you a session is still live. |
|
start_event_id |
text | yes | The event that opened the row, when one was observed. |
end_event_id |
text | yes | The event that closed the row, when one was observed. |
evidence |
list | The event ids this row was inferred from. The whole point of an inference: follow these to check the claim. | |
confidence |
float32 | 0.0-1.0. How strongly the evidence supports the row, not how important the row is. | |
retracted |
bool | True when a Retraction removed the row. SHOW hides these; SQL does not. |
inference · one row per human prompt and the agent's response to it
The unit a human recognises: what was asked, and everything the agent did before it stopped. objective is the ask in the human's words when content was captured; prompt_chars is there when it was not.
Joins: session_id → sessions.session_id
| column | type | null | meaning |
|---|---|---|---|
turn_id |
text | The turn (trn_…). |
|
session_id |
text | The session this row belongs to (ses_…). |
|
provider |
text | Which coding agent produced the underlying events. Common values: claude_code, codex, cursor, gemini_cli, attemptdb (open vocabulary — others appear). |
|
project_id |
text | Stable id of the repository (prj_…), derived from its root path and remote. |
|
project_name |
text | owner/repo when a git remote is known, otherwise the directory name. This is what a human filters on. |
|
turn_index |
int64 | Position of the turn in its session, from 0. | |
started_at |
timestamp | When the row's first evidence was observed. | |
ended_at |
timestamp | yes | When the row's last evidence was observed. Null while it is still open. |
status |
text | How the turn ended. Values: completed, failed, in_progress, unknown. |
|
prompt_event_id |
text | yes | The prompt that opened the turn. |
stop_event_id |
text | yes | The event that closed it. |
tool_call_ids |
list | The tool calls making up this row, in order. | |
tool_call_count |
int64 | How many tool calls this row contains. Computed with the retraction rules applied — do not re-derive it with COUNT(*). | |
objective |
text | yes | What the work was for, in the human's own words. Content: null under metadata_only. |
prompt_chars |
int64 | yes | Length of the prompt in characters. Metadata: present even under metadata_only, where objective is null. |
first_event_id |
text | First event of the row, in observation order. | |
last_event_id |
text | Last event of the row, in observation order. | |
evidence |
list | The event ids this row was inferred from. The whole point of an inference: follow these to check the claim. | |
confidence |
float32 | 0.0-1.0. How strongly the evidence supports the row, not how important the row is. | |
corrected_by |
text | yes | The Correction event (ev_…) that overrode this row's inference, if any. |
corrected_at |
timestamp | yes | When that correction was written. |
inferred_objective |
text | yes | The objective as the projector read it, kept when a human correction replaced objective. The two together are the audit trail. |
retracted |
bool | True when a Retraction removed the row. SHOW hides these; SQL does not. |
inference · one row per tool invocation
A started and a finished event paired into one call, with its path, its duration and how it ended. A call with finished_at IS NULL is still running — or its completion was never captured, which sessions.coverage tells you.
Joins: session_id → sessions.session_id · turn_id → turns.turn_id
| column | type | null | meaning |
|---|---|---|---|
tool_call_id |
text | The tool call (tc_…). |
|
session_id |
text | The session this row belongs to (ses_…). |
|
provider |
text | Which coding agent produced the underlying events. Common values: claude_code, codex, cursor, gemini_cli, attemptdb (open vocabulary — others appear). |
|
project_id |
text | Stable id of the repository (prj_…), derived from its root path and remote. |
|
project_name |
text | owner/repo when a git remote is known, otherwise the directory name. This is what a human filters on. |
|
turn_id |
text | yes | The turn (trn_…) this row belongs to: one human prompt and everything the agent did in response. |
agent_id |
text | Which agent instance made the call. | |
tool_name |
text | The tool as the provider named it. | |
tool_category |
text | The normalised category — compare providers on this, not on tool_name. Values: shell, file_read, file_write, file_edit, search, web, mcp, subagent, plan, notebook, other. |
|
provider_call_id |
text | yes | The provider's own call id. |
started_at |
timestamp | yes | When the call started. Null when only its completion was observed. |
finished_at |
timestamp | yes | When it returned. Null while it is still running. |
duration_ms |
int64 | yes | Wall-clock duration. Null unless both ends were observed. |
outcome_status |
text | yes | How it ended. Null while it is still running. Values: success, failure, denied, cancelled, unknown. |
outcome_class |
text | yes | A coarser reason for the outcome. Common values: exit_code, denied, timeout, cancelled (open vocabulary — others appear). |
exit_code |
int32 | yes | Process exit status, when the provider reported one. |
path_relative |
text | yes | The primary path, repository-relative. |
paths |
list | Repository-relative paths this row touched, deduplicated. | |
command_category |
text | yes | What a shell command was doing, classified from the command line. Common values: git, test, build, install, network, fs, run, other (open vocabulary — others appear). |
git_subcommand |
text | yes | For a git call, the subcommand (commit, push, …). |
lines_added |
int64 | yes | Lines added, when the provider reported a diff. |
lines_removed |
int64 | yes | Lines removed, same. |
start_event_id |
text | yes | The event that opened the row, when one was observed. |
end_event_id |
text | yes | The event that closed the row, when one was observed. |
evidence |
list | The event ids this row was inferred from. The whole point of an inference: follow these to check the claim. | |
confidence |
float32 | 0.0-1.0. How strongly the evidence supports the row, not how important the row is. | |
retracted |
bool | True when a Retraction removed the row. SHOW hides these; SQL does not. |
inference · one row per contiguous run of tool calls pursuing one objective
The table this database is named for: what the agent tried. Several attempts in one turn mean it tried, failed and tried again — attempt_index, supersedes and superseded_by are the retry chain. outcome = 'superseded' is a retry, not an independent failure; counting it as one double-counts.
Joins: session_id → sessions.session_id · turn_id → turns.turn_id · work_unit_id → work_units.work_unit_id · superseded_by → attempts.attempt_id
| column | type | null | meaning |
|---|---|---|---|
attempt_id |
text | The attempt (att_…). |
|
session_id |
text | The session this row belongs to (ses_…). |
|
provider |
text | Which coding agent produced the underlying events. Common values: claude_code, codex, cursor, gemini_cli, attemptdb (open vocabulary — others appear). |
|
project_id |
text | Stable id of the repository (prj_…), derived from its root path and remote. |
|
project_name |
text | owner/repo when a git remote is known, otherwise the directory name. This is what a human filters on. |
|
turn_id |
text | The turn (trn_…) this row belongs to: one human prompt and everything the agent did in response. |
|
turn_index |
int64 | Position of the enclosing turn in its session. | |
attempt_index |
int64 | Position of this attempt within its turn, from 0. Attempt 1 after a failed attempt 0 is a retry. | |
objective |
text | yes | What the work was for, in the human's own words. Content: null under metadata_only. |
approach |
text | How the attempt went about it, classified from the tool calls it used. Common values: edit, shell, search, read, mixed (open vocabulary — others appear). |
|
started_at |
timestamp | When the row's first evidence was observed. | |
ended_at |
timestamp | yes | When the row's last evidence was observed. Null while it is still open. |
outcome |
text | How the attempt ended. superseded means a later attempt in the same turn replaced it — that is a retry, not an independent failure. Values: succeeded, failed, abandoned, superseded, in_progress, unknown. |
|
failure_class |
text | yes | What kind of failure, when it failed. Open vocabulary: two failures of the same class are the signal that something is stuck. Common values: test_failure, compile_error, permission_denied, timeout, not_found, conflict, other (open vocabulary — others appear). |
tool_call_ids |
list | The tool calls making up this row, in order. | |
tool_call_count |
int64 | How many tool calls this row contains. Computed with the retraction rules applied — do not re-derive it with COUNT(*). | |
paths |
list | Repository-relative paths this row touched, deduplicated. | |
commit_shas |
list | Commit shas produced under this row. | |
superseded_by |
text | yes | The attempt that replaced this one. |
supersedes |
text | yes | The attempt this one replaced. |
evidence |
list | The event ids this row was inferred from. The whole point of an inference: follow these to check the claim. | |
confidence |
float32 | 0.0-1.0. How strongly the evidence supports the row, not how important the row is. | |
algorithm_version |
text | The projector version that produced the row (tier1-v3). Rows from different versions are not comparable. |
|
work_unit_id |
text | yes | The work unit (wu_…) this row was folded into, if any. |
corrected_by |
text | yes | The Correction event (ev_…) that overrode this row's inference, if any. |
corrected_at |
timestamp | yes | When that correction was written. |
correction_type |
text | yes | What the correction changed. Values: attempt_outcome, attempt_note, turn_objective. |
inferred_outcome |
text | yes | The outcome the projector derived, kept when a human correction replaced outcome. Values: succeeded, failed, abandoned, superseded, in_progress, unknown. |
inferred_failure_class |
text | yes | The failure class the projector derived, kept for the same reason. Common values: test_failure, compile_error, permission_denied, timeout, not_found, conflict, other (open vocabulary — others appear). |
note |
text | yes | A human's note from a Correction. Content: null under metadata_only. |
retracted |
bool | True when a Retraction removed the row. SHOW hides these; SQL does not. |
inference · one row per session picking up where another stopped
Two sessions, usually two different agents, touching the same files across a gap. gap_ms and shared_paths are the whole basis of the inference: a long gap with one shared file is weak evidence and the confidence says so.
Joins: from_session → sessions.session_id · to_session → sessions.session_id
| column | type | null | meaning |
|---|---|---|---|
from_session |
text | The session that stopped (ses_…). |
|
to_session |
text | The session that picked the work up. | |
from_provider |
text | Agent that stopped. Common values: claude_code, codex, cursor, gemini_cli, attemptdb (open vocabulary — others appear). |
|
to_provider |
text | Agent that continued. Common values: claude_code, codex, cursor, gemini_cli, attemptdb (open vocabulary — others appear). |
|
project_id |
text | Stable id of the repository (prj_…), derived from its root path and remote. |
|
handoff_at |
timestamp | When the second session started. | |
gap_ms |
int64 | Milliseconds between the last event of the first session and the first of the second. A large gap weakens the inference. | |
shared_paths |
list | Paths both sessions touched. This overlap is why the handoff was inferred at all. | |
evidence |
list | The event ids this row was inferred from. The whole point of an inference: follow these to check the claim. | |
confidence |
float32 | 0.0-1.0. How strongly the evidence supports the row, not how important the row is. |
inference · one row per causal or structural link between two entities
The graph WHY and TRACE walk. Endpoints are polymorphic: from_type/to_type name the table and from_id/to_id its prefixed id, so join by writing the type into the condition. edge_source separates edges the projector asserted from edges the causal layer derived on top of them.
| column | type | null | meaning |
|---|---|---|---|
ordinal |
int64 | Position in the edge list. A stable handle, not a meaning. | |
edge_kind |
text | What the edge asserts. Values: parent_of, caused, triggered, blocked, resolved, superseded, produced, verified, contradicted, handed_off, evidence_for. |
|
from_type |
text | Kind of entity the edge starts at. Values: event, tool_call, turn, attempt, session, work_unit. |
|
from_id |
text | Prefixed id of that entity. | |
to_type |
text | Kind of entity the edge ends at. Values: event, tool_call, turn, attempt, session, work_unit. |
|
to_id |
text | Prefixed id of that entity. | |
evidence |
list | The event ids this row was inferred from. The whole point of an inference: follow these to check the claim. | |
confidence |
float32 | 0.0-1.0. How strongly the evidence supports the row, not how important the row is. | |
edge_source |
text | projection for edges the projector wrote; derived for edges the causal graph added on top of them. Values: projection, derived. |
inference · one row per moment the agent needed a human
Permission requests, denials and notifications, each with the event that cleared it. A row with pending = true in an open session is an agent waiting right now — this is the fact behind Needs You.
Joins: session_id → sessions.session_id · event_id → events.event_id
| column | type | null | meaning |
|---|---|---|---|
session_id |
text | The session this row belongs to (ses_…). |
|
event_id |
text | The event that raised the signal. | |
raised_at |
timestamp | When it was raised. | |
kind |
text | What kind of signal. A permission request is the agent waiting on a human. Values: permission_requested, permission_denied, notification. |
|
signal_type |
text | yes | The provider's own label for a notification. Free text: there is no fixed set to match against. |
cleared_at |
timestamp | yes | When the next event in the session arrived, which is what ends the wait. Null while it is still pending. |
cleared_by |
text | yes | The event that cleared it. |
pending |
bool | True while nothing has cleared it. A pending signal in an open session is a human being waited on. | |
evidence |
list | The event ids this row was inferred from. The whole point of an inference: follow these to check the claim. | |
confidence |
float32 | 0.0-1.0. How strongly the evidence supports the row, not how important the row is. |
inference · one row per thread of work, across sessions and agents
What a human would call a task: an objective, the sessions and attempts spent on it, where it stands. It survives session boundaries, agent switches and days, which is what makes it the right grain for "what is going on in this repository". phase and status are inferences with reasons attached — quote the reason, not just the label.
| column | type | null | meaning |
|---|---|---|---|
work_unit_id |
text | The work unit (wu_…): one thread of work, which may span sessions, agents and days. |
|
version |
int64 | How many times the unit has been revised. It grows as evidence arrives. | |
project_id |
text | Stable id of the repository (prj_…), derived from its root path and remote. |
|
project_name |
text | owner/repo when a git remote is known, otherwise the directory name. This is what a human filters on. |
|
objective_event_id |
text | yes | The prompt the objective was read from. |
objective |
text | yes | What the unit is for. Content: null under metadata_only. |
phase |
text | Where the work stands. Inferred from the recent tool mix and outcomes, so read phase_reason with it. Values: explore, plan, implement, debug, verify, review, deliver, blocked. |
|
phase_reason |
text | Why that phase was chosen, in one sentence. | |
status |
text | Whether the unit is still open. Values: open, completed, abandoned, unknown. |
|
status_reason |
text | Why that status was chosen. | |
started_at |
timestamp | When the row's first evidence was observed. | |
updated_at |
timestamp | When the newest evidence for this row was observed. | |
ended_at |
timestamp | yes | When the row's last evidence was observed. Null while it is still open. |
sessions |
list | Every session that contributed. | |
session_count |
int64 | How many. | |
turns |
list | Every turn that contributed. | |
turn_count |
int64 | How many. | |
attempts |
list | Every attempt in the unit. | |
attempt_count |
int64 | How many. | |
failed_attempt_count |
int64 | How many of them failed. Two failures of the same class with no success after is the repeated-failure signal. | |
paths |
list | Repository-relative paths this row touched, deduplicated. | |
commit_shas |
list | Commit shas produced under this row. | |
actors |
list | The agents that worked on it — more than one means the work was handed off. | |
last_attempt |
text | yes | The most recent attempt (att_…). |
blocking_signal |
text | yes | The event id of the signal holding the unit up, when one is pending. |
evidence |
list | The event ids this row was inferred from. The whole point of an inference: follow these to check the claim. | |
confidence |
float32 | 0.0-1.0. How strongly the evidence supports the row, not how important the row is. | |
algorithm_version |
text | The projector version that produced the row (tier1-v3). Rows from different versions are not comparable. |
inference · one row per point where the direction changed
An agent abandoning one approach for another, or a human stepping in. rationale is derived from what happened around the change, never typed by anyone, and rationale_source says so.
Joins: session_id → sessions.session_id · turn_id → turns.turn_id · work_unit_id → work_units.work_unit_id
| column | type | null | meaning |
|---|---|---|---|
decision_id |
text | The decision (dec_…). |
|
kind |
text | What kind of decision. human_intervention is a human changing the direction; approach_change is the agent abandoning one approach for another. Values: approach_change, human_intervention. |
|
work_unit_id |
text | yes | The work unit (wu_…) this row was folded into, if any. |
session_id |
text | The session this row belongs to (ses_…). |
|
provider |
text | Which coding agent produced the underlying events. Common values: claude_code, codex, cursor, gemini_cli, attemptdb (open vocabulary — others appear). |
|
project_id |
text | Stable id of the repository (prj_…), derived from its root path and remote. |
|
project_name |
text | owner/repo when a git remote is known, otherwise the directory name. This is what a human filters on. |
|
turn_id |
text | The turn (trn_…) this row belongs to: one human prompt and everything the agent did in response. |
|
selected |
text | What was chosen, as a prefixed id or a short label. | |
alternatives |
list | What was not chosen, and had evidence behind it. | |
rationale |
text | Why, in one sentence, derived from what happened around it. | |
rationale_source |
text | How the rationale was produced. Always derived: nobody typed it. Values: derived. |
|
decided_at |
timestamp | When the decision was observed. | |
evidence |
list | The event ids this row was inferred from. The whole point of an inference: follow these to check the claim. | |
confidence |
float32 | 0.0-1.0. How strongly the evidence supports the row, not how important the row is. | |
algorithm_version |
text | The projector version that produced the row (tier1-v3). Rows from different versions are not comparable. |
inference · one row per observed git commit call
Where the work landed. One row per commit call, resolved to a sha or not: linkage says how confident the tie is, and sha IS NULL means the call was seen but the sha never was.
Joins: session_id → sessions.session_id · turn_id → turns.turn_id · attempt_id → attempts.attempt_id · tool_call_id → tool_calls.tool_call_id
| column | type | null | meaning |
|---|---|---|---|
commit_id |
text | The commit row (cmt_…). Not the sha: one row per observed git commit call, resolved or not. |
|
session_id |
text | The session this row belongs to (ses_…). |
|
provider |
text | Which coding agent produced the underlying events. Common values: claude_code, codex, cursor, gemini_cli, attemptdb (open vocabulary — others appear). |
|
project_id |
text | Stable id of the repository (prj_…), derived from its root path and remote. |
|
project_name |
text | owner/repo when a git remote is known, otherwise the directory name. This is what a human filters on. |
|
turn_id |
text | yes | The turn (trn_…) this row belongs to: one human prompt and everything the agent did in response. |
attempt_id |
text | yes | The attempt (att_…) this row belongs to. |
tool_call_id |
text | The tool call (tc_…) this row belongs to. |
|
sha |
text | yes | The commit sha. Null when the commit could not be resolved to one. |
previous_sha |
text | yes | What HEAD pointed at before the commit. |
branch |
text | yes | Git branch at the time, when the provider reported one. |
committed_at |
timestamp | When the commit call finished. | |
linkage |
text | How the sha was tied to the call. end_event means the call itself reported it; next_head means the sha was read from the next observed HEAD change, which is weaker; unresolved means no sha was found and sha is null. Values: end_event, next_head, unresolved. |
|
evidence |
list | The event ids this row was inferred from. The whole point of an inference: follow these to check the claim. | |
confidence |
float32 | 0.0-1.0. How strongly the evidence supports the row, not how important the row is. | |
algorithm_version |
text | The projector version that produced the row (tier1-v3). Rows from different versions are not comparable. |
inference · one row per human correction of an inference
A human saying the projector got it wrong. The correction is itself an immutable event; it never edits the row it corrects, which keeps both readings — see attempts.outcome against attempts.inferred_outcome. status says whether it found its target.
Joins: event_id → events.event_id · session_id → sessions.session_id
| column | type | null | meaning |
|---|---|---|---|
event_id |
text | The Correction event (ev_…). A correction is itself an immutable fact, never an edit of the row it corrects. |
|
corrected_at |
timestamp | When the human wrote it. | |
session_id |
text | The session the correction was written into. | |
project_id |
text | Stable id of the repository (prj_…), derived from its root path and remote. |
|
correction_type |
text | yes | What the correction changed. Values: attempt_outcome, attempt_note, turn_objective. |
target_type |
text | yes | Which kind of entity target names. Values: attempt, turn, session. |
target |
text | The projected entity the row points at, as a prefixed id. | |
outcome |
text | yes | The outcome the human asserted, for an attempt_outcome correction. Values: succeeded, failed, abandoned, superseded, in_progress, unknown. |
failure_class |
text | yes | The failure class the human asserted. Common values: test_failure, compile_error, permission_denied, timeout, not_found, conflict, other (open vocabulary — others appear). |
note |
text | yes | Free text a human wrote. Content: null under metadata_only. |
note_chars |
int64 | yes | Length of note in characters. Metadata, so it survives metadata_only even when note does not. |
status |
text | Whether the correction found its target and took effect. Values: applied, target_not_found, target_retracted, invalid. |
|
evidence |
list | The event ids this row was inferred from. The whole point of an inference: follow these to check the claim. | |
confidence |
float32 | 0.0-1.0. How strongly the evidence supports the row, not how important the row is. |
inference · one row per human retraction
A session, attempt or event removed from every projection — benchmarks, tests, mistaken imports, privacy. The facts stay in the log; the projections behave as if they never happened. retracted_events counts what left.
Joins: event_id → events.event_id
| column | type | null | meaning |
|---|---|---|---|
event_id |
text | The Retraction event (ev_…). |
|
retracted_at |
timestamp | When it was written. | |
project_id |
text | Stable id of the repository (prj_…), derived from its root path and remote. |
|
target_type |
text | yes | Which kind of entity target names. Values: session, event, attempt. |
target |
text | The projected entity the row points at, as a prefixed id. | |
reason |
text | Why the data was retracted. Values: benchmark, test, duplicate, mistaken_import, privacy, revoked, other. |
|
note |
text | yes | Free text a human wrote. Content: null under metadata_only. |
note_chars |
int64 | yes | Length of note in characters. Metadata, so it survives metadata_only even when note does not. |
matched |
bool | Whether the target was found. | |
retracted_events |
int64 | How many events left the projections as a result. The facts stay in the log. | |
evidence |
list | The event ids this row was inferred from. The whole point of an inference: follow these to check the claim. | |
confidence |
float32 | 0.0-1.0. How strongly the evidence supports the row, not how important the row is. |
inference · one row per pair of work units touching the same files
Two threads of work over the same paths. overlapping = true means both are still open: two agents editing the same files right now, which is worth interrupting someone over. The committed flags and line counts say how far each has gone.
Joins: first_work_unit → work_units.work_unit_id · second_work_unit → work_units.work_unit_id
| column | type | null | meaning |
|---|---|---|---|
conflict_id |
text | The conflict row. | |
project_id |
text | Stable id of the repository (prj_…), derived from its root path and remote. |
|
first_work_unit |
text | The work unit that started first (wu_…). |
|
second_work_unit |
text | The one that started later. | |
first_started_at |
timestamp | When the first unit started. | |
second_started_at |
timestamp | When the second started. | |
started_at |
timestamp | When the overlap began. | |
updated_at |
timestamp | When the newest evidence for the overlap arrived. | |
paths |
list | The files both units touched. This overlap is the conflict. | |
path_count |
int64 | How many. | |
overlapping |
bool | True while both units are still open: two agents editing the same files right now. | |
first_committed |
bool | Whether the first unit has committed the shared paths. | |
second_committed |
bool | Whether the second has. | |
first_lines_added |
int64 | Lines the first unit added to the shared paths. | |
first_lines_removed |
int64 | Lines it removed. | |
second_lines_added |
int64 | Lines the second unit added. | |
second_lines_removed |
int64 | Lines it removed. | |
evidence |
list | The event ids this row was inferred from. The whole point of an inference: follow these to check the claim. | |
confidence |
float32 | 0.0-1.0. How strongly the evidence supports the row, not how important the row is. | |
algorithm_version |
text | The projector version that produced the row (tier1-v3). Rows from different versions are not comparable. |
Placeholders ({session}, {attempt}) stand for a real id; substitute one before running.
Is coding-agent OTel telemetry arriving?
SELECT provider, COUNT(*) AS observations, MAX(observed_at) AS latest FROM events WHERE retracted = false AND kind = 'unknown' AND attrs_json LIKE '%"source":"otel"%' GROUP BY provider
Counts observed facts, not inferred work. Inspect x_otel_signal for logs, metrics and traces; receiver readiness alone does not prove an agent export.
What is going on in this repository right now?
WHAT IS project DOING NOW
Open work units with their phase and latest attempt. Answers with insufficient_evidence rather than guessing when nothing is active.
What did the agents try?
SHOW ATTEMPTS ORDER BY started_at DESC LIMIT 20
The default view of the database. Add INCLUDING RETRACTED to see what a retraction removed.
What failed?
SHOW FAILED ATTEMPTS
outcome = 'failed' only. Superseded attempts are retries and are excluded here on purpose.
What failed in one file?
SHOW FAILED ATTEMPTS FOR path = 'crates/*/src/*.rs'
path matches against the attempt's paths list; * is a glob.
Which attempts were retried?
SHOW SUPERSEDED ATTEMPTS
Each row is an attempt a later one replaced, with the successor's outcome — the retry chain, not a list of failures.
Where did work pass between agents?
SHOW HANDOFFS
Read gap_ms and shared_paths before believing a handoff: they are the evidence.
Why is this session stuck?
WHY session {session} STATUS BLOCKED
Answers from pending signals and repeated same-class failures, and says state_mismatch when the session is not blocked at all.
What caused this attempt?
TRACE attempt {attempt} CAUSES DEPTH 3
Walks the causal edges upward. DIRECTION DOWN walks to consequences instead.
What is this claim based on?
SHOW EVIDENCE FOR attempt {attempt}
The events the inference was built from, in observation order. Every derived row can be opened this way.
What did the repository look like yesterday?
STATE project AT '-1d'
Sessions and work units as they stood at that moment, with outcomes known only up to then.
What changed since yesterday?
DIFF STATE '-1d' NOW
One row per changed field. Units that completed in between show as removed with their final state.
Which failures repeat?
SELECT failure_class, count(*) AS failures FROM attempts WHERE outcome = 'failed' AND retracted = false GROUP BY failure_class ORDER BY failures DESC
failure_class is an open vocabulary: two attempts sharing one is the signal that something is genuinely stuck.
Which work is stuck?
SELECT work_unit_id, phase, phase_reason, failed_attempt_count FROM work_units WHERE status = 'open' AND failed_attempt_count >= 2 ORDER BY updated_at DESC
Quote phase_reason with the phase: the label alone is an inference presented as fact.
Is anyone waiting on me?
SELECT s.session_id, s.provider, g.kind, g.raised_at FROM signals g JOIN sessions s ON s.session_id = g.session_id WHERE g.pending = true AND s.state = 'open' ORDER BY g.raised_at
A pending signal in an open session is an agent waiting on a human right now.
How do the agents differ in what they run?
SELECT provider, tool_category, count(*) AS calls FROM tool_calls WHERE retracted = false GROUP BY provider, tool_category ORDER BY calls DESC
Compare on tool_category, never on tool_name: every provider names its tools differently.
What is slow?
SELECT tool_name, path_relative, duration_ms FROM tool_calls WHERE duration_ms IS NOT NULL ORDER BY duration_ms DESC LIMIT 10
Null duration_ms means one end of the call was never observed, not that it was instant.
How much of this history is actually captured?
SELECT provider, sum(CASE WHEN hook_version IS NULL THEN 1 ELSE 0 END) AS reconstructed, count(*) AS events FROM events WHERE retracted = false GROUP BY provider
Rows with no hook_version were reconstructed from a transcript after the fact. Their absence of a detail is not evidence of absence.
What did the agents commit?
SELECT committed_at, sha, branch, linkage FROM commits WHERE sha IS NOT NULL ORDER BY committed_at DESC LIMIT 20
linkage = 'next_head' is a weaker tie than end_event; sha IS NULL means the commit call was seen but its sha never was.
Are two agents editing the same files?
SELECT conflict_id, path_count, overlapping, first_committed, second_committed FROM conflicts WHERE overlapping = true
overlapping = true means both work units are still open — the case worth interrupting someone over.
What have humans corrected?
SELECT corrected_at, correction_type, target, status FROM corrections ORDER BY corrected_at DESC
The audit trail of where the projector was wrong. status says whether the correction found its target.
What kinds of events are in here at all?
SELECT kind, count(*) AS events FROM events WHERE retracted = false GROUP BY kind ORDER BY events DESC
The first query to run against an unfamiliar database: it shows what the hooks actually captured.
What columns does this table have?
DESCRIBE attempts
Types straight from the schema. attempt schema adds what they mean.
How will this query run?
EXPLAIN SELECT count(*) FROM events WHERE kind = 'tool_call_failed'
The DataFusion plan, including which filters were pushed into the segment scan.