# Loops are the primitive

> Observability gave us spans, metrics, and dashboards — primitives that end at a human. LoopOps starts where they stop: the improvement loop itself, with identity, lifecycle, evidence, and a termination condition.

July 3, 2026 · loopops.dev/blog/loops-are-the-primitive

Every telemetry product is built on the same primitives: spans, traces, metrics, logs. Two decades of tooling made those primitives very good at answering one question — *what happened?* Dashboards, alerts, and trace explorers are all projections of the same idea: collect signals, show them to a human.

But the question a product team actually cares about is different: **did we get better?** And there is no primitive for that.

## The loop everyone runs, and nobody stores

Every serious team already runs an improvement loop. Something fails in production, someone notices, someone diagnoses, a fix ships, and — sometimes — someone checks whether it worked. The loop is real. It's just not represented anywhere.

It lives in heads, Slack threads, and ticket queues. It has no identity, no state, no API. And implicit loops fail in predictable ways:

- Fixes ship and nobody re-reads the next window, so "resolved" means "merged", not "better".
- The same failure cluster gets rediscovered and rediagnosed from scratch, because closing the tab is the only way a dashboard investigation ends.
- "Did that PR actually help?" gets answered by vibes, or by whoever happened to be watching the graph that day.

Observability tools can't fix this, because their data model ends at the human. A span can tell you a tool call timed out nine times. Nothing in the model can tell you that this cluster was diagnosed last Tuesday, that PR #142 claims to fix it, and that the next window will decide whether it did.

## What "first-class" actually means

Programming languages have a precise meaning for first-class: a thing is first-class when you can name it, pass it around, store it, and inspect it — when it's a value, not a convention.

Apply that to improvement loops and you get a concrete checklist. In LoopOps:

- **Identity.** Every improvement cycle is a `run_id`. Not a dashboard session, not a ticket that drifts away from the telemetry — a key.
- **Lifecycle.** The cycle's stages are recorded as events in the same stream as the rest of your telemetry: `loop.diagnosed → loop.issue_opened → loop.pr_opened → loop.merged → loop.verified`. Each stage is idempotent per run, so recording is safe to retry.
- **Queryable state.** `GET /v1/loops` folds those events into runs: the failure cluster each run targets, how far it got, the issue and PR it produced, before/after failure rates. "Is the loop closing?" is a query, not a retrospective.
- **Evidence.** A loop starts from a bounded telemetry window, and its improvement candidates carry the evidence ids and traces they were derived from. A hypothesis with no evidence can't enter the loop.
- **A termination condition.** This is the part most workflows skip. A loop is not closed when the PR merges — it closes when the next telemetry window proves the metric moved. `POST /v1/runs/verify` compares failure rates across the fix boundary, window over window, and it refuses to record a verdict when there's no data. "Observe more" is a valid outcome; a fabricated green checkmark is not.

## What falls out when the loop is data

Once the loop is a value instead of a convention, useful properties stop being discipline and start being structure.

**Nothing resurfaces silently.** The batch cycle claims every unprocessed trace since the last close — the claim is the window. There is no gap between windows for a recurring failure to hide in, and pain that was already fixed and verified doesn't come back as a fresh discovery.

**Agents can operate the loop.** Because a loop is an addressable object with an API, a coding agent can open one, read the evidence, draft the eval and the patch plan, record each stage, and stop at the approval boundary. This is the part dashboards structurally can't do: a human-first surface makes the human the parsing layer for every trace; a loop-first surface makes the human the approver of every write.

**Humans keep the write side.** The approval gate isn't a setting bolted onto the loop — it's part of the primitive. Nothing posts an issue, opens a PR, or touches production until a human says so. First-class does not mean autonomous.

**The dashboard becomes a projection.** The console's Loop tab is honest about what it is: a view over `loop.*` events, the same rows any agent reads over the API. When the loop is the primitive, every surface — console, report, MCP tool — is just a lens on it.

## "But everyone is building self-improving agents"

They are, and that's worth being honest about. Detect-diagnose-fix features are appearing inside the big agent platforms, and they validate the thesis: telemetry should end in improvement, not in a chart.

The difference is representation and ownership. In a platform, the loop is a feature — a background job and a UI workflow that works best when you build on that platform's framework and trace to its store. The loop's state belongs to the platform. In LoopOps, the loop is a data primitive on top of open telemetry: `loop.*` events any stack can emit over OTLP, runs any agent can read over MCP or plain HTTP. Your improvement history is portable evidence, not a vendor's UI state.

That's the claim behind "loops as first-class primitives." Not that we invented improvement — every good team runs the loop already. It's that the loop deserves the same treatment spans got twenty years ago: a name, a schema, a lifecycle, and an API of its own.

## Where this goes

Primitives compound. Once loops are data:

- Loop history becomes a track record — which clusters keep reopening, which fixes actually held, what your real fix rate is.
- Evals attach to loops, so "prove the fix before you ship it" has a place to live.
- Scheduled scans are just loop-openers; the interesting state is in the runs, not the scanner.
- Window-over-window verification becomes the default definition of done for production behavior.

LoopOps is in alpha, and the loop primitive is live today: `POST /v1/loops/events` records stages, `GET /v1/loops` reads runs, `POST /v1/runs/verify` closes them honestly. Point it at your traces and open your first loop — the [quickstart](/quickstart) takes about five minutes.
