Skip to content
Conduit

skill

agent-gauntlet

Uncle Bob-style agent pipeline with deterministic quality gates: specifier → coder → cleaner → hardener. Open agent skill (npx skills) + Claude Code plugin.

n0an/agent-gauntletcommunitylistedunreviewed

Set up

Skills are installed by placing their folder where your agent loads skills.

The download comes from github.com/n0an/agent-gauntlet itself, not from our server, so what you get is exactly what its author published.

Install instructions come from the README

Skills do not use an MCP config. Follow the steps in the README below.

README

synced Oct 28, 2026

⚙️ Don't stuff quality rules into prompts - wrap the agent in deterministic gates. Based on Robert C. Martin's (Uncle Bob) description of how he codes with agents (interview, 2026).

An agent skill that helps AI coding agents like Claude Code, Codex, Cursor, and Gemini take a user story through a staged pipeline - specify, code, clean, harden - where every stage runs in a fresh context and loops against a hard gate (per-function CRAP score, mutation score) until the numbers pass.

It uses the Agent Skills format, so it works smoothly with Claude Code, Codex, Gemini, Cursor, and more.

story ──▶ specifier ──▶ coder ──▶ cleaner ──▶ hardener ──▶ (qa: phase 2)
           Gherkin +     tests +    CRAP gate   mutation
           QA proc       impl       passes      score clean

The premise: agents treat prompt rules as vague suggestions and lose the middle of long instruction docs. Deterministic tools don't decay. So instead of steering with prose, each stage runs against a checker that exits non-zero, and loops until it exits zero.

The stages

StageWhat it does
specifierStory text to features/<slug>.feature (3-7 Given/When/Then scenarios, user-observable only, no implementation terms) plus features/<slug>.qa.md, a numbered manual QA procedure. Never writes code.
coderFeature file to unit tests plus implementation until green. May not edit the feature file; if the spec is unimplementable it stops and reports. No refactoring of surrounding code.
cleanerAdds tests or refactors until every changed function is under the CRAP threshold (complexity² × (1 − coverage)³ + complexity, default 6). Behavior is frozen: structure changes only, tests stay green, assertions are never weakened.
hardenerMutation testing scoped to the diff. Writes and strengthens tests until zero unjustified surviving mutants. Never weakens production code; every waiver carries a one-line justification.
qa (phase 2)Compiles the QA procedure into an executable UI check and runs it, with a screenshot per verification step and a PASS/FAIL verdict per scenario. Opt-in.

Stages run sequentially, each as a separate subagent or session in its own git worktree. Handoff artifacts are files, not chat: the feature file is the contract, the stage's commits are the payload, and a handoff file carries each stage's report to the next. A failing stage stops the pipeline. Nothing is merged or pushed and the developer's checkout is never touched: the run's only output is a branch, gauntlet/<slug>, that the human reviews.

The handoff protocol

Borrowed from Uncle Bob's swarm-forge and cut down to what a sequential single-story pipeline needs. scripts/gauntlet/gauntlet.sh is the whole runtime - bash and git, no daemon:

scripts/gauntlet/gauntlet.sh start auto-delete-30d --story story.md     # branch gauntlet/auto-delete-30d at HEAD, story queued
scripts/gauntlet/gauntlet.sh stage auto-delete-30d coder                # the stage runs this: worktree at the inbound commit, prints the task
scripts/gauntlet/gauntlet.sh handoff auto-delete-30d coder --gate "swift test"   # and this when done
scripts/gauntlet/gauntlet.sh status auto-delete-30d                     # where the run is, what to run next
scripts/gauntlet/gauntlet.sh finish auto-delete-30d                     # after review: drop the worktrees, keep the branch and the trail

