Skip to content

Latest commit

 

History

History
733 lines (574 loc) · 59.9 KB

query-context.md

File metadata and controls

733 lines (574 loc) · 59.9 KB

Query context

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.

Rules

  1. 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.

  2. 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.

  3. events is 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 carries evidence (the event ids it was built from), confidence (0.0-1.0) and usually algorithm_version. Never present an inferred row as something the agent did; say what it was inferred from.

  4. 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_raw is the same stream with the storage types instead (16-byte UUIDs, dictionary-encoded strings); read events unless you need the raw layout.

  5. Times are timestamp(microsecond, UTC). observed_at is when the agent did it, captured_at when the hook recorded it, ingested_at when the database accepted it. Order history by observed_at; measure capture lag with the other two.

  6. Retracted rows are hidden by AttemptQL and visible to SQL. SHOW drops them unless the statement says INCLUDING RETRACTED; a bare SELECT does not, so filter retracted = false yourself on events, sessions, turns, tool_calls and attempts.

  7. 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. Check events.capture_mode before concluding an agent had no objective.

  8. Counts belong to the projection, not to SQL aggregates you re-derive. sessions.turn_count, attempts.tool_call_count and the rest are computed with the retraction rules applied; recomputing them with COUNT(*) over the child table gives a different (and usually wrong) number.

  9. 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 --project flag, never a different connection.

Tables

events

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.

events_raw

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.

sessions

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.

turns

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.

tool_calls

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.

attempts

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.

handoffs

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.

edges

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.

signals

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.

work_units

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.

decisions

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.

commits

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.

corrections

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.

retractions

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.

conflicts

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.

Example questions

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.