{"slug": "show-hn-redteam-if-you-are-using-more-than-2-coding-agents", "title": "Show HN: Redteam:If you are using more than 2 coding agents", "summary": "A developer released Redteam, an open-source adversarial agent-pair harness that uses two different AI models to plan, implement, and review code changes, producing draft pull requests for human review. The tool aims to prevent automatic self-agreement by forcing independent model perspectives, and is designed for use with coding agents like Claude Code.", "body_md": "An adversarial **agent-pair** harness for shipping code with AI. One model\ndrives a task through a pipeline (plan → implement → review); a\n**different** model reviews the work adversarially; the output is a draft PR you\nreview before merge. The collision of two independent model perspectives is the\npoint — automatic self-agreement is what it exists to prevent. (A single-model\n**TDD** mode that front-loads `write_test → verify_test`\n\nis also available — see\n[Phases by mode](#phases-by-mode).)\n\nStatus: early. redteam was built as one project's internal harness and then extracted into this standalone repo, which owns it going forward — it has driven real, merged pull requests. (Its early git history reflects that origin, including cross-repo coordination from the parent project.) APIs and layout may still move.\n\n**Quick install (Claude Code) — two commands:**\n\n```\n/plugin marketplace add https://github.com/AscendyProject/redteam\n/plugin install redteam@ascendy-redteam\n```\n\nNot on Claude Code? Vendor it into any repo — see [Install](#install).\n\nGiven a batch of tasks (each a short `input.md`\n\nbrief), the orchestrator walks\nevery task through a fixed pipeline, persisting `state.json`\n\nafter each phase so\na run is fully resumable and retrying on `CHANGES_REQUESTED`\n\n:\n\n``` php\nflowchart TD\n    PO[plan_outcome]:::worker --> PRV[plan_review]:::rev\n    PRV --> IMPL[implement]:::worker\n    IMPL --> RC[review_code]:::rev\n    RC -->|APPROVED| CPR[create_pr → draft PR]:::worker\n    RC -->|CHANGES_REQUESTED| IMPL\n    RC -. blocker persists .-> RES[rescue]:::rev\n    RES --> HGR[human_gate_rescue] --> CPR\n    CPR --> DONE([done]):::done\n\n    classDef worker fill:#e3f2fd,stroke:#1976d2,color:#0d47a1;\n    classDef rev fill:#fce4ec,stroke:#c2185b,color:#880e4f;\n    classDef done fill:#e8f5e9,stroke:#388e3c,color:#1b5e20;\n```\n\nBlue = worker model (writes) · pink = reviewer model (adversarial, fresh).\n\nThis is the default **agent-pair** flow. By design it runs with **no human gates\nin the common path** — the adversarial pair plus verification *is* the trust, and\nthe output is a **draft PR** (your existing human checkpoint before merge), not an\nauto-merge. Human gates are something you **add back for risky changes**, not the\ndefault tax on every change — see [When to use it](#when-to-use-it).\n\n`mode`\n\n(`agent-pair`\n\nby default, or `tdd`\n\n) decides which phases run. The\nauthority is `_phase_order()`\n\nin `orchestrator.py`\n\n(`AGENT_PAIR_PHASE_ORDER`\n\n/\n`TDD_PHASE_ORDER`\n\n) — driving the pipeline manually must follow the row for the\ndeclared mode, not the prose:\n\n| Mode | Core phases |\n|---|---|\n`agent-pair` (default) |\n`plan_outcome → plan_review → implement → review_code → create_pr` |\n`tdd` |\n`plan_outcome → write_test → verify_test → implement → review_code → create_pr` |\n\nThe **agent-pair** worker writes its tests **inside implement** — there is no\nseparate test-authoring phase; the second perspective is the adversarial\n\n**reviewer**(\n\n`review_code`\n\n), and the plan is independently checked by\n`plan_review`\n\n. The **TDD** mode instead drops\n\n`plan_review`\n\nand front-loads a\n`write_test → verify_test`\n\npair before `implement`\n\n. So `write_test`\n\n/\n`verify_test`\n\n(the test-author / test-verifier sub-agents) run in **TDD mode only**— inserting them into an agent-pair task runs a phase the mode excludes.\n\n(The table shows the worker + reviewer phases. A `rescue`\n\nslot is entered only if\na blocker persists across review rounds — in the untiered default a rescue is then\nhuman-reviewed (`human_gate_rescue`\n\n) before the PR. A plan-approval gate is opt-in\nper [tier profile](#when-to-use-it).)\n\nEach phase is run by a focused sub-agent with its own prompt and tool scope\n(`.claude/agents/*.md`\n\n): an outcome-planner, implementer, code-security-reviewer,\nand pr-author — plus a test-author / test-verifier pair used **only in TDD mode**.\nThe reviewer is a *fresh* agent that only sees the diff and the project's\nsecurity checklist — it never sees the implementer's reasoning.\n\nA plain \"two-model\" setup stops at *a second model takes a second look*. redteam\nmakes that separation structural and then acts on it:\n\n**Findings are tiered, not pass/fail.** The reviewer emits findings with a severity (`blocker`\n\n/`major`\n\n/`minor`\n\n), and the orchestrator tracks each one*across review rounds*(a carry-over count) — a review is not a single thumbs up/down.**Persistent problems escalate on a ladder.** A blocker that survives multiple rounds climbs: retry the worker → a heavier`rescue`\n\npass → hand to a human (`ask_user`\n\n). So one rejection doesn't kill a run, and a stubborn real bug doesn't get rubber-stamped after a single retry.**The reviewer is blind to the writer.** It's a fresh agent — and a configurably*different*model — that sees only the diff and the security checklist, never the implementer's reasoning, so self-justification can't cross the boundary.**The draft PR is the human checkpoint; the default common path has no gates.** The pair plus verification is the automated trust, so the output is a**draft PR** you review before merge — it never autonomously merges. Blocking human gates (plan approval, etc.) are**opt-in per tier** for risky changes, not the default.**Either model on either side, zero runtime deps.** See[Model freedom](#model-freedom)and[Install](#install).\n\nRoles bind to providers through a small adapter registry, not hardcoded calls.\nToday Claude and Codex can each take **either** side:\n\n| role | providers implemented |\n|---|---|\n| worker (planner / implementer) | `claude` , `codex` |\n| reviewer / rescue | `codex` , `claude` |\n\nYou choose per role in `.redteam/config.toml [models]`\n\n. A reviewer value that\nisn't an adapter (e.g. `\"human\"`\n\n) falls back to the manual flow (you paste the\nreview and touch the sentinel). Adding another provider is one adapter file plus\none registry line.\n\nThe goal is **minimal human intervention, without losing trust** — the\nadversarial pair is the automated trust, so the common path has no human gates.\nBut not every change needs the same weight: a typo shouldn't pay for a full\nagent-pair, and an auth change shouldn't ship with only the light path. So you\n**scale the response to the risk of the change**:\n\n| change | response |\n|---|---|\n| trivial — non-behavior-changing (rename, comment, formatting) | single-agent, no review |\n| routine — small, local, reversible | single-agent loop; review optional |\nguarded — behavior change with real blast radius (auth, storage, concurrency, public API, migrations) |\nthe adversarial pair + verification (the default) |\nstrategic / production-critical — architectural, irreversible, or changes prod posture |\nthe pair plus human gates (and a rollback plan you require) |\n\n**Tier-aware routing** lets the harness apply this automatically (opt-in via\n`config.toml`\n\n). You define tier profiles as declarative toggles, and a\ndeterministic classifier picks each task's tier:\n\n```\n[tiers.0]                       # trivial\nreview = false                  # single-agent, no adversarial pair\nmodels = { implementer = \"claude-haiku-4-5\" }   # cheap model\n\n[tiers.2]                       # guarded (a sensible default)\nreview = true                   # the adversarial pair; no human gate\n\n[tiers.4]                       # production-critical\nreview = true\ngates = [\"outcome\", \"pr\"]       # add human checkpoints back here\n\n[tier_triggers]\n\"**/auth/**\" = 4                # touching auth floors the task at tier 4\ndefault = 2                     # unclassified → safe default\n```\n\nThe binding tier is `max(declared, path-triggered, default)`\n\n— a task can be\n**raised** but never lowered below what its paths demand, and an unclassified task\nfalls to the mandatory safe default. With no `[tiers]`\n\nsection, routing is off and\nevery task takes the default pipeline (fully backward-compatible).\n\nTwo levers also work on their own, without tiers:\n\n**Model per role**(`[models]`\n\n) — a cheaper implementer for routine work, a frontier reviewer for guarded work; either provider on either side.**The escalation ladder**— a`blocker`\n\nfinding that survives review rounds climbs retry →`rescue`\n\n, concentrating effort where a problem actually persists.\n\nTrigger globs are git-pathspec-style: `*`\n\nmatches within a path segment, `**`\n\nmatches across directories (so `**/auth/**`\n\nmatches `auth/x`\n\nat any depth).\n\nScope note: v1 path triggers match the paths a task\n\ndeclaresin its front-matter, and tier profiles vary review/gates/models over the canonical pipeline (not arbitrary phase orders). Re-checking the real committed diff and richer profiles are tracked on[issue #13].\n\nThis repo doubles as a single-plugin marketplace, so two commands install it:\n\n```\n/plugin marketplace add https://github.com/AscendyProject/redteam\n/plugin install redteam@ascendy-redteam\n```\n\nThe HTTPS URL works everywhere, including behind firewalls that block SSH (port 22). The\n\n`AscendyProject/redteam`\n\nshorthand also works if you have GitHub SSH keys configured.\n\nThat registers the six sub-agents and the `/redteam:*`\n\ncommands. Run\n`redteam-install`\n\n(also exposed as a `redteam-install`\n\ntool on PATH) from your\nproject root to vendor the harness in, then use the others as needed:\n\n```\n/redteam:redteam-install        # vendor .redteam/ into the current repo\n/redteam:redteam-new-task       # scaffold the next task-NNN dir + input.md from the template\n/redteam:redteam-review         # one-shot cross-model review of the current branch diff\n/redteam:redteam-config         # choose the per-role models (writer / reviewer / rescue)\n/redteam:redteam-status         # show the pipeline status for a batch\n# from a clone of this repo:\npython3 .redteam/scripts/install.py /path/to/your/project\n\n# preview first:\npython3 .redteam/scripts/install.py /path/to/your/project --dry-run\n```\n\nUseful flags: `--overwrite`\n\n(refresh harness-owned files; never touches your\n`config.toml`\n\n/ `docs/*`\n\n/ `batches/`\n\n), `--protect-config`\n\n(opt-in: add Claude\nCode `Edit/Write`\n\ndeny rules for `.redteam/config.toml`\n\nto the consumer's\n`.claude/settings.json`\n\n, add-only — the runtime pairing guard is the backstop\nregardless), and `--check`\n\n(report whether a vendored install is behind this\nharness version, then exit — writes nothing).\n\nEither way it's the same vendoring model: the harness ships *inside* your project\ntree (`.redteam/`\n\n) because the engine resolves your repo root from its own file\nlocation. Harness-owned files (`workflows/`\n\n, `prompts/`\n\n, `templates/`\n\n, agent\nskeletons) are re-vendored on each run (`--overwrite`\n\nto refresh); project-owned\nfiles (`config.toml`\n\n, `docs/*`\n\n, `verify.sh`\n\n, your `batches/`\n\n) are seeded once and\nnever overwritten.\n\nThe installer does **not** vendor the harness's own unit tests, so a consumer\nnever runs (or maintains) them — your `verify.sh`\n\nruns *your* tests, not the\nengine's. The vendored `.redteam/`\n\nengine follows the harness's own style, so\n**exclude .redteam/ from your project's linter/formatter** (e.g. ruff's\n\n`extend-exclude`\n\n, an eslint ignore) to avoid it flagging code you don't own.- Python 3.11+ (stdlib only — zero runtime pip dependencies).\n- The model CLIs you configure, installed and authenticated:\nand/or`claude`\n\n`codex`\n\n.\n\nA vendored install is a *copy* of the engine in your repo, so it doesn't update\nitself — you re-vendor when a new version ships. `--overwrite`\n\nrefreshes only\nharness-owned trees (`workflows/`\n\n, `prompts/`\n\n, `templates/`\n\n, `scripts/install.py`\n\n,\nthe six agent skeletons, and the `.redteam/.redteam-version`\n\nstamp); your existing\nproject-owned files (`config.toml`\n\n, `docs/*`\n\n, `verify.sh`\n\n) and your task content\nunder `batches/`\n\nare never overwritten (the installer only ensures an add-only\n`batches/.gitignore`\n\nrule there, leaving your files intact).\n\n`--check`\n\ncompares thesourceside against your vendored stamp, so it's only meaningful when the source is thenewerone — run it from an updated plugin (`redteam-install …`\n\n) or a fresh clone. Running your repo's own vendored`.redteam/scripts/install.py`\n\nagainst that same repo compares the stamp to itself, so it can't reveal an upstream release (it just echoes the vendored version, or`unknown`\n\nif the stamp is missing). Exit codes:`0`\n\ncurrent/ahead ·`1`\n\noutdated ·`2`\n\ncannot determine. It writes nothing.\n\nThe plugin ships the engine and puts `redteam-install`\n\non PATH, so updating is\ntwo layers — refresh the plugin first, then re-vendor the engine it carries:\n\n```\n/plugin marketplace update ascendy-redteam   # refresh the cached marketplace\n/plugin update redteam@ascendy-redteam       # update the plugin to the latest\n/plugin list                                 # confirm the new version\n/reload-plugins                              # apply updated commands/agents (no restart needed)\n```\n\nThen re-vendor the engine into your repo and confirm. Because `redteam-install`\n\nself-locates the *plugin's* (now-updated) source, its `--check`\n\nmeaningfully\ncompares that against your repo's vendored stamp:\n\n```\nredteam-install . --check        # plugin source vs your vendored stamp: 1 = outdated\nredteam-install . --overwrite    # re-vendor the new engine into .redteam/\nredteam-install . --check        # expect \"verdict: up-to-date.\"\nbash .redteam/scripts/verify.sh  # your gate still passes\n```\n\nPull the latest of this repo (your clone), then run the **clone's** installer\nagainst your project so the source side is the updated one:\n\n```\n# from your refreshed clone of this repo:\npython3 /path/to/redteam-clone/.redteam/scripts/install.py /path/to/your/project --check\npython3 /path/to/redteam-clone/.redteam/scripts/install.py /path/to/your/project --overwrite\n```\n\nDo the update on a branch and open a PR (don't push the engine bump straight to\nyour default branch), and keep `.redteam/`\n\nexcluded from your linter as in\n[Install](#install).\n\nEdit `.redteam/config.toml`\n\nfor your stack (paths, `verify_command`\n\n,\n`branch_prefix`\n\n, role→model), then fill the three project docs the sub-agents\nread:\n\n`.redteam/docs/project-context.md`\n\n— stack + hard rules`.redteam/docs/security-checklist.md`\n\n— the reviewer's hard lines`.redteam/docs/test-conventions.md`\n\n— how your test suite is wired\n\nTwo complete examples to copy the shape from: `examples/fastapi-like/`\n\n(Python —\nFastAPI + Celery + Postgres + a vector DB) and `examples/nuxt-like/`\n\n(JS/TS — Nuxt 3 +\nVue + Vitest).\n\n```\npython3 .redteam/workflows/orchestrator.py new    .redteam/batches/<batch> <slug> [--title \"...\"]\npython3 .redteam/workflows/orchestrator.py start  .redteam/batches/<batch>\npython3 .redteam/workflows/orchestrator.py resume .redteam/batches/<batch>\npython3 .redteam/workflows/orchestrator.py status .redteam/batches/<batch>\n```\n\nA batch is a directory of `tasks/<task-id>/input.md`\n\nbriefs. `new`\n\nscaffolds the\nnext `task-NNN`\n\ndirectory with a template `input.md`\n\n(or use\n`/redteam:redteam-new-task`\n\n); fill in the brief, then `start`\n\n. The orchestrator\ncreates a per-task branch (`<branch_prefix>/<task-id>`\n\n), runs the pipeline, and\nstops at each human gate until you touch the sentinel file it names.\n\n**One-shot review (no batch).** To run just the adversarial reviewer over your\ncurrent branch diff — a *different* provider than whoever wrote the code,\nread-only:\n\n```\npython3 .redteam/workflows/orchestrator.py review\n```\n\nIt reviews `git diff <base>...HEAD`\n\nand exits `0`\n\n/ `1`\n\n/ `2`\n\n(approved /\nchanges requested / reviewer failed), so it can gate CI. Exposed as\n`/redteam:redteam-review`\n\nin Claude Code. Fail-closed: it refuses if the\nconfigured reviewer would collapse to the worker's own provider (self-review).\n\nIssues and PRs welcome. See [CONTRIBUTING.md](/AscendyProject/redteam/blob/main/.github/CONTRIBUTING.md) for the\ndev setup and the gate (`bash .redteam/scripts/verify.sh`\n\n), and the\n[Code of Conduct](/AscendyProject/redteam/blob/main/.github/CODE_OF_CONDUCT.md). The engine stays\nproject-agnostic and stdlib-only — those two invariants drive most review\nfeedback. To report a vulnerability, see [SECURITY.md](/AscendyProject/redteam/blob/main/.github/SECURITY.md)\n(don't open a public issue).\n\nApache License 2.0 (`LICENSE`\n\n). Contributions are accepted under the\n[Contributor License Agreement](/AscendyProject/redteam/blob/main/CLA.md), which keeps provenance clean and\npreserves the option of offering the project under other terms.", "url": "https://wpnews.pro/news/show-hn-redteam-if-you-are-using-more-than-2-coding-agents", "canonical_source": "https://github.com/AscendyProject/redteam", "published_at": "2026-06-19 08:02:18+00:00", "updated_at": "2026-06-19 08:32:18.036902+00:00", "lang": "en", "topics": ["ai-agents", "developer-tools", "ai-safety", "ai-research"], "entities": ["Redteam", "Claude Code", "AscendyProject", "GitHub"], "alternates": {"html": "https://wpnews.pro/news/show-hn-redteam-if-you-are-using-more-than-2-coding-agents", "markdown": "https://wpnews.pro/news/show-hn-redteam-if-you-are-using-more-than-2-coding-agents.md", "text": "https://wpnews.pro/news/show-hn-redteam-if-you-are-using-more-than-2-coding-agents.txt", "jsonld": "https://wpnews.pro/news/show-hn-redteam-if-you-are-using-more-than-2-coding-agents.jsonld"}}