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. Web dashboard analytics use the mounted universe's own /api/events collector, which stamps the viewer's actor when a credential is present; web dashboard 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).

Frontend envelopes also carry client_ip from the trusted request client, optional geo_country, geo_region, geo_city, network_org, and browser language, time_zone, screen, viewport. GeoIP setup, privacy and DB-IP attribution belong to operations. events.query.run accepts visitor_id, matching only frontend envelopes; authorization still excludes global rows for actors without org owner or admin access.

Optional device facts use standard browser APIs, with no prompts or active fingerprinting. Fields are absent when an API is unavailable or its value is unusable; a failed lookup never drops the event:

Field Source
os_version Client Hints platformVersion; collector parses iOS/iPadOS User-Agent and overrides it; no frozen macOS Safari version
device_model Client Hints model
cpu_architecture Client Hints architecture
cpu_bitness Client Hints bitness
cpu_cores navigator.hardwareConcurrency
device_memory_gb navigator.deviceMemory (browser rounded and capped)
pixel_ratio window.devicePixelRatio
color_depth screen.colorDepth
touch_points navigator.maxTouchPoints
gpu_renderer WebGL UNMASKED_RENDERER_WEBGL, including generic values
connection_type navigator.connection.effectiveType
connection_downlink_mbps navigator.connection.downlink

Client Hints resolve once per page, never delaying a view; flush fills resolved hints into queued events. WebGL is read once from a throwaway context without rendering or reading pixels. No canvas image, audio or font probing, hashing or combined device identifier is collected. These are disposable telemetry; no operational code reads them. The collector drops invalid facts without rejecting the event: nonempty strings up to 128 characters; CPU cores integer 1..1024; memory >0..1024 GB; pixel ratio >0..16; touch points integer 0..256; color depth integer 1..64; downlink 0..100000 Mbps. Other envelope fields still pass through.

Event model · Yoke