> ## Documentation Index
> Fetch the complete documentation index at: https://critiqor.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Observation Engine: Session Lifecycle and Evidence Capture

> Critiqor's observation engine manages run sessions from creation through finalization, capturing runtime events and orchestrating diagnosis generation.

The observation engine is the part of Critiqor that lives between your terminal and the OpenClaw process. It creates a persistent session record the moment monitoring begins, owns the child process lifecycle, merges two independent event streams at finalize time, and drives the diagnosis engine to produce `diagnosis.json`. All of this happens in `runtime.py` and `session.py` — the CLI layer only routes to these functions; it does not participate in any of the runtime logic.

## Session Lifecycle

Every run moves through a strict state machine. States are written into the session record at each transition so you can always inspect `runs/<run_id>.json` to see exactly where a session stopped.

```
IDLE → MONITORING → FINALIZING → COMPLETED
                               → ABORTED (on error)
```

| State          | When it occurs                                                                                                                                                                                                                                             |
| -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **IDLE**       | The implicit starting state before `critiqor monitor openclaw` is called. No files exist yet.                                                                                                                                                              |
| **MONITORING** | `create_session()` has run, the plugin is loaded, OpenClaw is running as a child process, and events are being captured into `runs/<run_id>/session.json`.                                                                                                 |
| **FINALIZING** | `critiqor finalize` was called. `request_finalize()` stamps the session `FINALIZING` and `active_session.json` transitions to the same status so nothing else can claim the active slot. Evidence is being assembled and the diagnosis is being generated. |
| **COMPLETED**  | `finalize_session()` has finished. `diagnosis.json` has been written, the session record is updated with full diagnosis data, and `active_session.json` is removed.                                                                                        |
| **ABORTED**    | An error occurred during monitoring (launch failure, unhandled `OSError`, or explicit call to `abort_session()`). The session record is stamped `ABORTED` and `active_session.json` is removed.                                                            |

Only sessions in `MONITORING` or `FINALIZING` status are considered active. `load_active_session()` returns `None` for any other status, which prevents accidental double-starts.

## Session File Structure

`create_session()` writes the initial session record to `runs/<run_id>.json` using `write_json()`, a low-level helper that writes atomically through a `.tmp` rename. Path resolution for every run artifact goes through `paths_for(runs_dir)`, which returns a `SessionPaths` object whose methods — `run_path(run_id)`, `evidence_summary_path(run_id)`, and `diagnosis_path(run_id)` — resolve to `runs/<run_id>.json`, `runs/<run_id>/session.json`, and `runs/<run_id>/diagnosis.json` respectively. At finalize time `write_json` is called again through `evidence_summary_path` to update the evidence file and a second time through `diagnosis_path` to write the completed diagnosis artifact.

The structure below reflects what is present in `runs/<run_id>.json` at creation time and what gets filled in at finalization:

```json theme={null}
{
  "run_id": "run_001",
  "status": "MONITORING",
  "lifecycle": [
    { "state": "IDLE", "timestamp": "2025-06-01T12:00:00.000Z" },
    { "state": "MONITORING", "timestamp": "2025-06-01T12:00:00.000Z" }
  ],
  "timestamps": {
    "created_at": "2025-06-01T12:00:00.000Z",
    "started_at": "2025-06-01T12:00:00.000Z",
    "finalized_at": null
  },
  "metadata": {
    "agent_id": "openclaw_agent",
    "tenant_id": "default",
    "framework": "openclaw",
    "visibility": "private",
    "benchmark_id": "openclaw_runtime_v1",
    "difficulty_tier": "standard"
  },
  "event_log": [],
  "diagnosis": null,
  "trust_score": null,
  "confidence_score": null,
  "causal_graph": null,
  "failure_analysis": null,
  "cost_analysis": null
}
```

After `finalize_session()` completes, `status` becomes `"COMPLETED"`, `finalized_at` is stamped, `event_log` contains the merged event stream, and `diagnosis`, `trust_score`, `causal_graph`, `failure_analysis`, and `cost_analysis` are all populated from the diagnosis engine output.

## How Events Flow Into the Session

Two independent sources are merged at finalize time. Neither source blocks the other — they run in parallel during monitoring and are joined only when `critiqor finalize` is called.

**Source 1 — Internal lifecycle events**

Written directly to `runs/<run_id>.json` by `append_event_to_run()` throughout the monitoring session. These include:

* `state_transition` — every status change (`MONITORING`, `FINALIZING`, `COMPLETED`, `ABORTED`)
* `process_start` — recorded immediately after `subprocess.Popen` succeeds, includes the PID and launch command
* `process_end` — recorded when the child process exits, includes exit code and PID
* `error_event` — recorded on non-zero exit, timeout, `KeyboardInterrupt`, or `OSError`

**Source 2 — Plugin evidence events**

Written in real time to `runs/<run_id>/session.json` by the bundled OpenClaw plugin as the agent runs. These are the structured tool and extension API events described in the [OpenClaw Plugin](/architecture/openclaw-plugin) page.

At finalize time, `load_session_evidence_events()` reads `session.json`, normalizes each event through `normalize_plugin_event()` (which maps raw plugin event types to canonical Critiqor types), and merges the result with the internal lifecycle events before passing the combined stream to the diagnosis engine.

## Environment Variables Set for OpenClaw

Before spawning the child process, the runtime calls `openclaw_environment()` to build an enriched copy of the current environment. The following variables carry run context into the plugin:

| Variable                | Purpose                                                                                                    |
| ----------------------- | ---------------------------------------------------------------------------------------------------------- |
| `CRITIQOR_RUN_ID`       | The current run identifier (e.g. `run_001`). The plugin uses this to construct the path to `session.json`. |
| `CRITIQOR_RUNS_DIR`     | Absolute path to the runs directory. The plugin resolves all write paths relative to this value.           |
| `CRITIQOR_AGENT_ID`     | Agent identifier passed from `--agent-id`. Stored in session metadata.                                     |
| `CRITIQOR_TENANT_ID`    | Tenant identifier passed from `--tenant-id`. Stored in session metadata.                                   |
| `CRITIQOR_VISIBILITY`   | Dashboard visibility state (`private`, `public`, `anonymous`, or `shared`). Applied at ingestion.          |
| `CRITIQOR_EVENT_SOURCE` | Always `"openclaw"` for monitored sessions. Identifies the event origin in the session record.             |

In addition, `OPENCLAW_BUNDLED_PLUGINS_DIR` is set to the parent of the `critiqor-openclaw` plugin directory, which causes OpenClaw to auto-load the plugin without any manual configuration.

## Related Pages

* [Architecture Overview](/architecture/overview) — The two-layer design and the full session pipeline from CLI to dashboard.
* [OpenClaw Plugin](/architecture/openclaw-plugin) — How the plugin captures events and writes them to `session.json`.
