Source trust contracts (SNUG-122)

Agent-facing semantics for every source family that retrieval and MCP tools may surface. Read this before citing Hippo evidence — it tells you where data lives, which rows are synthetic, what timestamps mean, what “missing” means, and which fields are safe to quote.

Companion docs (do not duplicate here):

TopicDoc
Eligibility filters (what retrieval excludes by default)retrieval-eligibility.md
Capture entry points and operator runbookssources.md, operator-runbook.md
SQLite table reference../schema.md
Redaction limits and config surface../../config/README.md; deeper redaction reference tracked in #114 ↗
MCP tool shapes../mcp-reference.md

Machine-readable index: brain/src/hippo_brain/_fixtures/source_trust_contracts.json.

How to read a contract

Each family below uses the same headings:

FieldMeaning
TablesSQLite tables holding raw or enriched data
IdentityStable row keys agents can cite
TimestampsWhich column is “when this happened” (all epoch milliseconds unless noted)
Synthetic / probeRows that are canaries, not user activity
Expected absentNormal gaps — not evidence of failure
In-flightRows that exist but are not yet eligible for retrieval
FreshnessHow to tell whether capture is current (source_health key)
Safe to citeFields appropriate for evidence packets
Do not citeOperational noise or PII-adjacent columns

Retrieval applies eligibility from retrieval_eligibility.py by default. Operator debug: include_excluded=true or HIPPO_RETRIEVAL_INCLUDE_EXCLUDED=1.


Shell commands

Tables: events (source_kind='shell'), sessions, knowledge_node_eventsknowledge_nodes.

Identityevents.id (integer); in retrieval: linked_source_ids entry shell-<id>
Timestampsevents.timestamp — command start (preexec). Duration in duration_ms; exit in exit_code.
Synthetic / probeevents.probe_tag IS NOT NULL — synthetic hippo probe commands. Excluded from MCP/RAG by default.
Expected absentNo rows when terminal idle, hook not sourced, or command redacted to empty.
In-flightN/A — shell events are point-in-time.
Freshnesssource_health.source = 'shell'last_event_ts, probe_ok, probe_lag_ms. Invariant I-1.
Safe to citecommand (redacted), cwd, git_repo, git_branch, exit_code, duration_ms, timestamp
Do not citeRaw stdout/stderr blobs unless operator mode; probe_tag; env_snapshot_id internals

Safe citation example

Shell event shell-18442: cargo test -p hippo-core in /Users/me/projects/hippo on main exited 0 after 12.4s (2026-07-05T10:15:00Z).


Claude tool events

Tables: events (source_kind='claude-tool', tool_name set), linked via knowledge_node_events.

Identityevents.id; linked_source_ids: shell-<id> (tool events share the events table)
Timestampsevents.timestamp — when the tool call was ingested from the parent session JSONL
Synthetic / probeInherits session probe tag when parent segment is synthetic; filtered via shell eligibility
Expected absentNo tool rows when the session had no tool calls in the segment window
In-flightAppears while parent agentic_sessions segment is inside the 90s settle window
FreshnessIndirect — source_health.source = 'claude-tool'; primary signal is parent agentic-session-claude
Safe to citetool_name, command (tool input summary), cwd, git_branch, parent session session_id
Do not citeFull tool I/O if truncated; probe-tagged parent sessions

Note: MCP search_events with source="claude" queries agentic_sessions, not claude-tool rows. Tool calls appear under source="shell" or source="all".

Safe citation example

Claude tool event shell-9912: Bash ran rg 'retrieval_eligibility' brain/ in hippo repo (linked to session sess-abc).


Agentic sessions (Claude Code, Codex, Cursor, opencode)

Tables: agentic_sessions, knowledge_node_agentic_sessionsknowledge_nodes. Legacy claude_sessions is frozen (v18+); retrieval reads agentic_sessions only.

Harnesssource_health keyOrigin path
claude-codeagentic-session-claude~/.claude/projects/**/*.jsonl (FS watcher)
codexagentic-session-codex~/.codex/sessions/**/rollout-*.jsonl
cursoragentic-session-cursor~/.cursor/projects/**/agent-transcripts/**/*.jsonl
opencodeagentic-session-opencodeopencode SQLite → poller
Identity(session_id, harness, segment_index) UNIQUE; row id for joins; retrieval: claude-<id>, codex-<id>, cursor-<id>, opencode-<id>
Timestampsstart_time / end_time — segment bounds. Cursor segments use file mtime when JSONL has no per-line times.
Synthetic / probeprobe_tag IS NOT NULL on segment row
Expected absentNo segment until file idle past min_idle_secs; opencode sessions with <3 messages and no diffs (enrichment skip)
In-flightend_time within 90s of now (IN_FLIGHT_SETTLE_MS); workflow journal.jsonl under subagents/.../workflows/; empty stubs (message_count=0 and blank summary_text)
FreshnessPer-harness source_health row above; invariants I-2, I-11, I-13, I-15
Safe to citesession_id, harness, segment_index, summary_text, cwd/project_dir, git_branch, start_time/end_time, source_file (path only, not full transcript)
Do not citeFull tool_calls_json / user_prompts_json without redaction review; subagent workflow journals; probe segments

Safe citation example

Codex segment codex-5021 (session_id=rollout-7f3a, segment 2): summarized dependency bump work in /Users/me/projects/hippo, ended 2026-07-04T18:22:00Z.


Browser visits

Tables: browser_events, knowledge_node_browser_eventsknowledge_nodes.

