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

# Autonomous loops

> Import production agent traces, grade them, reproduce real failures against service clones, and optionally open fix PRs from verified reproductions.

Archal has one autonomous trace loop you can drive from a terminal:

| Loop             | Use it for                                                                                                                  | Primary command   |
| ---------------- | --------------------------------------------------------------------------------------------------------------------------- | ----------------- |
| Post-prod traces | Import traces after an agent has already run, grade them, reproduce real failures against clones, and open reviewable fixes | `archal autoloop` |

Use Autoloop when your agent already produces traces and you want Archal to keep
checking those traces after they happen. Autoloop imports traces from a read-only
source, grades them, tries to reproduce real failures against service clones,
and opens a GitHub PR only when a reproducible fix exists.

Autoloop is evidence-bounded. It does not replay every arbitrary production trace
end to end. It attempts reproduction only when the trace, standing scenario,
and seed templates contain enough information to rebuild the relevant clone
state. Missing evidence should produce a blocked artifact, not a guessed seed.

The important boundary is the same as `archal run`: your agent and harness keep
using normal service SDKs and normal service URLs. Archal handles trace import,
isolation, clone routing, scoring, artifacts, and the GitHub workflow behind
that boundary.

## When to use Autoloop

Use Autoloop for a repo that has all three:

* a headless command for running the agent
* production or staging traces that can be normalized into Archal's trace schema
* a GitHub repo where fixes can be proposed as PRs

If you are still writing the first scenario for an agent, start with
[Run scenarios against your agent](/guides/run-with-agent). If you want to keep
a clone alive while debugging manually, use [Clone sessions](/guides/clone-sessions).

## How the loop works

Autoloop has two entry points:

* **Hosted database source**: `archal autoloop --source postgres|supabase ...`
  registers a read-only trace source with Archal. Hosted workers poll it and
  write run state to the dashboard.
* **Local trace directory**: `archal autoloop ./traces --repo .` watches a local
  directory with one trace file per run and writes repo-local artifacts under
  `.archal/autoloop/`.

Hosted Autoloop supports two ingestion paths: hosted polling for
Supabase/Postgres, and client-uploaded normalized traces for file, HTTP/OTel,
Langfuse, Braintrust, S3/GCS, and custom sources.

| Tier             | Providers                                                               | Current path                                                                                   |
| ---------------- | ----------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| Supported hosted | `supabase`, `postgres`                                                  | Register with `archal autoloop --source ...`                                                   |
| Client upload    | `file`, `http`, `otel`, `langfuse`, `braintrust`, `s3`, `gcs`, `custom` | Send traces via the dashboard OTLP endpoint or drop normalized JSON into an autoloop watch dir |

Client upload still needs workspace auth. Use `archal login` for local setup, or
set `ARCHAL_TOKEN` to a workspace API key in CI and include `--repository` so
Archal knows which repo should receive issues or PRs.

The phases are:

| Phase     | What Archal does                                                             | If it cannot continue                                                    |
| --------- | ---------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
| Import    | Reads the trace and spans, then normalizes them into Archal trace evidence   | Marks the source or trace as blocked with the connection or schema error |
| Grade     | Decides whether the trace contains a real task failure worth reproducing     | Stores the verdict and reason                                            |
| Seed      | Builds the scenario and clone seed needed to replay the trace-relevant state | Writes the missing evidence instead of guessing                          |
| Reproduce | Runs the generated scenario against clones with the repo harness             | Marks not reproduced, flaky, or reproduced                               |
| Fix       | Asks a coding agent to patch the harness or agent code, then validates it    | Writes blocked status, issue details, or a PR                            |

`fix` is the default policy. Use a narrower policy when you only want part of
the loop.

| Policy      | Stops after              |
| ----------- | ------------------------ |
| `observe`   | import                   |
| `grade`     | grading                  |
| `seed`      | seeding                  |
| `reproduce` | reproduction             |
| `fix`       | PR or blocked fix status |

## Prerequisites

1. Install and log in:

```bash theme={null}
npm install -D archal
npx archal login
npx archal usage
```

In CI or a hosted customer repo, set `ARCHAL_TOKEN` to a workspace API key
(`archal_ws_...`). Use `archal login` for local setup.

2. Install the GitHub App on the target repo:

