{"id":51451095,"url":"https://github.com/rxdt/py_ralph_frame","last_synced_at":"2026-07-11T19:00:29.919Z","repository":{"id":366755888,"uuid":"1277595908","full_name":"rxdt/py_ralph_frame","owner":"rxdt","description":"A lightweight spec-driven loop harness for Claude Code, Codex, and Gemini CLI. Fresh-context runs, specs, commits, and gates to get you and your agents productive immediately.","archived":false,"fork":false,"pushed_at":"2026-07-10T06:43:00.000Z","size":403,"stargazers_count":6,"open_issues_count":9,"forks_count":2,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-07-10T08:15:25.929Z","etag":null,"topics":["agent","agent-loop","agentic-ai","agentic-coding","ai-agents","automation","autonomous-agents","claude-code","codex-cli","coding-agents","developer-tools","framework","gemini-cli","harness","pair-programming","pypi","python","scaffold-template","template","uvx"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rxdt.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-06-23T03:05:34.000Z","updated_at":"2026-07-09T02:45:15.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/rxdt/py_ralph_frame","commit_stats":null,"previous_names":["rxdt/py_ralph_frame"],"tags_count":1,"template":true,"template_full_name":null,"purl":"pkg:github/rxdt/py_ralph_frame","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rxdt%2Fpy_ralph_frame","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rxdt%2Fpy_ralph_frame/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rxdt%2Fpy_ralph_frame/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rxdt%2Fpy_ralph_frame/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rxdt","download_url":"https://codeload.github.com/rxdt/py_ralph_frame/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rxdt%2Fpy_ralph_frame/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35372639,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-11T02:00:05.354Z","response_time":104,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["agent","agent-loop","agentic-ai","agentic-coding","ai-agents","automation","autonomous-agents","claude-code","codex-cli","coding-agents","developer-tools","framework","gemini-cli","harness","pair-programming","pypi","python","scaffold-template","template","uvx"],"created_at":"2026-07-05T21:00:25.530Z","updated_at":"2026-07-11T19:00:29.911Z","avatar_url":"https://github.com/rxdt.png","language":"Python","funding_links":[],"categories":["Tools","Agent Orchestration \u0026 CLI Tools"],"sub_categories":["Development Tools"],"readme":"\u003cdiv align=\"center\"\u003e\n\u003cimg src=\"banner.svg\" alt=\"Blue infinity loop\" width=\"360\"\u003e\n\n\u003ch1\u003eL∞PS: A Python Ralph Harness\u003c/h1\u003e\n\u003cp\u003eLoosely opinionated scaffold for a gated autonomous agent loop (\"Ralph\"). A dumb Ralph tells an agent to \"Go!\" and hands it a PROMPT. You set the tasks. No pre-defined, orchestrator. Agents loop on specs. Each iteration a worker commits with guardrails and updates its own specs.\u003c/p\u003e\n\n![Python](https://img.shields.io/badge/Python-3.11+-3776AB?logo=python\u0026logoColor=white)\n![Status](https://img.shields.io/badge/github-repo-blue?logo=github)\n[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat)](https://makeapullrequest.com)\n![GitHub activity](https://img.shields.io/github/commit-activity/m/rxdt/py_ralph_frame)\n![GitHub Release](https://img.shields.io/github/v/release/rxdt/py_ralph_frame?color=pink)\n![GitHub Repo Size](https://img.shields.io/github/repo-size/rxdt/py_ralph_frame)\n![X (formerly Twitter) Follow](https://img.shields.io/twitter/follow/roxdtvc)\n[![](https://img.shields.io/badge/code%20style-mine-999)](https://github.com/sebmestrallet/absurd-badges)\n[![](https://img.shields.io/badge/created%20an%20AGI%20by%20mistake-no-3C1)](https://github.com/sebmestrallet/absurd-badges)\n![Claude](https://img.shields.io/badge/Claude-D97757?style=for-the-badge\u0026logo=claude\u0026logoColor=white)\n\n\u003c/div\u003e\n\n---\n\n## TL;DR: Getting Started.\n\n1. Use as a project template or drop into your project\n2. `uv run harness install \u003cyour-project-name\u003e`\n3. Write your project goal in [docs/plan.md](docs/plan.md)\n4. `harness run \u003cagent=claude|codex|agy|copilot\u003e [max_iterations] [max_minutes]`\n5. Not what you wanted? Refine `plan.md` / `PROMPT.md` and re-run\n\n---\n\n## Details\n\n`PROMPT.md` tells each agent to pick a `spec` and build. `specs/` say _what_ to build. The agent decides _what next_. You keep `docs/plan.md` current, and specs get rewritten from it (agent is told in `PROMPT.md` to update the specs). Each iteration the agent updates its spec and `PROJECT_STATUS`. Ideas from [ghuntley](https://github.com/ghuntley), How to Ralph Wiggum.\n\n## Start a new project\n\nUse with new Python projects or drop `harness/` and dependencies into an existing project.\n\n1. From inside the checkout, run `harness install \u003cyour-project-name\u003e`. Names the project, installs dependencies, and sets up the git hook.\n2. Write your grand vision into `docs/plan.md`.\n3. Optionally add the first spec in `specs/`, or have an agent draft the first specs.\n4. Put product code under `src/` and list new source directories in `pyproject.toml [tool.coverage.run]`.\n5. Strict Ruff rules, type checking, pyright, complexipy, and pytest coverage are set in `pyproject.toml`.\n6. Your coding quirks go in `harness/preferences.py`.\n7. Run a loop:\n\n```sh\nharness run \u003cagent\u003e [max_iterations] [max_minutes]  # agent: claude/codex/agy/copilot. ralph adds prompt\n```\n\n![L∞PS Architecture Engine Flow](.loops.svg)\n\n## A L∞PS Loop\n\nThe repo is the only memory. Each iteration is a fresh-context agent.\n\n- `specs/` say WHAT to build\n- constant `PROMPT.md` tells the agent: read `specs/`, review `src/`, build the most important unfinished thing\n- agent builds\n- agent commits\n- every git commit passes the fast preflight (lint, format, plus loop containment for the agent)\n- every git push runs the full gate: lint, types, semgrep, tests, 100% coverage\n- the loop stops at `max_iterations`, a nonzero worker exit, or a timeout\n- Unspecified iterations/minutes → default to 2 iterations × 20 minutes each\n- **The harness is worker-agnostic.** Any agent CLI that reads a prompt from stdin and can edit/commit works.\n\n![L∞PS Agents](.loops_agents.svg)\n\n- There is NO worktree/branch creation by design. Agent duties can be contained to a part of the repo. e.g. Codex-1-frontend uses `specs/frontend.md`, Claude-2-researcher `specs/backend`...\n- Intentional:\n  1. For simplicity and maintainability of the framework.\n  2. Because a fresh iteration can't see the unmerged work in another worktree, so agents miss context and scramble to merge while conflicts pile up.\n  3. Change this behavior if you're comfortable with granting agents machine access, feeding context to agents, and managing rapidly moving git history.\n  4. You can create branches/trees and run a loop in each, then merge.\n- If you don't like _ANYTHING_ in this framework, remove it.\n\n## Safety\n\n`harness/ralph.sh` launches an autonomous LLM worker with the permissions you grant it (e.g.\n`--permission-mode acceptEdits`). The gate bounds what any **commit** may touch, but the worker itself is **not** sandboxed to this repo. Under a permissive mode it can run arbitrary shell. You are authorizing real changes. Choose the worker and permission mode deliberately. Use `git log --oneline \u003cbranch\u003e..HEAD` to show what's unpushed.\n\n#### The Gate: Tiered Checks\n\n`harness/gate.py` holds `FORBIDDEN_FILES`, `FORBIDDEN_DIRS` and `FORBIDDEN_PATTERNS`. `harness/preferences.py` holds human's style checks other tools can't catch. Containment runs when `RALPH_LOOP=1`, which `ralph.sh` sets on each run. `pyproject.toml` holds many rules. Humans own them (`harness/preferences.py` is part of `harness/`).\n\n⚡ `harness preflight` (pre-commit) → fast checks.\nRuff lint + check format for everyone, _plus_ **containment** for the agents. Self-heals by un-staging forbidden files.\n\n✅ `harness gate` (CI/PR pre-push). Local checks mirror CI → ruff lint + format report-only, pyright, pylint, semgrep, complexipy, hypothesis, pytest @ 100% cov.\n\nOnly humans can bypass triggered gates and commit by adding flag `--no-verify`.\n\n## Layout\n\n```\nharness/        the gate, loop (ralph.sh), CLI, custom user checks   (🤖 forbidden)\n  preferences.py  user-defined preferences not covered by tools      (🤖 forbidden)\n  gate.py         mirror the CI locally + preferences.py honored     (🤖 forbidden)\n  tests/          the harness's own tests                            (🤖 forbidden)\n    test_properties.py  hypothesis tests                             (🤖 forbidden)\n.githooks/      pre-commit / pre-push gate hooks                     (🤖 forbidden)\n.github/        CI that re-runs the gate                             (🤖 forbidden)\npyproject.toml  project + tooling config                             (🤖 forbidden)\nAGENTS.md       rules for agents working in the repo                 (🤖 forbidden)\nPROMPT.md       the standing per-iteration instruction               (human maintained)\ndocs/           PLAN, PROJECT_STATUS                                 (human maintained plan.md)\nscratchpad/     scratch dir agents can use for temp files            (For the 🤖 to play)\nspecs/          WHAT to build, one PRIORITY-bannered file per track\nsrc/            your product/source code (add to coverage source)\n```\n\nIf an agent edits a forbidden file, the file will be unstaged (not allowed to commit). A forbidden pattern by an agent (e.g. `# noqa` will also prevent their commit and force them to fix it.)\n\n## ⚠️ Warnings. Read this before a first run.\n\n1. **This harness does not sandbox agents.** It _tries_ to harness bad code in loops via gates. Sandboxing agents will, e.g. prevent them from maintaining git, running Playwright, being seen as trustworthy by semgrep leading to cyclical failures, etc.\n\n2. **The gate is a guardrail, not a jail.** Agents are crafty, like people. They will find a way to complete a task at all costs. **Trust nothing and no one.**\n\n3. **Mind your usage limits.** `ralph.sh` works agents to the cap set. You can easily burn through your tokens, context windows, and provider usage limits. **Workers continue running as long as there is work to do.**\n\n4. **`PROMPT.md` tells the worker to push every iteration** Protect `main` and run the loop on its own branch.\n\n5. **100% coverage does not mean good tests.** That is quantity, not quality. (Upcoming feature: mutation testing)\n\n## Commands\n\nTool commands are defined in [harness/gate.py](harness/gate.py#L62-L94).\n\n```sh\nharness install \u003cyour-project-name\u003e  # rewrite [project] name, uv sync, set core.hooksPath to .githooks\nharness preflight  # fast checks: preferences, ruff lint + format (plus loop containment)\nharness gate  # full pass: preferences, ruff, format, pyright, pylint, complexipy, semgrep, pytest @ 100% cov, hypothesis\nRALPH_LOOP=1 harness gate  # to run as if you are the agent in the loop\nharness run \u003cagent\u003e [max_iterations] [max_minutes] [verbose] # claude/codex/agy/copilot, defaults: 2 20 True\n\n# UNDERLYING TOOLS are run the way CI runs them\nruff check . \u0026\u0026 ruff format --check .  # lint. --check verifies formatting only, no auto-fix\npyright  # static type checker: flags type errors without running code\npylint harness src  # deeper lint: dead code, bad patterns, style beyond ruff\nsemgrep scan --config auto --config p/secrets --exclude-rule yaml.github-actions.security.github-actions-mutable-action-tag.github-actions-mutable-action-tag .  # SAST. exclude-rule allows the template's floating action tags\npytest --cov --cov-report=term-missing --cov-fail-under=100  # Note: Pydantic is included. Use it.\n\n# UNDERLYING AGENT CALLS (presets defined in AGENTS object at harness/cli.py:30)\nharness/ralph.sh 10 20 claude -p --permission-mode acceptEdits --no-session-persistence --output-format stream-json --verbose\n\nharness/ralph.sh 2 20 env -u CODEX_THREAD_ID -u CODEX_CONVERSATION_ID -u CODEX_SESSION_ID codex exec -m gpt-5.5 --json --sandbox danger-full-access -\n\nharness/ralph.sh 3 10 agy --log-file agy.log --print --dangerously-skip-permissions\n\nharness/ralph.sh 2 20 sh -c 'copilot --output-format json --stream on --allow-all-tools -p \"$(cat)\"'\n```\n\n## Expanding your harness\n\n- Edit rules at [pyproject.toml](pyproject.toml) for [ruff](https://docs.astral.sh/ruff/), [pylint](https://pypi.org/project/pylint/), [pydoclint](https://pypi.org/project/pydoclint/0.9.1/), [pyright](https://github.com/microsoft/pyright), [pytest](https://docs.pytest.org/en/stable/), [hypothesis](https://hypothesis.readthedocs.io/), [complexipy](https://github.com/rohaquinlop/complexipy)\n- Add forbidden files, directories, or patterns in [gate.py](harness/gate.py)\n- Add Hypothesis tests in any test directory, examples at [test_properties.py](harness/tests/test_properties.py)\n- [semgrep](https://docs.semgrep.dev/semgrep-ci/sample-ci-configs) has no repo config here. It uses registry configs plus Semgrep's built-in defaults which ignore tests.\n- Edit checks in [gate.py](harness/gate.py) and [ci.yml](.github/workflows/ci.yml)\n- Removing existing preferences or add your own preferences at [preferences.py](harness/preferences.py). Current preferences:\n\n```py\nfunction_argument_assignment_has_star  # agents use non-specific `def fun(*)`\nfunction_argument_assignment_underscore_lead  # agents love over-using underscore names `def _fun()`\nhidden_signature_star_args  # Complain when a function uses *args or **kwargs (it hides function signatures)\ndynamic_star_call  # Calls to def fun(*items) breaks when you can't tell how many arguments f is getting\npointless_class  # ensure classes are added for good reasons (carry state, values, methods)\nlazy_assert  # enforce real assertions, stronger tests\nobjects_injected_into_runtime_memory  # finds calls that manipulate global state (dangerous, tricky)\nlambda_found  # abolish lambdas for agents to keep their code simpler\nlazy_any_type_hints  # abolish type `Any` used to bypass strict type-checking\nchaotic_continue_statements  # abolish unecessary nested continue statements, clean code\ncomplex_comprehension  # no needlessly dense list/set/dict comprehensions, prefer linear code\n```\n\n![diagram](.diagram.png)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frxdt%2Fpy_ralph_frame","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frxdt%2Fpy_ralph_frame","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frxdt%2Fpy_ralph_frame/lists"}