# Eventing

{% tabs %}
{% tab title="End users" %}
Events are what you see in the run log and UI stream.

They explain **why CrossWatch did nothing** or **why it did something scary**.

#### Events worth recognizing

* `pair:skip` → auth failed or hard gate
* `writes:skipped` → provider down, so no writes
* `snapshot:suspect` → drop guard treated a snapshot as unsafe
* `mass_delete:blocked` → delete wave blocked
* `blocked.counts` → items were blocked by tombstones/blackbox/unresolved

#### Quick workflow

1. Scan for `pair:skip` and `auth_failed`.
2. Check for `snapshot:suspect` before trusting delete plans.
3. If adds repeat, look for `blocked.counts` and unresolved.

Related:

* Safety model: [Guardrails](/blueprint-architecture/orchestrator/guardrails.md)
* Provider down/auth behavior: [Health](/blueprint-architecture/orchestrator/health.md)
  {% endtab %}

{% tab title="Power users" %}
This doc explains how the orchestrator reports progress: events, debug lines, API hit samples, and the conventions the UI expects.

<details>

<summary>Implementation notes</summary>

**Core code:** `cw_platform/orchestrator/_emit.py` (Emitter) + usage across `_pairs*.py`\
**Also relevant:** provider `health(..., emit=...)` and any provider that emits `api:hit`.

</details>

***

### Two output channels

The orchestrator produces two kinds of output:

1. **Structured events** (`Emitter.emit`)
2. **Human log lines** (`Emitter.info` / `Emitter.warn` / `Emitter.err`)

Both typically go to stdout/stderr, but structured events are JSON-like and machine-consumable.

***

### Emitter: what it is

An `Emitter` instance is created per run and passed around via `ctx.emit` and `ctx.dbg`.

It provides:

* `emit(event_name, **data)` → structured event
* `info(msg, **data)` → log line + optional structured sidecar
* `warn(msg, **data)`
* `err(msg, **data)`
* `dbg(event_name, **data)` → debug-only structured event (when runtime debug enabled)

In practice, most modules use:

* `emit` for lifecycle + counts
* `dbg` for internal reasoning (suspect snapshot, blocklist composition, alias matches)

***

### Event naming conventions

Event names are string tokens, generally grouped by subsystem:

#### Run lifecycle

* `run:start`
* `run:pair`
* `run:done`

#### Health

* `health` (per provider)

#### Feature lifecycle

* `feature:start`
* `feature:unsupported`
* `feature:done`

#### Planning

* `one:plan`
* `two:plan`

#### Apply

* `apply:add:start`
* `apply:add:progress`
* `apply:add:done`
* `apply:remove:start`
* `apply:remove:progress`
* `apply:remove:done`
* `apply:unresolved`

#### Guardrails / safety

* `snapshot:suspect` / `snapshot.guard`
* `mass_delete:blocked`
* `blocked.counts`
* `writes:skipped`

#### Metrics

* `api:hit`
* `api:totals`
* `http:overview`
* `stats:overview`

These are not enforced by a schema, but the UI expects many of them.

***

### Standard fields (what you’ll usually see)

Most events include some subset of:

* `pair` / `pair_key`
* `src`, `dst`
* `feature`
* `mode` (`one-way` / `two-way`)
* counts:
  * `src_count`, `dst_count`
  * `adds`, `removes`
  * `attempted`, `confirmed`, `unresolved`, `errors`, `skipped`
* timings:
  * `ms` (milliseconds)
  * `started_at`, `ended_at` (epoch)

Pair context is usually implicit via scope env vars, but many events still include it explicitly for UI clarity.

***

### Debug gating

Debug events are controlled by runtime flags in config, typically under:

* `config["runtime"]["debug"]`
* `config["runtime"]["suspect_debug"]`

When debug is off:

* `dbg(...)` emits nothing.

When debug is on:

* you’ll see internal events such as:
  * alias token matches
  * blocklist breakdown per source
  * suspect snapshot reasons and checkpoints

***

### API hit samples (`api:hit`)

This is a “soft metric” system: providers can report API calls without the orchestrator needing to know provider internals.

A provider can call:

* `emit("api:hit", provider="SIMKL", op="GET /sync/activities", ms=123, ok=True, status=200)`

The orchestrator aggregates these into:

* totals per provider
* totals per op group (sometimes)
* overall http overview

These totals are emitted at the end:

* `api:totals`
* `http:overview`

And may be written into `state.json` metrics as well.

If you want useful performance troubleshooting, emit api:hit aggressively in providers.

***

### Count previews (plan/apply)

For large runs, the orchestrator often emits:

* total counts
* small “preview” lists of first N items (titles/keys)

This is intentionally limited to avoid:

* huge logs
* UI stream lag

Typical preview size:

* 10–30 items

If you need full details, use debug mode or export via state files.

***

### Warnings and errors

`Emitter.warn/err` produce human-readable lines, but the orchestrator also tries to emit structured events for error contexts.

Common error patterns:

* provider exceptions in build\_index/add/remove
* auth failures
* unexpected provider response shapes (missing counts)
* mass delete blocks

When errors happen, you’ll usually see:

* `ok:false` in the feature summary
* `errors > 0`
* plus unresolved entries recorded if applicable

***

### UI integration expectations (practical)

The UI generally expects:

* A run start event quickly (to show spinner)
* Feature start / plan / apply / done sequences
* Periodic progress during large apply batches
* A final run done event with totals

If you add new event names, keep them:

* short
* lower-case with `:` separators
* grouped by subsystem (e.g., `cache:*`, `provider:*`)

***

### Tips for provider authors

If you’re implementing provider ops:

* Emit `api:hit` on every external request (include op + ms + ok).
* Emit `health` details that include per-feature flags.
* When returning unresolved items, include IDs so unresolved can be tracked.

If you do these three, debugging becomes 10x easier.

***

### Related pages

* High-level orchestrator flow: [Orchestrator](/blueprint-architecture/orchestrator.md)
* Apply events and result normalization: [Applier](/blueprint-architecture/orchestrator/applier.md)
* Snapshot debug events: [Snapshots](/blueprint-architecture/orchestrator/snapshots.md)
* Safety events: [Guardrails](/blueprint-architecture/orchestrator/guardrails.md)
  {% endtab %}
  {% endtabs %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://wiki.crosswatch.app/blueprint-architecture/orchestrator/eventing.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