[Install Archal on GitHub](https://github.com/apps/archal-labs/installations/new)

This applies whether you register a source from the CLI or connect a trace
database from the dashboard. The autoloop opens fixes as pull requests through
the Archal GitHub App, so until the app is installed on the repository the fix
step blocks instead of opening a PR. The dashboard's **Connect a trace database**
form shows the same install link beside the repository field.

3. Make sure the repo has a GitHub origin remote:

```bash theme={null}
git remote get-url origin
```

Hosted sources and `--execution-policy fix` require a remote that resolves to
`github.com/<owner>/<repo>`.

4. Add the agent execution contract under `archal/`:

```text theme={null}
archal/
  harness.json
  scenario.md
  seeds/
    github.json
    slack.json
```

`harness.json` tells Archal how to run the agent. `scenario.md` gives the
standing task and evaluation contract for the trace family. Seed files are
optional, but they make reproduction much more reliable when trace evidence is
thin.

5. Export the model keys your agent and the autoloop worker need.

For a local file-backed loop, the worker checks local environment variables
before it starts. Hosted database sources are registered locally, then operated
by Archal workers.

Smoke-test the harness before letting either loop spend attempts:

```bash theme={null}
AGENT_TASK="Reply with OK and do not use tools." node ./.archal/harness.mjs
```

## Add the harness contract

The minimum `archal/harness.json` is:

```json theme={null}
{
  "version": 1,
  "local": {
    "command": "node",
    "args": ["agent.mjs"]
  }
}
```

The command should run the same headless agent code you would use in
`archal run`: no UI, no browser OAuth, no manual prompts. It should accept the
task from `AGENT_TASK`, call your normal agent runtime, and exit cleanly.

Use `env` only for non-secret defaults that belong in the repo. Keep secrets in
your shell, CI, hosted secret manager, or Archal workspace configuration.

```json theme={null}
{
  "version": 1,
  "local": {
    "command": "pnpm",
    "args": ["agent:run"],
    "env": {
      "NODE_ENV": "test"
    }
  }
}
```

## Add the standing scenario

`archal/scenario.md` is not a one-off reproduction. It is the standing contract
that tells Autoloop what the agent is supposed to do for this class of traces.

```md theme={null}
# Billing support follow-up

## Setup

The trace source contains customer-support agent runs involving Stripe
customers, invoices, subscription status, support messages, and refund
decisions. When Archal reproduces a failed trace, it should create the smallest
clone state needed for the customer, invoice, subscription, and support thread
present in the trace.

## Prompt

Use the customer request and available service state from the trace. Resolve the
support task without changing unrelated customers, invoices, or subscriptions.

## Expected Behavior

The agent should verify the customer and invoice, apply the refund or escalation
policy, make only the necessary service changes, and leave a clear support
reply.

## Success Criteria

- [D] No unrelated customer records are modified
- [D] No refund is created unless the invoice is eligible under policy
- [D] Any required escalation task is created exactly once
- [P] The support reply explains the decision without overpromising

## Config

clones: stripe, jira
timeout: 120
```

The prompt should describe the real task. Do not put instructions in the
scenario that tell the tested agent it is using Archal, clones, or a special
environment.

## Add seed templates

Autoloop can seed from trace evidence, but the best integrations include
repo-owned seed templates. Templates are especially useful when a trace contains
IDs and tool calls but not the complete starting state.

```text theme={null}
archal/seeds/
  stripe-billing-support.json
  jira-support-escalations.json
```

Seed templates should match the services in `archal/scenario.md`. The autoloop
worker prefers repo-owned seed templates when they exist, then fills in
trace-specific IDs and fields during reproduction.

See [Seeds](/guides/seeds) for clone seed formats and built-in examples.

## Autoloop a Supabase or Postgres trace source

Use this path when traces live in a database table such as `ai_traces` with
child spans in `ai_spans`.

Create a read-only database user first. It should be able to select trace and
span rows, but not mutate application data.

```bash theme={null}
export TRACE_DATABASE_URL='postgres://readonly:...'
```

Name the source in the harness repo's `.archal.json`:

```json theme={null}
{
  "autoloop": {
    "source": { "name": "prod-agent-traces" }
  }
}
```

Run a readiness check:

```bash theme={null}
npx archal autoloop \
  --repo . \
  --source supabase \
  --database-url-env TRACE_DATABASE_URL \
  --check
```

Register the source:

```bash theme={null}
npx archal autoloop \
  --repo . \
  --source supabase \
  --database-url-env TRACE_DATABASE_URL
```

This registers the source with the active Archal workspace. The database URL is
sent to Archal during registration and is not written to `.archal/autoloop/`.

If your secret already lives in an external secret manager, pass a reference
instead of a local environment variable:

```bash theme={null}
npx archal autoloop \
  --repo . \
  --source postgres \
  --database-url-secret-ref aws-secretsmanager://customer/prod-agent-traces
```

`--database-url-secret-ref` must be a secret reference, not a plaintext database
URL.

## Map custom trace tables

The defaults match the common shape:

| Purpose              | Default         |
| -------------------- | --------------- |
| Trace table          | `ai_traces`     |
| Span table           | `ai_spans`      |
| Trace id column      | `id`            |
| Span id column       | `id`            |
| Span trace id column | `trace_id`      |
| Trace updated column | `updated_at`    |
| Span updated column  | `updated_at`    |
| Cursor mode          | `updated_at_id` |

If your schema uses different table or column names, map them in the
`.archal.json` `autoloop` block:

```json theme={null}
{
  "autoloop": {
    "source": {
      "mapping": {
        "traces": {
          "table": "public.agent_runs",
          "idColumn": "trace_id",
          "updatedAtColumn": "updated_at"
        },
        "spans": {
          "table": "public.agent_spans",
          "idColumn": "span_id",
          "traceIdColumn": "trace_id",
          "parentSpanIdColumn": "parent_span_id",
          "updatedAtColumn": "updated_at"
        }
      }
    }
  }
}
```

For append-only tables, use `created_at_id` and point the updated columns at
the creation timestamp too. The updated columns are always required (readiness
checks them and the import query projects them), so on tables whose rows never
change after insert, map them to `created_at`:

```json theme={null}
{
  "autoloop": {
    "source": {
      "mapping": {
        "traces": { "createdAtColumn": "created_at", "updatedAtColumn": "created_at" },
        "spans": { "createdAtColumn": "created_at", "updatedAtColumn": "created_at" }
      },
      "cursor": { "mode": "created_at_id" }
    }
  }
}
```

You can also start from a known cursor: `cursor.watermarks` is a repair escape
hatch for rewinding or skipping the import cursor. Watermarks are server-owned
progress state, so leave `cursor.watermarks` unset in normal operation: streams
you do not name keep their stored position across re-registrations. Name a
stream to move its cursor, or set its `at` or `id` to `null` to reset that
stream and re-import from the beginning. Do not leave `null` placeholders in
the config: `{ "at": null }` is a deliberate reset on every registration, not
an unset default.

```json theme={null}
{
  "autoloop": {
    "source": {
      "cursor": {
        "watermarks": {
          "traces": { "at": "2026-05-27T00:00:00.000Z", "id": "trace_123" }
        }
      }
    }
  }
}
```

## Filter imported traces

Use filters when the trace source contains many agents, tenants, or statuses.

```json theme={null}
{
  "autoloop": {
    "source": {
      "filters": {
        "workspaceIds": ["workspace_123"],
        "agentIds": ["support-agent"],
        "statuses": ["failed", "error"],
        "traceGroups": ["billing-support"],
        "limit": 250
      },
      "metadata": { "owner": "customer-success" }
    }
  }
}
```

Metadata is for labels and routing context. Do not put secrets in
`autoloop.source.metadata`.

## Autoloop a local trace directory

Use a local directory when you are testing the loop before a hosted source is
ready, or when traces arrive as files.

```bash theme={null}
npx archal autoloop ./prod-traces --repo . --execution-policy reproduce
```

Each file in `./prod-traces` should contain one top-level trace and enough child
span data for grading. The local loop writes artifacts under:

```text theme={null}
.archal/autoloop/
  autoloops.json
  runs.jsonl
  raw/
  grades/
  seeds/
  runs/
  fixes/
  failed/
  logs/
```

Stop a local file-backed autoloop with:

```bash theme={null}
npx archal autoloop detach ./prod-traces --repo .
```

`archal autoloop detach` is for local file-backed autoloops. Hosted database sources
are managed from the dashboard.

For a terminal-first handoff, keep these commands with the artifact directory:

```bash theme={null}
npx archal autoloop status --repo .
npx archal autoloop reprocess --repo . <trace-id>
```

Use `autoloop status` to summarize local trace jobs (or pass a hosted run id to
inspect one run's grade evidence and artifacts). Use `autoloop reprocess` only
after fixing the evidence, mapping, credential, harness, or GitHub blocker that
caused a terminal job.

## Dashboard pages

Autoloop results live in the workspace dashboard.

| Page                | Use it for                                                                  |
| ------------------- | --------------------------------------------------------------------------- |
| Imported traces     | Source health, trace import status, grade verdicts, and blocked reasons     |
| Reproduced failures | Seed evidence, reproduction status, clone parity, and run artifacts         |
| Opened issues/PRs   | GitHub issue links, PR links, branch names, check status, and residual risk |

The pages are intentionally split so an operator can answer three different
questions:

* Did Archal receive the trace?
* Did Archal reproduce a real failure against clones?
* Did Archal open something reviewable in GitHub?

## Artifact contract

Hosted sources surface these artifacts in the dashboard. Local file-backed
local autoloops write the same kinds of artifacts under `.archal/autoloop/`.

| Artifact        | Purpose                                                                            |
| --------------- | ---------------------------------------------------------------------------------- |
| `routing.json`  | What trace was imported and how it moved through the loop                          |
| `grade.json`    | Grade verdict, failure summary, evidence, and whether reproduction should continue |
| `scenario.md`   | Generated reproduction scenario for the trace                                      |
| `seed.json`     | Materialized seed metadata                                                         |
| `manifest.json` | Phase status, commands, attempts, evidence, and output paths                       |
| `stdout.json`   | Machine-readable output from the reproduction run                                  |
| `stderr.log`    | Reproduction command stderr                                                        |
| `status.json`   | Fix status when a run blocks before a patch or PR                                  |
| `pr-details.md` | Review summary for a generated fix                                                 |
| `repo.patch`    | Patch captured from the fix worker when a PR cannot be opened                      |

Artifacts should explain whether a failure was an agent issue, missing trace
evidence, clone fidelity gap, harness issue, or GitHub/CI issue. When evidence
is missing, Autoloop should say what is missing instead of manufacturing a
reproduction.

## Troubleshooting

### No traces import

Check:

* `archal login` or `ARCHAL_TOKEN`
* GitHub origin remote
* the read-only database URL
* the `autoloop.source.mapping` tables and columns in `.archal.json`
* the `autoloop.source.filters` block (`statuses`, `agentIds`, `workspaceIds`,
  `traceGroups`) is not filtering everything out
* whether `updated_at` or `created_at` values move past the stored cursor

Run the same `archal autoloop --source ... --database-url-env ...` invocation
with `--check` to validate setup without registering or starting work; the
mapping and filters are read from `.archal.json` either way.

### Hosted source registration fails

Common causes:

* no logged-in Archal account
* missing `ARCHAL_TOKEN` in CI
* `--database-url-env` points to an unset environment variable
* `--database-url-secret-ref` contains a plaintext credential
* repo remote is missing or is not GitHub

### Reproduction is blocked as missing evidence

That usually means the trace does not contain enough state to build the
starting clone state. Add one of:

* richer trace fields such as `input_data`, `output_data`, state snapshots, or
  tool-call arguments
* child spans for service reads and writes
* repo-owned seed templates under `archal/seeds/`
* a narrower `archal/scenario.md` that tells Archal what state matters

Blocked reproduction is a useful signal. It means Archal refused to claim a
failure was reproducible without enough evidence.

### A PR is not opened

Check:

* the execution policy is `fix`
* the GitHub App is installed on the repo
* the repo has an origin remote on GitHub
* reproduction status is `reproduced` or `flaky`
* generated changes pass local validation
* GitHub branch protection and CI status

Autoloop opens PRs for review. It does not auto-merge them.

## Safety

* Use read-only trace source credentials.
* Do not commit database URLs, API keys, or model keys.
* Prefer `--database-url-env` for local setup and secret references for hosted
  production setup.
* Keep trace artifacts redacted before sharing them outside the workspace.
* Do not add model-visible copy telling the tested agent it is in Archal or a
  clone-backed run.
* Let the GitHub App open PRs; do not bypass the review path with direct pushes
  to protected branches.

## What to hand off

For every loop, include:

* exact command and repo
* scenario paths or source name
* final status and stop reason
* artifact directory and the two or three files that justify the decision
* PR URL, if opened
* remaining blocker, if any

Do not add model-visible instructions telling the tested agent it is in Archal
or in a clone-backed run. That changes the behavior being evaluated.

## Next steps

* [CLI reference for `archal autoloop`](/cli/autoloop)
* [Run scenarios against your agent](/guides/run-with-agent)
* [Writing scenarios](/guides/writing-scenarios)
* [Seeds](/guides/seeds)
* [Authentication](/guides/authentication)
