Metadata-Version: 2.4
Name: bounded-loops
Version: 0.1.0
Summary: Runnable, CLI-agent-portable reference library of bounded AI-agent loops.
License: Apache-2.0
License-File: AUTHORS.md
License-File: LICENSE
Requires-Python: >=3.11
Requires-Dist: jsonschema>=4.0
Requires-Dist: pyyaml>=6.0
Provides-Extra: dev
Requires-Dist: mypy>=1.10; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.5; extra == 'dev'
Requires-Dist: types-pyyaml; extra == 'dev'
Provides-Extra: mcp
Requires-Dist: mcp<2,>=1.27; extra == 'mcp'
Provides-Extra: otel
Requires-Dist: opentelemetry-api>=1.27; extra == 'otel'
Requires-Dist: opentelemetry-sdk>=1.27; extra == 'otel'
Provides-Extra: qualixar-gates
Description-Content-Type: text/markdown

<p align="center">
  <img src="https://raw.githubusercontent.com/qualixar/bounded-loops/main/assets/bounded-loops-logo.svg" alt="bounded-loops logo" width="170"/>
</p>

<h1 align="center">bounded-loops</h1>

<p align="center"><strong>Loop engineering you can actually run — every safety bound enforced in engine code, not described in a checklist.</strong></p>

<p align="center"><em>63 runnable AI-agent loops across a dozen industries — keyless, offline, and gate-verified.<br/>The runnable engine layer for the loop-engineering practice that Karpathy, Steinberger, Cherny, Osmani, and Berman defined.</em></p>

<p align="center">
  <a href="LICENSE"><img src="https://img.shields.io/badge/License-Apache_2.0-3b82f6?style=for-the-badge" alt="License: Apache-2.0"/></a>
  <img src="https://img.shields.io/badge/tests-619_passing-22c55e?style=for-the-badge" alt="619 tests passing"/>
  <img src="https://img.shields.io/badge/runnable_loops-63-e2725b?style=for-the-badge" alt="63 runnable loops"/>
  <img src="https://img.shields.io/badge/setup-keyless_%C2%B7_offline-0a0a0a?style=for-the-badge" alt="keyless and offline"/>
  <img src="https://img.shields.io/badge/safety-9_bounds_%2B_kill_switch-ff6b35?style=for-the-badge" alt="nine bounds plus kill switch"/>
</p>

---

Clone this, and in ~30 seconds — no API key, fully offline — watch an AI-agent
loop *wreck* without its gate (it confidently claims done while the bug is still
there), then watch the same loop *stop cleanly* with the gate enforcing it. Every
loop is a real runnable folder: a **runner** (the agent) proposes a change, an
**independent gate** (pytest, a JSON-Schema check, a command, a real security
scanner) verifies it, and a small engine repeats under **nine enforced bounds +
a kill switch** until the gate passes or a bound trips.

> "Loop engineering" is a practice the community named in 2026 — *"stop prompting
> your agent; design the loop that prompts it."* This repo doesn't claim the term;
> it ships the part others describe but don't run: the **runnable, enforced
> implementation** — **63 runnable loop folders across a dozen industries**, plus a
> wider [catalog of loop recipes](catalog/README.md). Full credits below.

## Inspired by the loop-engineering community — we build on it, not against it

bounded-loops didn't invent loop engineering. It's the runnable engine layer for
a practice that brilliant people defined first, and it exists to **complement
their work, not compete with it**.

