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. state says whether it is open, stale (silent for 30 minutes with no end observed) or closed: the first two are guesses made from the clock, 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. A provider that sent none (unknown) gets one session per project, whose session_id is derived and carried by no event. |
|
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 | closed: an end event was observed and nothing resumed the session after it. open: no end, and activity within the last 30 minutes of when the database was read (12 hours while it is waiting on a human). stale: no end and silent for longer: agents are killed far more often than they exit, so this is the usual fate of a session that just stopped, and it is an inference from silence, not a fact. The one column that depends on when you read: the same events are open at 10:05 and stale at 11:00. Values: open, stale, closed. |
|
started_at |
timestamp | When the row's first evidence was observed. | |
ended_at |
timestamp | yes | When the end was observed. Null for a session that never ended or was resumed after its end event: a session used again after SessionEnded is not closed. |
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. state is derived from this and the clock; read it directly when you need the raw fact. |
|
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 | How completely the session was observed, by coverage: full 1.0, partial 0.8, minimal 0.6, unknown 0.4; a stale session is capped at 0.7. An open session is at most 0.8 because its end has not been observed and may have happened unobserved. |
|
algorithm_version |
text | The projector version that produced the row (tier1-v<n>). Every inferred table carries it; rows from different versions are not comparable. |
|
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. | |
algorithm_version |
text | The projector version that produced the row (tier1-v<n>). Every inferred table carries it; rows from different versions are not comparable. |
|
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. |
tests_failed |
int64 | yes | Failing tests the runner reported (attrs.tests_failed), on a shell call that ran a test suite. Null when no summary was read. A run that exits 0 but reports failures is still a failed verification. |
pairing |
text | How the start and end events were matched into this call, which is where confidence comes from: call_id 1.0 (same provider call id), fifo 0.9 (no shared id; matched first-in first-out on agent and tool name, a guess), end_only 1.0 (a single event is a complete call for post-only hooks), in_flight 0.7 (a start with no end: still running, or its completion was never captured). Values: call_id, fifo, end_only, in_flight. |
|
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. | |
algorithm_version |
text | The projector version that produced the row (tier1-v<n>). Every inferred table carries it; rows from different versions are not comparable. |
|
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_…). Derived from the attempt's own evidence, never from its position: the end of the call that failed it, else its first tool-call event, else its turn's opening event. It survives events that arrive late and earlier, events appended to it, and a scoped view of the log, so a correction or retraction written against it keeps pointing at the same work. Ids from before tier1-v5 were positional; corrections and retractions that still use one are resolved by position and flagged legacy_position. |
|
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 by start time, from 0. A display order only: it is not part of the id and can move when events arrive late. | |
agent_id |
text | The agent whose tool calls make up the attempt. A main agent and the subagents it runs in parallel get separate attempts in one turn, so one agent's failure never ends another's. | |
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. failed means a file edit failed, or a shell command failed after the attempt had edited something or while running a test or build (a shell command that fails during plain exploration, like a grep with no match, is a failed call, not a failed attempt). succeeded means the turn stopped normally and nothing ended the attempt; see verification. superseded means a later attempt of the same agent replaced it — it edited the same paths again, or passed the test or build this one failed: 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. tests_failed is a test run that exited 0 but reported failing tests. Common values: test_failure, compile_error, permission_denied, timeout, not_found, conflict, other (open vocabulary — others appear). |
verification |
text | yes | Whether the attempt ran a test or build and how the last such run went: passed, failed (a non-zero exit, or a runner that exited 0 but reported failing tests), or null when it ran none. outcome = 'succeeded' only means the turn stopped: with a null verification nothing checked the work. Values: passed, failed. |
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-v<n>). Every inferred table carries it; 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. |
|
from_turn |
text | yes | The giving session's turn that went quiet just before the handoff. |
to_turn |
text | yes | The receiving session's turn that began it. A session that resumes after another agent worked (Claude, then a Codex review, then Claude again) is a receiving session again, so one session pair can appear in several rows. |
handoff_at |
timestamp | When the receiving turn began (for a session's first turn: when the session started). | |
gap_ms |
int64 | Milliseconds between the last activity of the giving session and the start of the receiving one. A large gap weakens the inference. | |
shared_paths |
list | Paths the giving session edited that the receiving session touched. A path both sides only read is not shared evidence. This overlap is why the handoff was inferred at all; empty means only timing links the sessions. | |
evidence |
list | The event ids this row was inferred from. The whole point of an inference: follow these to check the claim. | |
confidence |
float32 | RFC 0003 §5.5: 0.6 with a shared edited path within 30 minutes, +0.2 when the giving session had ended, +0.1 for three or more shared paths, capped at 0.9; 0.5 with no shared path when the receiving session simply starts within 5 minutes. | |
algorithm_version |
text | The projector version that produced the row (tier1-v<n>). Every inferred table carries it; rows from different versions are not comparable. |
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 | How strongly the relation is supported. Structural edges (parent_of, evidence_for, turn-level triggered) restate a grouping and are 1.0. superseded and handed_off carry the attempt's or handoff's confidence. A caused edge from a failed call to the retry that followed it is adjacency plus a shared edit, capped at 0.6. blocked carries the signal's confidence. No inferred edge claims certainty. |
|
edge_source |
text | projection for edges the projector wrote; derived for edges the causal graph added on top of them. Values: projection, derived. |
|
algorithm_version |
text | The projector version that produced the row (tier1-v<n>). Every inferred table carries it; rows from different versions are not comparable. |
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. | |
agent_id |
text | The agent that raised the signal. Only that agent's own progress, a human prompt, or the end of the session clears it: a background subagent working on does not mean the main agent's approval arrived. | |
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 blocking signal in an open session is a human being waited on; read blocking too. |
|
blocking |
bool | Whether a human must answer for work to continue. False for an idle_prompt raised after the turn ended (or before any turn): the agent finished and is merely idle. Such a signal is kept because it was observed, but it never makes a session blocked, never enters Needs You and never blocks a work unit. |
|
evidence |
list | The event ids this row was inferred from. The whole point of an inference: follow these to check the claim. | |
confidence |
float32 | A pending blocking signal claims a person is being waited on now: 0.85 with full coverage of its session, 0.65 otherwise (a reply given outside the hook surface would not be captured). A cleared or non-blocking one is 0.9. | |
algorithm_version |
text | The projector version that produced the row (tier1-v<n>). Every inferred table carries it; rows from different versions are not comparable. |
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-v<n>). Every inferred table carries it; 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-v<n>). Every inferred table carries it; 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-v<n>). Every inferred table carries it; 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. content_unavailable: it is well-formed but its text was not stored (metadata_only), so a turn objective or note had nothing to apply and the target is untouched. Values: applied, target_not_found, target_retracted, invalid, content_unavailable. |
|
legacy_position |
bool | True when target was a positional attempt id from before tier1-v5 and the correction was applied by position under today's rules: best effort, since the position may have moved since the id was shown. |
|
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. | |
legacy_position |
bool | True when target was a positional attempt id from before tier1-v5, resolved by position under today's rules. |
|
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-v<n>). Every inferred table carries it; 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 g.blocking = true AND s.state = 'open' ORDER BY g.raised_at
A pending blocking signal in an open session is an agent waiting on a human right now. blocking = false drops an idle agent that finished its turn; state = 'open' drops sessions that ended or went stale.
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.