Contents

Event records

Read the event log, write-side severity configuration and event registry. Database reference owns JSON conventions; timestamp discipline owns clock conventions.

Table: events

Cross-stack structured event log. Unified envelope for agent tool calls, session lifecycle, backend telemetry, frontend analytics, and system events. All source types share the same schema, enabling cross-source queries on a single table. Workbench analytics use the mounted universe's own /api/events collector, which stamps the viewer's actor when a credential is present; workbench telemetry explains consent, admission, URL hygiene, packaging, durable actors.attribution ownership, and the actor_visitor_links join that ties anonymous page views to actors.

Compatibility note: first-class tool-call correlation columns are optional across local installs and emitters. Readers and emitters must tolerate their absence until all live event tables expose the full correlation surface.

id INTEGER PRIMARY KEY
event_id TEXT UNIQUE NOT NULL -- UUID, deduplication key (ON CONFLICT DO NOTHING)
source_type TEXT NOT NULL -- 'agent' | 'backend' | 'frontend' | 'system' | 'script' | 'hook' | 'skill'
session_id TEXT NOT NULL -- session/request correlation ID
severity TEXT NOT NULL DEFAULT 'INFO' -- DEBUG | INFO | WARN | ERROR | FATAL
event_kind TEXT NOT NULL -- taxonomy tier 1 (e.g., 'system', 'domain', 'user')
event_type TEXT NOT NULL -- taxonomy tier 2 (e.g., 'tool_call', 'session')
event_name TEXT NOT NULL -- PascalCase event name (e.g., 'HarnessToolCallCompleted')
event_outcome TEXT -- nullable outcome (e.g., 'completed', 'failed')
actor_id INTEGER -- nullable Yoke control-plane subject; references actors(id)
environment TEXT -- runtime environment (e.g., 'dev', 'prod')
service TEXT NOT NULL DEFAULT 'cli' -- emitting service
project_id INTEGER -- nullable indexed ownership; context, item, session, then project token
item_id TEXT -- owned backlog join key; public reads project it to public_ref
task_num INTEGER -- epic task number
agent TEXT -- agent role (e.g., 'engineer', 'tester')
tool_name TEXT -- tool that was called (e.g., 'Bash', 'Read')
duration_ms INTEGER -- execution duration in milliseconds
exit_code INTEGER -- process exit code
trace_id TEXT -- distributed trace ID
anomaly_flags TEXT -- → JSONB on Postgres (array shape); today a comma-separated anomaly-flag list per `docs/event-contract.md` (e.g., 'nonzero_exit,retry_loop')
tool_use_id TEXT -- target first-class dedup key for tool-call events; current live coverage is still incomplete
client_timing_id TEXT -- key a hook client minted for its own dispatch, so the wall time it reports afterwards finds this row by index; cleared once that report lands
envelope TEXT -- → JSONB on Postgres; full JSON envelope for lossless storage
created_at TEXT NOT NULL -- app-supplied ISO-8601 UTC; see timestamp discipline in the introduction

Indexes: source_type, session_id, (event_name, created_at), (severity, created_at), created_at, actor_id, trace_id, project_id, tool_name, (event_kind, event_type), plus the partial dedup index (tool_use_id, event_name) WHERE tool_use_id IS NOT NULL and the partial correlation index (client_timing_id) WHERE client_timing_id IS NOT NULL on DBs with the full correlation surface. Correlate through an indexed column; matching a substring of envelope with LIKE has no index to use and reads every row the other predicates admit.

Event reads project nested envelope item identities and item history target_id to complete public refs; storage keeps owned join keys. Other record IDs retain their domain meaning.

Engine identity is actor-only. Historical JSON envelopes may retain a null human-user key, but fresh schemas, writers, readers, and filters do not expose that retired surface.

Deduplication: The event_id column has a UNIQUE constraint. Inserts use ON CONFLICT DO NOTHING so duplicate event IDs are silently dropped.

Write-side severity filtering: Before inserting, events are checked against the severity_config table. Events below the configured minimum severity for their (event_name, source_type) pair are silently dropped without error. The acknowledged anonymous frontend collector bypasses this optional filter; retention still applies.

Retention: yoke universe prune --dry-run previews the connected universe using its effective settings; yoke universe retention get shows them. Universe retention owns severity/name windows, durable-reference protection, bounded foreground apply, counts and recovery. DEBUG remains the on-demand capture tier and has the shortest retention.

Table: severity_config

Write-side severity filtering configuration for the events table. Controls which severity levels are persisted per event name and source type combination.

id INTEGER PRIMARY KEY
event_name TEXT NOT NULL DEFAULT '*' -- event name pattern ('*' = wildcard)
source_type TEXT NOT NULL DEFAULT '*' -- source type pattern ('*' = wildcard)
min_severity TEXT NOT NULL DEFAULT 'INFO' -- DEBUG | INFO | WARN | ERROR | FATAL
created_at TEXT NOT NULL -- app-supplied ISO-8601 UTC; see timestamp discipline in the introduction
UNIQUE(event_name, source_type)

Lookup order: exact (event_name, source_type) > (event_name, *) > (*, source_type) > (*, *). First match wins. Seeded with catch-all (*, *, INFO) on init.

Table: event_registry

Central registry of all known event types. Governs event naming, ownership, and lifecycle. Used by yoke_core.domain.observe (lint-event-registry guardrail) (PreToolUse hook) and doctor health checks to enforce event governance.

event_name TEXT PRIMARY KEY -- PascalCase event name (e.g., 'HarnessToolCallCompleted')
event_kind TEXT NOT NULL -- taxonomy tier 1 (e.g., 'system', 'domain')
event_type TEXT NOT NULL -- taxonomy tier 2 (e.g., 'tool_call', 'session')
owner_service TEXT NOT NULL -- emitting service (e.g., 'yoke_core.domain.observe')
description TEXT NOT NULL -- human-readable description
severity_default TEXT NOT NULL DEFAULT 'INFO' -- default severity level

Source of truth: the rows come from the code event catalog, yoke_core.domain.event_catalog. Every server boot makes event_registry equal that table: it inserts missing names, rewrites drifted metadata, and deletes names the catalog no longer lists. A second boot writes nothing. The table has no status column; a name with no producer is deleted from the catalog, not marked. Registry rows are not edited by hand or through a CLI.

Drift: emitting a name the catalog does not list still records the event. The registry audit subcommand and the HC-event-registry-coverage doctor check report it as rogue (emitted but not registered), and report stale entries (registered but not emitted in 30 days, apart from expected low-cadence events).

Event model · Yoke