---
title: "Agent Run Log"
description: "Internal endpoint for recording structured execution logs for every OpenClaw agent run, feeding the Intelligence Dashboard and RLHF quality loop"
canonical: "https://docs.ainative.studio/docs/agent-cloud/agent-run-log"
last-updated: "2026-10-13T21:42:35.442Z"
---

# Agent Run Log

Source: https://docs.ainative.studio/docs/agent-cloud/agent-run-log

> Internal endpoint for recording structured execution logs for every OpenClaw agent run, feeding the Intelligence Dashboard and RLHF quality loop

Internal endpoint that records structured execution logs for every OpenClaw agent run. Each agent calls this endpoint at the end of its cron cycle, producing a permanent audit trail that feeds the Intelligence Dashboard and the recursive RLHF quality loop.

:::caution AINative-internal infrastructure, not a third-party integration point
This endpoint requires no authentication and is intended only for AINative's own OpenClaw agent
swarm reporting its own run logs. It is documented here for completeness, not as an integration
point for externally-built agents.
:::

Refs #3345

---

## Purpose

Every OpenClaw agent (Sage, Aurora, Nova, Atlas, Lyra, Luma, Helios, Scout, etc.) runs on a cron schedule. When a run finishes, the agent reports what it did, whether it succeeded, and a structured summary of actions taken. This data serves three consumers:

1. **Intelligence Dashboard** -- the `tasks_completed_today` metric counts successful runs from `agent_run_log`.
2. **Auditor Agent** -- reads recent runs to validate agent liveness and detect silent failures.
3. **RLHF Quality Loop** -- each logged run automatically triggers an RLHF quality score via Celery, enabling recursive improvement measurement over time.

---

## Endpoint

### `POST /api/v1/internal/agent-run-log`

Records a single agent run completion.

**Status Code:** `204 No Content` on success

**Authentication:** None. This is an internal-only endpoint behind Kong's internal route. It is not exposed to public consumers.

### Request Body

| Field           | Type     | Required | Description                                                      |
|-----------------|----------|----------|------------------------------------------------------------------|
| `agent_id`      | `string` | Yes      | Agent identifier (e.g., `sage`, `nova`, `aurora`). Must not be empty. |
| `status`        | `string` | Yes      | One of: `success`, `partial`, `failed`.                          |
| `actions_taken` | `object` | No       | Structured summary of what the agent did. Defaults to `{}`.      |

### Validation Rules

- `agent_id` is trimmed of whitespace and must not be empty after trimming.
- `status` must be exactly one of `success`, `partial`, or `failed`. Any other value returns a `422 Unprocessable Entity` error.
- `actions_taken` is an arbitrary JSON object. By convention, it should include a `summary` key with a human-readable string describing the run.

### Database Table

```sql
CREATE TABLE agent_run_log (
    id                        UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    agent_id                  VARCHAR(255) NOT NULL,
    run_at                    TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    action_summary            TEXT,
    actions_taken             JSONB DEFAULT '{}',
    status                    VARCHAR(32) NOT NULL DEFAULT 'success',
    consecutive_silence_count INT NOT NULL DEFAULT 0,
    rlhf_scores               JSONB DEFAULT NULL,
    created_at                TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_agent_run_log_agent_id ON agent_run_log(agent_id);
CREATE INDEX idx_agent_run_log_created_at ON agent_run_log(created_at DESC);
CREATE INDEX idx_agent_run_log_status ON agent_run_log(status);
CREATE INDEX idx_agent_run_log_agent_created ON agent_run_log(agent_id, created_at DESC);
```

### Column Details

| Column                      | Description                                                              |
|-----------------------------|--------------------------------------------------------------------------|
| `id`                        | Auto-generated UUID primary key.                                         |
| `agent_id`                  | Which agent produced this run (e.g., `sage`).                            |
| `run_at`                    | Server-side UTC timestamp of when the run was recorded.                  |
| `action_summary`            | Extracted from `actions_taken["summary"]`. Used for quick display.       |
| `actions_taken`             | Full structured payload as JSONB. Contains everything the agent reported.|
| `status`                    | `success`, `partial`, or `failed`.                                       |
| `consecutive_silence_count` | Tracks how many consecutive runs had no meaningful output. Used by the Auditor. |
| `rlhf_scores`               | Populated asynchronously by the RLHF scoring Celery task after insertion.|
| `created_at`                | Row creation timestamp.                                                  |

### Insert Behavior

Unlike the peer swarm heartbeat (which upserts), the agent run log **always inserts a new row**. Every run produces a distinct record, building a complete execution history per agent.

After the row is inserted and committed, the endpoint fires an asynchronous Celery task (`score_agent_run`) to compute RLHF quality scores for the run. This is fire-and-forget: if the RLHF task fails, the run log row is still committed and the failure is logged as a warning.

