Skip to content

Latest commit

 

History

History
111 lines (80 loc) · 6.1 KB

File metadata and controls

111 lines (80 loc) · 6.1 KB
type concept
topic architecture
audience
human
agent

Events

Every state change in reyn emits an audit-event. The audit-event log is the runtime's diary: a JSONL stream that records what happened, in order, with enough detail to replay the run.

Why everything is an audit-event

There is no separate logger, tracer, or telemetry hook. The same channel powers:

  • Live debug output. Console reporters subscribe to the audit-event stream and render each audit-event as it arrives.
  • Replay. reyn events <log_file> re-renders a saved log to the console without re-invoking the LLM.
  • Eval analytics. Eval reports aggregate audit-event data (token usage, validation errors) per case.
  • Crash recovery (implemented). Crash recovery reconstructs agent state from the WAL (.reyn/state/wal.jsonl) plus seq-keyed snapshots as its substrate — not the audit-event log. User-facing rewind/resume (PITR + global rewind) is a separate design; see Time-travel.

If the OS is the only mutator (P3) and every mutation emits an audit-event, the audit-event log is sufficient. There's no "what else happened" to chase down.

What gets recorded

A few of the larger buckets:

  • LLM and contextllm_called.
  • Control IR — one audit-event per op kind, for example:
    mcp_called
    sandboxed_exec_started
    semantic_search_embed_failed
    web_search_started
    
    plus permission_denied. A file op like a read instead rides the shared tool_executed kind, naming the specific operation in its op field rather than as a kind of its own.
  • User interactionuser_message_received, user_intervention_received, chat_started, chat_stopped, turn_cancelled.
  • Agent-to-agent messagingagent_message_sent, agent_request_received, agent_response_received, agent_message_refused, chain_timeout. Each carries chain_id so a single user request can be traced across hops.

The buckets above are examples. The type field is a closed vocabulary — every kind reyn emits, and nothing else, is enumerated in the events reference, derived from one source of truth in src/reyn/core/events/event_schema.py and CI-checked against both the emitting code and the page. A consumer outside reyn can therefore enumerate the complete set of types it may receive.

Task subscription events — historical WAL design, now removed

Task↔session binding changes (assignee/requester) were designed (#2187) to be recorded as WAL kinds (task_subscribed, task_rebound — StateLog, .reyn/state/wal.jsonl), not the P6 audit-event log — so if this mechanism were live, you would look for it there, not here. It never got a writer, and #3436 removed the two kinds from the WAL vocabulary entirely (dead declaration, no producer). Task↔session binding changes are not recorded anywhere today. The WAL is generally the crash-recovery and time-travel substrate; the audit-event log is the per-run trace — they are separate logs with different durability contracts (see Time-travelWAL vs audit-event separation).

What an audit-event is

Every audit-event has a stable envelope:

type      — event type (see reference)
timestamp — ISO-8601 timestamp
data      — flat dict of payload fields specific to the type

Key fields present in most audit-events (in data):

run_id    — uuid for the run (present on most run-scoped audit-events)

Note: run_id is present on most run-scoped audit-events (llm_called, permission_denied) but absent from some audit-events emitted outside a run context (e.g. chat_started). Which kinds carry run_id is a payload question, not a vocabulary one — the closed set above says which kinds exist, and does not partition them by field. There is no enumeration of the run_id-carrying subset; read the per-kind payloads in the reference.

Audit-events with required fields (FP-0021)

A growing set of audit-event kinds are required to carry specific audit fields in their data dict (the required fields vary per kind — e.g. llm_called requires model; permission_granted/permission_denied require run_id, actor, phase). The authoritative, current registry lives in src/reyn/core/events/event_schema.py (EVENT_AUDIT_REQUIREMENTS) — this list has grown over time and will keep growing, so it is not duplicated here. Dedicated per-feature invariant tests (e.g. tests/test_session_lifecycle_events_1800.py, tests/test_mcp_search_tool_invariants.py, tests/test_chat_turn_completed_inline.py) each assert that their event kinds are declared here with the correct required fields, on every CI run.

Enforcement is test-time only (not at emit() runtime) to keep production overhead zero.

Stable shape makes the log machine-readable without a custom parser per consumer.

What audit-events are NOT

  • Not application logs. A workflow author shouldn't emit free-form audit-events. The set is OS-defined.
  • Not memory. Audit-events are the runtime's per-run record; memory is across-run knowledge. See ../data-retrieval/memory.md.
  • Not the source of truth for artifacts. Artifacts pass through the workspace channel; audit-events record that they passed.

Reading audit-events as a debugging tool

When something looks wrong:

  1. Find the run id from the run output's last line (events saved → ...).
  2. reyn events .reyn/events/<run_id>.jsonl --conversation to see what each LLM call looked like and what it returned.
  3. Or --filter permission_denied to jump straight to where the OS refused an op.

You don't need a debugger; the log already has the information.

See also

Audit-events are the per-run trace, not the crash-recovery or time-travel substrate — those are WAL-backed (see Time-travel). For payload-level trace inspection, see reference/dogfood-tracing.md.