Identitybrowser_events.id; retrieval: browser-<id>
Timestampstimestamp — page departure; dwell_ms, scroll_pct describe engagement
Synthetic / probeprobe_tag IS NOT NULL — probe canary page views
Expected absentDomains outside [browser.allowlist]; Firefox down; extension not loaded
In-flightN/A
Freshnesssource_health.source = 'browser'; invariant I-4
Safe to citedomain, title, url (query params may be redacted per config), dwell_ms, timestamp
Do not citeFull content article text in agent summaries unless user asked for page content; probe_tag

Safe citation example

Browser event browser-3301: read “SQLite WAL mode” on sqlite.org for 4m 12s (2026-07-03T14:00:00Z).


GitHub CI (workflow runs)

Tables: workflow_runs, workflow_jobs, workflow_annotations, workflow_log_excerpts, knowledge_node_workflow_runsknowledge_nodes.

Identityworkflow_runs.id; GitHub run_id; retrieval: workflow-<id>
Timestampsstarted_at / completed_at on run; job-level times on workflow_jobs
Synthetic / probeNo probe rows — opt-in source ([github] enabled = true)
Expected absentNo rows when GitHub polling disabled, no token, or repo has no recent Actions activity
In-flightstatus = 'in_progress' and conclusion IS NULLexcluded from retrieval until completed
Freshnesssource_health.source = 'workflow'; doctor soft/hard thresholds on MAX(started_at) (3d / 30d)
Safe to citerepo, head_sha, head_branch, name (workflow), conclusion, html_url, annotation message excerpts
Do not citeFull log blobs; secrets from log excerpts (should be redacted at ingest)

Safe citation example

Workflow run workflow-88 on stevencarpenter/hippo @ abc1234 (main): CI workflow success (2026-07-05T09:00:00Z).


Claude auto-memory

Tables: memory_documents, memory_revisions, memory_chunks, knowledge_node_memory_chunksknowledge_nodes.

Identitymemory_documents.id; retrieval: memory-<chunk_id>
Timestampsmemory_revisions.created_at for revision; document updated_at
Synthetic / probeNone
Expected absentWhen [auto_memory] disabled or no ~/.claude/... memory files ingested
In-flightQueue rows in memory_enrichment_queue with status != 'done'
Freshnesssource_health.source = 'claude-auto-memory'
Safe to citerepository, source_path, chunk heading, content excerpt, active revision only (state = 'active')
Do not citeSuperseded revisions (active_revision_id mismatch)

Safe citation example

Claude auto-memory chunk from ~/.claude/projects/.../memory/MEMORY.md (repo: hippo): heading “Capture reliability”.


source_health (capture freshness metadata)

Tables: source_health only — not linked to knowledge_nodes. Query via hippo doctor, SQL, or Grafana (hippo_daemon_source_health_* metrics).

Identitysource (TEXT PK) — e.g. shell, agentic-session-cursor, watchdog
Timestampslast_event_ts, updated_at, probe_last_run_ts (all epoch ms)
Synthetic / probeprobe_ok, probe_lag_ms, probe_last_run_ts describe canary health, not user events
Expected absentRow missing only on pre-v8 schema; new sources seed via migration
In-flightN/A
FreshnessSelf-describing — last_event_ts staleness vs invariant thresholds in architecture.md
Safe to citesource, last_event_ts, consecutive_failures, probe_ok, probe_lag_ms when explaining coverage gaps
Do not citeAs user-activity evidence — health rows are operational telemetry

Safe citation example

source_health for agentic-session-cursor: last_event_ts 45m ago, consecutive_failures=0, probe_ok=1 — capture healthy.


Watchdog and probe metadata

Tables: source_health (source='watchdog'), capture_alarms; probe tags on real event tables.

Identitycapture_alarms.id; invariant I-1I-16
Timestampscapture_alarms.raised_at, resolved_at; watchdog bumps source_health.updated_at every 60s
Synthetic / probeProbe UUID in probe_tag on events / browser_events / agentic_sessions
Expected absentNo alarms when capture healthy; probe_last_run_ts NULL until first probe cycle
In-flightActive unacked alarms (acked_at IS NULL, resolved_at IS NULL)
FreshnessWatchdog I-7: watchdog row stale > 180s → doctor [!!]
Safe to citeAlarm invariant_id, details_json summary, raised_at when warning about stale or broken capture
Do not citeProbe-tagged event rows as user evidence; unacked alarms as factual claims about user work

Knowledge nodes (enriched layer)

All MCP retrieval tools (ask, search_knowledge, search_hybrid, get_context) return knowledge nodes — LLM-enriched summaries linked to source rows above.

Identityknowledge_nodes.uuid (prefer for citations); integer id internal
Timestampscreated_at on node; captured_at on SearchResult = best-effort max linked source time
Synthetic / probeNodes linked only to probe-tagged sources are excluded by eligibility
Expected absentEnrichment backlog when brain down; ineligible sources never enqueue
Safe to citeuuid, summary, outcome, tags, cwd, git_branch, linked_*_ids / linked_source_ids
Do not citeembed_text as user-facing prose (identifier-dense index text); orphan nodes with no links (rare)

Cross-link: node schema and link tables in ../schema.md.


Quick reference: MCP source filter vs storage

MCP source argStorage queriedNotes
shellknowledge_node_eventseventsShell commands only, not claude-tool
claudeknowledge_node_agentic_sessionsagentic_sessionsAll harnesses
browserknowledge_node_browser_eventsbrowser_events
workflowknowledge_node_workflow_runsworkflow_runsCompleted runs only (retrieval)
claude-auto-memoryknowledge_node_memory_chunksActive revisions only

Adding a contract

When a new capture source ships:

  1. Add a section here and an entry in source_trust_contracts.json.
  2. Extend retrieval-eligibility.md and retrieval_eligibility.py.
  3. Add a trust-eval case in trust_eval_cases.json if the source appears in retrieval.
  4. Update sources.md for operator-oriented capture detail.