:::note RLHF Daemon Thread
The RLHF scoring task dispatch now runs in a daemon thread to prevent request hangs. Previously, if the Celery broker was unavailable, the `score_agent_run.delay()` call could block the HTTP response. The daemon thread ensures the 204 response is returned immediately regardless of broker availability.
:::

---

## Intelligence Dashboard Integration

### `tasks_completed_today`

The `GET /api/v1/public/platform/intelligence` endpoint counts today's successful runs:

```sql
SELECT COUNT(*) FROM agent_run_log
WHERE status = 'success'
AND created_at >= :today_start
```

This count is combined with peer swarm `improvement_loops` and Agent Cloud task counts to produce the final `tasks_completed_today` metric.

### Agent Event Cards

The dashboard generates per-agent event cards from the run log via `_get_peer_swarm_events()`. Each agent's most recent run becomes a card showing:

- The agent name and role (e.g., "Sage -- Backend")
- What the agent did (from `action_summary`)
- Run status (`completed` or `failed`)
- How recently it ran

### Auditor Agent

The Auditor agent calls `recent_runs()` from `agent_run_log_service.py` to read structured state across all agents, detecting patterns like:

- Agents that have stopped reporting (liveness check)
- Agents with consecutive failures
- Agents with empty `actions_taken` (silent runs)

---

## RLHF Quality Loop

After every run log insertion, the endpoint triggers:

```python
from app.tasks.rlhf_tasks import score_agent_run
score_agent_run.delay(str(row_id))
```

This Celery task scores the run's quality and writes results back to the `rlhf_scores` JSONB column on the same row. The RLHF data feeds the `rlhf_quality_score_7d` metric on the Intelligence Dashboard, enabling the team to track whether agent output quality is improving over time.

Refs #3347

---

## Example Integration

### Python agent script

```python
import requests

BACKEND_URL = "https://api.ainative.studio"

def report_run(agent_id: str, status: str, actions: dict) -> None:
    """Report agent run completion to the backend."""
    resp = requests.post(
        f"{BACKEND_URL}/api/v1/internal/agent-run-log",
        json={
            "agent_id": agent_id,
            "status": status,
            "actions_taken": actions,
        },
        timeout=10,
    )
    resp.raise_for_status()


## Example: Sage agent completing a backend task
report_run(
    agent_id="sage",
    status="success",
    actions_taken={
        "summary": "Fixed database connection pool leak in session.py",
        "files_changed": ["src/backend/app/db/session.py"],
        "tests_run": 14,
        "tests_passed": 14,
        "pr_created": "#3456",
    },
)

## Example: Aurora agent with a partial result
report_run(
    agent_id="aurora",
    status="partial",
    actions_taken={
        "summary": "Ran QA suite; 3 of 5 test suites passed",
        "suites_total": 5,
        "suites_passed": 3,
        "failures": ["test_billing_edge_cases", "test_oauth_refresh"],
    },
)

## Example: Agent that failed
report_run(
    agent_id="nova",
    status="failed",
    actions_taken={
        "summary": "Security scan aborted: dependency resolution timeout",
        "error": "pip install timed out after 120s",
    },
)
```

### curl

```bash
curl -X POST https://api.ainative.studio/api/v1/internal/agent-run-log \
  -H "Content-Type: application/json" \
  -d '{
    "agent_id": "sage",
    "status": "success",
    "actions_taken": {
      "summary": "Deployed schema migration for billing_events table",
      "tables_modified": ["billing_events"],
      "rows_affected": 0
    }
  }'
```

Response: `204 No Content`

---

## Service Layer

The `agent_run_log_service.py` module provides an async interface for writing and reading run logs directly via SQLAlchemy (bypassing the REST endpoint). This is used by agents running inside the same process:

```python
from app.services.agent_run_log_service import write_run_log

await write_run_log(
    agent_id="atlas",
    actions_taken={"summary": "Scaled worker replicas to 3"},
    status="success",
)
```

Both the REST endpoint and the service layer write to the same `agent_run_log` table with identical column mappings.

---

## Related Files

| File | Purpose |
|------|---------|
| `src/backend/app/api/internal/agent_run_log.py` | Endpoint implementation |
| `src/backend/app/services/agent_run_log_service.py` | Async service layer for direct DB access |
| `src/backend/app/tasks/rlhf_tasks.py` | Celery task that scores runs after insertion |
| `src/backend/app/api/v1/endpoints/platform_intelligence.py` | Dashboard that reads from `agent_run_log` |
| `scripts/sync-production-schema.py` | Table creation (search for `sync_agent_run_log_table`) |