Each stage checks out exactly the commit the previous stage handed it, works only there, commits with the byline By <stage>., writes a report, and hands off. handoff refuses uncommitted files, missing bylines, a coder that edited the spec, a missing report, and a failing gate. Hardener additionally requires current commit/base/scope-bound raw mutation-tool evidence with nonzero results and no unjustified survivors; neither prose nor GAUNTLET_REQUIRE_GATE can disable this check. Then it refuses once more on purpose: the first valid call prints AUDIT_REQUIRED with the stage's checklist, and only the unchanged second call queues the handoff. Agents say "done" too early; the second call is where they look again.

.gauntlet/runs/<slug>/            gitignored run state
├── handoffs/00_human_to_specifier.handoff … 04_hardener_to_human.handoff
├── gates/                        gate logs + retained mutation bundles under mutation/<digest>/
└── worktrees/<stage>/            one detached worktree per stage

Full contract, headers, exit codes and retry semantics: references/handoff-protocol.md.

What It Covers

  • The pipeline - the five stage contracts, what each stage may and may not touch, and why every stage gets a fresh context ("born, do the task, die") instead of one long session that accumulates trajectory contamination
  • The handoff protocol - one git worktree per stage, a handoff file per boundary, commits with a stage byline, a helper that prints the task and a handoff command that enforces the contract and demands one audit pass before queueing. The developer's checkout is never touched; the output is a reviewable branch
  • The deterministic gates - tests green (coder), a per-function CRAP score (cleaner), and a diff-scoped mutation score (hardener), each a script with a hard exit code rather than a sentence in a prompt
  • Agent-calibrated thresholds - Uncle Bob's values for agents rather than humans: CRAP 6 per function (he held humans to 4), a 70% module coverage floor as the coarse safety net, zero unjustified surviving mutants. Adjust the thresholds, keep the values, and don't impose human disciplines such as strict TDD choreography
  • The CRAP gate in any language - crap-gate.sh measures complexity with lizard (27 languages, real line spans, no compiler) and joins it to the coverage the repo already exports: llvm-cov export for Swift/SPM modules (never --summary-only, which silently reads as 0% everywhere), an Xcode .xcresult for iOS/macOS app projects where swift test cannot build the target at all, lcov for JS/TS/Python/gcov, Cobertura for JVM/.NET. The scorer is crap-check's crap.py, vendored with its fixture tests
  • Why per function, not per module - a module coverage floor lets an untested complex function hide behind well-covered neighbours; CRAP cubes the uncovered share of each function, so it cannot. The floor stays as a coarse net for files the tests never load
  • Swift extras - swiftlint-gauntlet.yml structure rules (body length, nesting; optional, complexity deliberately left to the scorer) and muter.conf.template.yml for mutation testing
  • Setup - where the scripts live in the target project, the tools to install, the env overrides (GAUNTLET_CRAP_THRESHOLD, GAUNTLET_COV_FLOOR, GAUNTLET_MODULES_DIR), and how to scope a mutation run to the story's diff so the inner loop stays affordable
  • The working philosophy - keep stories small; specs are throwaway scaffolding and code is the only source of truth; run the loop in fast host-testable modules, because test-loop speed decides whether the hardener is affordable at all

Installing

You can install this skill into Claude Code, Codex, Gemini, Cursor, and more by using npx:

npx skills add n0an/agent-gauntlet --skill agent-gauntlet

If you get the error npx: command not found, it means you don't currently have Node installed. You need to run this command to install Node through Homebrew:

brew install node

And if that fails it usually means you need to install Homebrew first.

When using npx, you can select exactly which agents you want to use during the installation. You can also select whether the skill should be installed just for one project, or whether it should be made available for all your projects.

Alternative install methods

Claude Code (installs the plugin: the /gauntlet command plus the five stage subagents):

/plugin marketplace add n0an/skills
/plugin install agent-gauntlet@n0an-skills

Gemini:

gemini extensions install https://github.com/n0an/agent-gauntlet.git --consent

Alternatively, you can clone this whole repository and install it however you want.

Using the Gauntlet

In Claude Code the whole pipeline is one command - hand it a story:

/gauntlet Add a setting to auto-delete items older than 30 days

Add --qa to run the optional phase-2 QA stage after the hardener:

/gauntlet Add a setting to auto-delete items older than 30 days --qa

In Codex you would use $agent-gauntlet. You can also trigger the skill using natural language:

Run this story through the gauntlet.

Set up deterministic quality gates for this module.

Outside Claude Code, follow skills/agent-gauntlet/SKILL.md: the stage prompts in skills/agent-gauntlet/stages/ work as standalone system prompts in any harness that can run staged sessions, the runtime is gauntlet.sh, and the gates are shell scripts plus Python helpers (Python 3.8+ for mutation evidence).

Before the first run, put the runtime and the gates in the target project and commit them (stages work in fresh worktrees, which only contain committed files):

# from the installed skill folder (or a clone of this repo)
cp -R agent-gauntlet/scripts <target-repo>/scripts/gauntlet
printf '.crap/\n.gauntlet/\n' >> <target-repo>/.gitignore   # gate venv + exports; run state + worktrees
brew install muter-mutation-testing/formulae/muter    # hardener adapter currently requires Muter v16
brew install swiftlint                                # optional Swift structure rules

cd <target-repo>
git add scripts/gauntlet .gitignore && git commit -m "Add the gauntlet"
scripts/gauntlet/crap-gate.sh <ModuleName>                       # Swift/SPM module
scripts/gauntlet/crap-gate.sh --lcov coverage/lcov.info src      # any other stack, after your test run
scripts/gauntlet/crap-gate.sh --xcresult .crap/cov.xcresult Sources  # iOS/macOS app project

Hardener capture and handoff require Python 3.8+ (stdlib only). In its clean, committed worktree, run python3 scripts/gauntlet/mutation-evidence.py run-muter --base <printed MUTATION_BASE> --module <module-path>, then hand off with regular tests. The wrapper captures Muter v16 raw JSON and provenance; handoff only validates/hashes stored evidence, never repeats expensive mutations. Mutation evidence contract covers scope, reviewed survivor waivers and retention. For non-Swift projects, run-report --base <MUTATION_BASE> --config <runner-policy.json> captures a real configured command producing Mutation Testing Report v1/v2 JSON; a StrykerJS runner and policy template are bundled. Scope and survivor policy are language-neutral, not a promise of built-in runners for every language. Other formats need adapters. No zero/not-run exception exists. Hashes establish consistency, not proof against malicious agents with write access.

lizard installs itself into .crap/venv on first run. The gate exits 1 with the offending functions listed, worst first:

   CRAP   CX     COV  LOCATION
  226.0!  37   48.3%  TextEnhancer.swift:61  AIKit.TextEnhancer.enhance(AIKit.EnhancementInput) async
   25.6!  17   69.0%  AIProviderRegistry.swift:50  AIKit.AIProviderRegistry.makeTextProvider(for:)

Why Use an Agent Skill for This?

Agents are good at making tests pass and bad at noticing that passing tests prove nothing. Told in a prompt to "keep complexity low and coverage high", they:

  • Write tests that execute the code without asserting anything meaningful, so coverage looks healthy while the suite would not notice a flipped operator
  • Follow the rules at the top of a long instruction doc and drop the ones in the middle, which is exactly where quality rules usually sit
  • Drift from refactoring into behavior changes, then adjust the tests to match the new behavior
  • Carry the previous task's failed attempts into the next one, so a messy implementation session poisons the cleanup session
  • Report "done, all tests pass" against a suite that grew weaker over the course of the session