- **Andrej Karpathy** ([@karpathy](https://x.com/karpathy)) framed the "loopy era" —
  the new skill is designing loops that keep useful work moving — and named the
  exact constraint this project is built around: *you can only safely automate a
  loop when its result is easy to evaluate.* bounded-loops turns that into code:
  the **gate** is that evaluable metric, and nothing is "done" until it passes.
- **Peter Steinberger** lit the spark: *"stop prompting your agents; design the
  loops that prompt them."*
- **Boris Cherny** (Claude Code, Anthropic): *"My job is to write loops."*
- **Addy Osmani** named the practice and gave it its anatomy in his
  [*Loop Engineering*](https://addyosmani.com/blog/loop-engineering/) essay.
- **Matthew Berman's [Loop Library](https://github.com/Forward-Future/loopy)** —
  the curated catalog of loops-as-prompts that we extend into runnable, gated folders.
- **proof-loop**, **repo-task-proof-loop**, and **agentops** pioneered the runnable
  fresh-verifier loop; bounded-loops adds the full nine-bound safety envelope and
  the by-industry library on top.

What bounded-loops adds to all of it: every loop is a **real folder that runs to
`✓ [DONE]` under nine enforced bounds** — across finance, legal, healthcare,
retail, operations, enterprise/ERP, security, testing, content, research, and
business, not just software. If we've mischaracterized or missed your loop
project, [open an issue](../../issues) — this space grows by building on each other.

## Three ways to use it

1. **As a CLI** — clone, `pip install -e .`, then `bl list` / `bl run loops/<name> --yes`. The raw engine; see [Quick start](#quick-start-keyless-30s) below.
2. **As an MCP server** — the `bounded-loops-mcp` command exposes `bl_run` / `bl_lint` / `bl_list` to any MCP client (Claude Code, Cursor, Codex, Antigravity…).
3. **As an agent plugin** — [`plugins/`](plugins/) ships a ready-to-install package for **Claude Code**, **Codex**, and **Antigravity**, each with a Skill, a `/bl-run` command, the MCP wiring, and a verify-on-stop hook. Inside your agent you get **gate-verified "done" instead of the agent claiming done**. See [MCP server and IDE plugins](#mcp-server-and-ide-plugins).

## Repository layout

```
bounded-loops/
├── bounded_loops/      the engine (Python package)
│   ├── domain/           pure rules + data types, no I/O — the ports (the seam) live here
│   ├── application/      the loop algorithm (run_loop.py) + bounds enforcer + manifest loader
│   ├── adapters/         concrete runners (stub, shell, claude-code, codex, …) and
│   │                     gates (command, pytest, jsonschema, osv, checkov)
│   ├── composition.py    the ONLY file that wires adapters onto the engine (composition root)
│   ├── cli.py            the `bl` command
│   ├── mcp_server.py     the `bounded-loops-mcp` server (bl_run / bl_lint / bl_list)
│   └── hooks/            the verify-on-stop hook
├── loops/              the 67 runnable loop folders — the library (each = seed + gate + cassette)
├── catalog/            the recipe catalog — a browsable menu of loop ideas across industries
├── docs/               how the engine works — ARCHITECTURE, NINE-BOUNDS, WRITING-A-LOOP (SVG diagrams)
├── plugins/            install packages for Claude Code / Codex / Antigravity (skills, commands, hooks)
├── tests/              the 619 tests
└── README.md · CONTRIBUTING.md · LICENSE · pyproject.toml
```

`docs/` explains **how the machine works**; `catalog/` lists **which loops you can run** — different jobs, not duplicates. Every other top-level entry (`.venv/`, `__pycache__/`, the `.*_cache/` dirs) is a local build artifact that `.gitignore` already excludes from the repo.

## Install

The engine is a **Python 3.11+** package. Install it whichever way fits your stack:

**pip — native, recommended**
```bash
pip install bounded-loops            # from PyPI
bl list
```
```bash
# …or from source, today:
git clone https://github.com/qualixar/bounded-loops
cd bounded-loops && pip install -e .
```

**npx — Node convenience wrapper** (still needs Python 3.11+ on your PATH)
```bash
npx bounded-loops list
npx bounded-loops run loops/bug-fix-red-green --yes
```
The npm package is a **thin launcher**: on first run it finds Python 3.11+,
installs the engine, then hands off to the real CLI. It does not reimplement the
tool in Node — Python is the engine, npm is just a convenient front door.

## Quick start (keyless, ~30s)

```bash
pip install -e .
bl run loops/bug-fix-red-green --yes    # keyless stub runner + real pytest gate
```
```
✓ [DONE] gate-passed (laps: 1)  ledger: loops/bug-fix-red-green/.ledger.jsonl
```

See the contrast that makes the point — the same loop, ungated, believing a lie:

```bash
cd loops/bug-fix-red-green && ./wreck.sh    # exits 1: "LIE CONFIRMED"
```

And browse every loop with its role, rung, and gate:

```bash
bl list
```

## Nine bounds (+ kill switch)

Every loop's `bounds.yaml` maps onto nine bounds, enforced across the engine's
layers — not nine flat booleans in one file:

| # | Bound | Enforced by |
|---|---|---|
| 1 | Iteration control — hard lap cap + no-progress stall detection | `bounds.max_iterations` / `bounds.no_progress_window`, in `run_loop.py` |
| 2 | Sandboxing — the agent operates on an isolated scratch **copy** of `seed/`, never the source dir; symlinks refused | `bounds.sandbox` + `composition._make_scratch_workspace` |
| 3 | Input quarantine — secret-bearing files (`.env*`, `.ssh`, `.aws`, `*.pem`/`*.key`, `id_rsa`, `credentials`, `.git`) are **excluded from the sandbox copy**, so a shared loop can't smuggle or exfiltrate credentials | `bounds.quarantine_inputs` (active in `_make_scratch_workspace`) |
| 4 | Output schema validation | `bounds.schema`, consumed by `JsonSchemaGate` when a loop opts in |
| 5 | Tracing — one OTel span per lap, or a no-op tracer when the `otel` extra isn't installed | `bounds.trace` + `TracerPort` |
| 6 | Regression evaluation — satisfied by the loop's **gate choice** (pytest / command / jsonschema / osv / checkov), not a `Bounds` field | the `GatePort` adapter a loop selects |
| 7 | Token budget — real token counts parsed from `claude-code`'s `usage`, accumulated and enforced by `BudgetMeter`. (`shell`/`codex`/`antigravity` report 0 tokens — an honest tool limitation, not a silent gap; `stub`/`python_callable` supply counts from the cassette/glue.) | `bounds.max_tokens` + `BudgetMeter` |
| 8 | Human approval gating | `bounds.require_approval` (derived from `rung` when unset: L1 → none, L2/L3 → required) |
| 9 | Wall-clock timeout — `max_wallclock_s: null` means "use the conservative 1-hour default", **not** "unbounded". It is an **inter-lap** ceiling (checked before each lap); the **in-lap** guard against a single long turn is the runner's and gate's own subprocess `timeout_s` | `bounds.max_wallclock_s` + `BudgetMeter`; runner/gate `timeout_s` |

On top of all nine, an env-var kill switch (`BOUNDED_LOOPS_KILL`) is polled once
per lap, before anything else — the highest-priority stop. And a frozen invariant
runs through all of it: **the engine never trusts the agent's own claim of
"done." Only the gate decides.** The runner's `agent_claimed_done` flag is
recorded but never substituted for a real gate pass.

## Runners and gates

Runners (what proposes a change each lap):

| Runner | What it does | Needs |
|---|---|---|
| `stub` | Replays a recorded cassette of agent turns — fully deterministic, zero external calls. The keyless demos (including the `osv`/`checkov` loops' quick path) use this: the cassette *simulates* the agent's fix so the loop runs offline. To exercise a real agent/scanner, install the tool and use `--runner` or a non-stub cassette | nothing (keyless) |
| `shell` | Pipes the loop's prompt to any CLI command over stdin | whatever CLI you point it at |
| `python_callable` | Calls a `run_turn(prompt, workspace) -> dict` in a spawn-isolated subprocess with a scrubbed env | a Python module implementing the contract |
| `claude-code` | `claude -p --output-format json --bare`; parses real `total_cost_usd` + `usage` tokens | the `claude` CLI **and `ANTHROPIC_API_KEY`/`apiKeyHelper`** — `--bare` never reads OAuth/keychain (confirmed against the real binary) |
| `codex` | `codex exec --json`, sandbox mode derived from rung | the `codex` CLI (JSON schema not yet smoke-tested against a real binary — see Known limitations) |
| `antigravity` | `agy -p`, approval policy derived from rung | the `agy` CLI |

Gates (what decides if a lap is done):

| Gate | Checks | Needs |
|---|---|---|
| `command` | Any command (tokenized, run without a shell — no `\|`/`&&` chaining); exit 0 = pass. Verifies by **exit code only** — for real output validation prefer a typed gate below. A gate needing shell features ships a wrapper script in its loop folder | whatever command you configure |
| `pytest` | `pytest -q` in the workspace | `pytest` |
| `jsonschema` | `workspace/output.json` against a JSON Schema (path from `bounds.schema`) | nothing beyond the core deps (keyless) |
| `osv` | `osv-scanner` reports zero known vulnerabilities (fails **closed** on an empty/garbage report) | [`osv-scanner`](https://github.com/google/osv-scanner) binary **+ network** (fetches the OSV.dev advisory DB) |
| `checkov` | `checkov` reports zero failed IaC checks (fails **closed** on uninterpretable output) | [`checkov`](https://www.checkov.io/) |

Additional gate adapters are pluggable via the composition root; the default
install ships only the universal, keyless-first set above — no gate in any
shipped loop's default manifest requires a paid product.

## Runnable loops (67 folders, across a dozen industries)

Every one is a real folder you run to DONE — a broken seed, a mechanical gate,
the nine bounds, an `L1`/`L2`/`L3` rung, and a `forbid:` guard so the agent
can't rewrite the gate to cheat. **63 of the 67 reach `✓ [DONE] gate-passed`
with zero setup** (keyless stub runner + a real gate); the 4 framework examples
need their framework installed. `bl list` prints them all — here's the spread:

| Industry / role | # | Examples |
|---|---|---|
| Software · backend · dev | 12 | `bug-fix-red-green`, `data-contract`, `openapi-schema-valid`, `dead-import-clean`, `type-annotations-present`, `json-config-schema` |
| Security · supply-chain | 7 | `secret-scan-keyless`, `dependency-pinning`, `dockerfile-no-root`, `cors-not-wildcard`, `jwt-alg-not-none`, `osv-scanner-example` |
| Finance · accounting | 6 | `ledger-reconciliation`, `invoice-3way-match`, `journal-entries-balance`, `iso20022-payment-valid`, `fx-rate-sanity` |
| Legal · compliance | 6 | `citation-existence-check`, `contract-clause-extraction`, `nda-required-clauses`, `gdpr-dpa-terms`, `contract-defined-terms` |
| Content · marketing | 6 | `content-fact-gate`, `frontmatter-schema`, `broken-internal-links`, `alt-text-present`, `reading-level-gate`, `seo-meta-limits` |
| Retail · commerce | 5 | `product-feed-schema`, `price-margin-floor`, `inventory-nonnegative`, `gtin-checkdigit`, `catalog-required-fields` |
| Operations · SRE | 5 | `runbook-completeness`, `oncall-coverage`, `slo-error-budget`, `conventional-commits`, `alert-runbook-link` |
| Enterprise · ERP/SAP | 5 | `idoc-xml-schema`, `transport-request-manifest`, `cds-view-annotations`, `bapi-payload-contract`, `material-master-completeness` |
| Business alignment | 5 | `okr-measurable`, `prd-acceptance-criteria`, `rfc-decision-recorded`, `roadmap-field-contract`, `meeting-action-items` |
| Testing · QA | 4 | `test-naming-contract`, `assertion-density`, `no-hardcoded-sleep`, `test-presence-per-module` |
| Research pipeline | 4 | `claim-source-mapping`, `dataset-license-allowed`, `reproducibility-manifest`, `bibliography-completeness` |
| Healthcare · frontend | 2 | `clinical-note-completeness`, `a11y` |

The flagship for "AI reliability" is [`citation-existence-check`](loops/citation-existence-check/):
a runnable form of the single most-documented AI failure in law — fabricated
case citations (1,600+ sanction cases catalogued by mid-2026) — where the gate
fails until every cited case resolves to a real one.

Every loop's own README shows the "prove the gate genuinely fails on the unfixed
seed, then passes on the fix" walkthrough with **actual captured output** — run,
not asserted. The wider [recipe catalog](catalog/README.md) lists more loop
recipes across these same industries (each naming a real gate) to wire up next.

On accessibility specifically: `axe-core`/Lighthouse need a running browser, and
no keyless *universal* a11y tool exists — so the `a11y` loop ships a small,
dependency-free static linter (missing `alt`, missing `lang`, unlabeled inputs).
It's honest about catching the static WCAG subset, not rendered-DOM issues.

## Scaffolding your own loop

```bash
bl new --list                    # pytest-basic, jest-basic, go-test-basic,
                                 # cargo-test-basic, rspec-basic, junit-basic
bl new pytest-basic my-loop      # <template> <destination> (positional)
```

## MCP server and IDE plugins

`bounded-loops-mcp` exposes `bl_run`/`bl_lint`/`bl_list` as MCP tools — a thin
shim over the same engine the CLI uses. `bl_run`'s `confirm` is a real,
server-side-enforced gate: it binds the full run identity (gate **+ runner +
iteration cap**), so a caller can't preview a safe `shell` runner and then
confirm a credentialed one; and a loop needing interactive approval is refused
before it's ever wired. `plugins/{claude-code,codex,antigravity}/` each ship the
MCP config + an `AGENTS.md` snippet + a verify-on-stop hook.

## Known limitations (stated plainly, not hidden)

- The `codex` runner's real-binary JSON schema is not yet smoke-tested (no
  `codex` install at build time); its token accounting reports 0 until verified.
- `langgraph-example`'s glue hard-codes `changed: True` — its no-progress bound
  can't fire for that example; a real integration should diff before/after.
- Two gates are **not** offline, and require network at check time:
  `content-fact-gate` (link liveness) and the `osv` gate (`osv-scanner` fetches
  the OSV.dev advisory database). The "~30-second, no-API-key, fully offline"
  quick-start refers to the default `bug-fix-red-green` loop specifically
  (stub runner + pytest gate), which needs neither network nor a key. The
  keyless-first set — stub/shell runners with pytest/jsonschema/command gates —
  is offline; the security-scanner gates (`osv`, `checkov`) bring their own
  binary and, for `osv`, a network fetch.
- `python_callable` always spawns and scrubs the child env to a small allowlist —
  by design, but your callable's import side effects run in a clean environment.

## Contributing

See [CONTRIBUTING.md](CONTRIBUTING.md) — `bl new` a template, replace the seed,
prove your gate genuinely fails then genuinely passes, and open a PR. The bar for
a catalog recipe or a loop: a **real** gate and a **testable** done-condition,
never "an LLM decides."

## Citation

bounded-loops is research-grade software from the **Qualixar AI Reliability
Engineering** initiative. If it's useful in your work, please cite it — GitHub's
"Cite this repository" button reads [`CITATION.cff`](CITATION.cff):

```bibtex
@software{bhardwaj_bounded_loops_2026,
  author    = {Bhardwaj, Varun Pratap},
  title     = {bounded-loops: runnable, bounded AI-agent loops},
  year      = {2026},
  publisher = {Qualixar},
  version   = {0.1.0},
  url       = {https://github.com/qualixar/bounded-loops}
}
```

## Security

Found a way to trick the gate or escape a bound? That's the most valuable bug we
can get — please report it privately per [SECURITY.md](SECURITY.md).

## License

[Apache-2.0](LICENSE). Copyright &copy; 2026 Varun Pratap Bhardwaj / Qualixar.

---

## Part of the Qualixar AI Reliability Engineering initiative

bounded-loops is one product in [Qualixar](https://qualixar.com)'s open-source
platform for making AI agents reliable:

| Product | Purpose | Install |
|---|---|---|
| **[SuperLocalMemory](https://github.com/qualixar/superlocalmemory)** | Local-first agent memory + learning | `npm install superlocalmemory` |
| **[Qualixar OS](https://github.com/qualixar/qualixar-os)** | Universal agent runtime | `npx qualixar-os` |
| **[SLM Mesh](https://github.com/qualixar/slm-mesh)** | P2P coordination across sessions | `npm i slm-mesh` |
| **[SLM MCP Hub](https://github.com/qualixar/slm-mcp-hub)** | Federate 430+ MCP tools | `pip install slm-mcp-hub` |
| **[AgentAssay](https://github.com/qualixar/agentassay)** | Token-efficient agent testing | `pip install agentassay` |
| **[AgentAssert](https://github.com/qualixar/agentassert-abc)** | Behavioral contracts + drift detection | `pip install agentassert` |
| **[SkillFortify](https://github.com/qualixar/skillfortify)** | Formal verification for agent skills | `pip install skillfortify` |
| **[Agent Amplifier](https://github.com/qualixar/agent-amplifier)** | Runtime amplification hooks | `pip install agent-amplifier` |
| **bounded-loops** *(this repo)* | Runnable, gate-verified agent loops | `pip install bounded-loops` |

**Local-first. Honest about what runs. Built to complement the ecosystem, not fence it off.**

Start here → **[qualixar.com](https://qualixar.com)** · Author: **[Varun Pratap Bhardwaj](https://qualixar.com)** — founder, Qualixar.

---

<p align="center">
  If bounded-loops saves you from an agent that lies about being done,<br/>
  <a href="https://github.com/qualixar/bounded-loops">⭐ <strong>star it on GitHub</strong></a> — it helps other developers find it.
</p>