This skill:

  • Replaces prose rules with exit codes - a per-function CRAP scorer and a mutation run decide when a stage is finished, and the agent loops until they agree
  • Splits the work into stages with hard boundaries - the coder may not touch the spec, the cleaner may not change behavior, the hardener may not weaken production code
  • Gives every stage a fresh context so nothing carries over except the files on disk
  • Proves the tests would catch a regression - coverage shows the tests ran the code, mutation testing shows they would fail if the code were wrong
  • Keeps the human in the loop where it matters - the run lands on its own branch, nothing is merged or pushed, and design findings, waived mutants, and spec ambiguities get surfaced rather than silently resolved
  • Makes "done" cost a second look - the handoff is refused once with a checklist before it is accepted, and it never accepts uncommitted work, a missing report, or a red gate

Repository layout

skills/agent-gauntlet/               the portable Agent Skills folder
├── SKILL.md                         the workflow: pipeline, stage contracts, gates, setup
├── stages/                          the five stage prompts, one copy each
│   ├── specifier.md  coder.md  cleaner.md  hardener.md  qa.md
├── references/handoff-protocol.md   the worktree + handoff contract: layout, headers, refusals, retry
├── scripts/                         the runtime and the gates, copied into the target repo as scripts/gauntlet/
│   ├── gauntlet.sh                      start / stage / handoff / status / finish: worktree per stage, handoff files, audit gate
│   ├── mutation-evidence.py             capture Muter or standard raw reports; fail-closed validation
│   ├── stryker-runner.mjs               StrykerJS runner for the standard-report adapter
│   ├── mutation-runner.template.json    committed source-scope/command policy example
│   ├── crap-gate.sh                     lizard + coverage export + scorer; SPM module, or --lcov/--cobertura/--xcresult
│   ├── crap.py                          the CRAP scorer, vendored from n0an/crap-check (pinned sha in its header)
│   ├── swiftlint-gauntlet.yml           optional Swift structure rules
│   └── muter.conf.template.yml          mutation testing config template
└── agents/openai.yaml               Codex interface metadata
commands/gauntlet.md                 the /gauntlet orchestrator command for Claude Code
tests/test_crap.py                   fixture tests for the scorer (lcov, Cobertura, llvm-cov)
tests/test_handoff.py                end-to-end tests for gauntlet.sh over a throwaway git repo
tests/test_mutation.py               adapter + mutation handoff tests (fake Muter, real git/worktrees)
tests/test_standard_mutation.py      language-neutral report and handoff regressions
scripts/sync-crap.sh                 maintainer tool: re-vendor crap.py + tests from crap-check main (--check for CI)
.claude-plugin/plugin.json           the plugin manifest

Everything the skill needs lives inside skills/agent-gauntlet/, so a standalone Agent Skills install is self-contained: SKILL.md addresses its files as stages/... and scripts/..., with no path reaching outside the skill folder.

The Claude Code plugin layer reuses those same files instead of copying them. commands/gauntlet.md provides the /gauntlet orchestrator, and the agents key in .claude-plugin/plugin.json lists the five files under skills/agent-gauntlet/stages/, so they load as subagents from the one place they exist. Editing a stage prompt updates the plugin and the portable skill at once.

Contributing

Contributions are welcome - whether adding gates for another stack, sharpening the stage prompts, or fixing typos.

  • Keep Markdown concise. There is a token cost to using skills, so respect the token budgets of users.
  • Do not repeat things LLMs already know. Focus on the gate mechanics, thresholds, and the failure modes the pipeline exists to catch.
  • Gates must stay deterministic: one script, one hard exit code, runnable in a loop.
  • gauntlet.sh stays Bash 3.2 + git, no daemon; mutation evidence uses Python 3.8+ stdlib. Every refusal needs a test. Run python3 -m unittest discover -s tests -v (handoff + mutation) and python3 tests/test_crap.py (scorer). Real tool adapters require official source/schema references; fixture tests are not real mutation runs.
  • Do not edit crap.py here. Its source of truth is crap-check; fix it there, then run scripts/sync-crap.sh to re-vendor. scripts/sync-crap.sh --check exits 1 on drift.
  • All work must be licensed under the MIT license.

License

Available under the MIT License, which permits commercial use, modification, distribution, and private use.

↑↓ navigate openesc close