A fairlead is the fitting on a boat that keeps a line running true, so it
doesn't chafe, tangle or pull off course. Fairlead does that for an agent
working in your repository. It is one Rust binary with no project-specific
logic: everything about your repository lives in your own fairlead.toml.
Why Fairlead
Join a good team and you don't start from zero. Someone tells you which module never to touch without running the migration check first, which test lies on Windows, and which "quick fix" broke production last spring. That knowledge is why a new engineer is useful in week two instead of month six.
A coding agent gets none of it. It opens your repository cold every time, reads the same code again, makes the mistake your team already paid for, and finds out in CI that it was an old lesson nobody told it. Fairlead gives every repository its own memory, so your agent starts where your team left off instead of trying things like a stranger.
- A memory for every repository. The hard lessons from past work live with
the code, per module, where the agent meets them before it acts. A lesson is
kept short, reviewed on a date, and turned into a check when it can be, so
the memory stays true instead of growing into noise.
Since 0.2.0: owner rules record which tests guard which code, and a
quarantined test applies only while the evidence holds and until its date.
Since 0.7.0: a
[[quarantine]]entry records a test that lies on one platform, such as a CRLF checkout or a path with a space. There, the plan lists it as not provable here, anddoneexcuses only the failure it expects. - Set up once, never start cold. The agent's first job is to learn
the repository: its modules, test runners, rules, and the commands that prove
a change is right. That goes into one checked config, so every session starts
where the last one left off.
Since 0.1.1:
fairlead config checkvalidates every layer, andshow --originsays where each value came from. - The right tool, not the nearest one. The agent asks what applies to the
files in front of it and gets the rule, the command and the next step for
exactly those files.
Since 0.2.0:
fairlead plannames the tests and checks a change can reach, andtest --explainsays why each one is in or out. Since 0.13.0:plan --head REVplans a past commit without checking it out. - Plans the change, not just the tests. Before an edit, the agent sees
what it's about to touch, everything that depends on it, the rules and
lessons recorded there, and what has to pass before it's done. Afterwards,
what actually changed is checked against that brief.
Since 0.6.0:
fairlead brief <paths>answers that before the first edit,fairlead doneis the gate a change passes before it's finished,fairlead receiptcompares what changed with the brief, andfairlead nextnames the one step left. Since 0.13.0: a brief says when it carries paths from earlier briefs of the session, andbrief --freshstarts the set over. - The right skills for the code in front of it. A frontend change
shouldn't come with database advice. Fairlead picks the skills that apply to
what a change reaches, just as it picks tests, and checks the agent used them.
Since 0.8.0:
[[skills.routes]]scopes each skill, the brief lists the ones a change reaches,skills syncwrites them where each agent loads them, andskills reportmeasures whether the agent used them. - Measured, not guessed. Good and bad are numbers: whether the plan would
have caught real CI failures, rework, escaped defects, tokens and cost.
Since 0.2.0:
fairlead replayre-plans real failures from a repository's CI history, and the benchmarks measure that recall every week. Since 0.6.0:ci run --judgenames each failing test on the default branch that the merged change's plan left out. Since 0.13.0: when the config being replayed differs from the base branch's,replay runreports the recall of both side by side and names each failure that moved between hit and miss. Since 0.18.0:fairlead ci ledgersays whether each red check is the change's own, inherited from the base branch ("red on main since abc1234"), flaky or the platform's, with every workflow's red streak on the default branch;next,resume,briefanddoctorread it offline, andinsights --speedsays where CI time goes: slow steps, steps that never fail, suites faster than their plan, reruns and cancellations. - Fast because it remembers. No rereading the codebase to rediscover what was learned last week. The graph, the plan and the lessons are already there. Since 0.2.0: the import graph is built without installing dependencies and cached between runs.
- It learns where it's blind. When a change reaches no test, Fairlead says
so and points at the rule that's missing, so the team's knowledge grows
exactly where it was thin.
Since 0.3.0:
fairlead planlists every file no test depends on, and replay suggests the owner rule or check path that would have caught a miss. - Stopped before the mistake, not after. Your rules run as the agent
works, catching a wrong move in seconds instead of minutes later in CI.
Since 0.4.0:
[guard.*]rules run in a Claude Code hook before each edit and in a git commit hook, andfairlead guard checkruns them on the tree. Since 0.7.0: the same hooks for Codex and Gemini CLI, withfairlead hooks install --codexor--gemini.
Works with
- Any test runner. Vitest, Jest, Playwright,
node --test, Bun, pytest,go test, PHPUnit and Pest: a runner is one command in your config, so Fairlead never needs a plugin for your stack. - Monorepos, precisely. pnpm, npm, yarn and bun workspaces. A pnpm lockfile
change runs only the packages whose dependencies actually changed.
Since 0.8.0: in a bun project, a dependency's version bump runs only the
tests of the files that import it, and nothing when nothing does.
Since 0.13.0: a
plan.run_allentry can name the kinds of change that run everything, so a new migration goes through the graph and an edited one runs everything. Since 0.14.0: a named import from a barrel depends on the file that declares the name, so a change behind a package'sindex.tsselects only the tests that import what it declares. - No install needed to read your code. The import graph comes from source
alone, with tsconfig paths,
go.modand composer's autoload resolved and parsed files cached, so a plan takes a fraction of a second. - Your CI, not a new one. A GitHub Action and
ci plan --format githubfor Actions, and a JSON plan with a published schema for any other CI. - Proven on real projects. Recall is replayed from the CI history of Effect, pnpm and vitest every week, and published.
- Private by default. The CLI sends nothing you didn't ask for: no telemetry, and it never phones home.
- One binary, everywhere. Linux (glibc or musl, as on Alpine), macOS and Windows, installed with a shell script, PowerShell or npm.
What it reads, and what replay has proven. A language counts as proven when replaying a public project's CI history judges at least 10 failures, at high recall:
| Language | Since | Replay evidence |
|---|---|---|
| JavaScript, TypeScript | 0.2.0 | Effect, pnpm and vitest, replayed weekly (benchmarks) |
| Python | 0.5.0 | poetry: 99.2% of 122 failures |
| PHP | 0.5.0 | pterodactyl/panel and invoiceninja: 100% of 27 failures |
| Go | 0.5.0 | Reads, not yet proven: opentelemetry-go has too few failures to judge |
| Vue, Svelte, Astro components | 0.5.0 | Reads, not yet proven: no replay yet |
| Java, Kotlin (Maven, Gradle) | 0.7.0 | Reads, not yet proven: okhttp at 92.6%, most misses look flaky (#184) |
An external provider feeds the graph for any other language, and a coverage map adds what imports can't show. Framework packs and folders of several repositories are on the way (#47).
What it does
This README documents Fairlead 0.18.0. Each command, by what it's for:
- The test plan:
plan,test --explain, the import graph (graph) and coverage maps (coverage import). Since 0.13.0:graph hubslists the files that link the most changes to the most tests and the barrier that would cut the top one, and a changed barrier file selects its importers' tests. - Plans in CI:
ci plan(by stage, with--since-green),ci run(with--results,--judge,--onlyand--except),ci report,ci workflow, which writes a staged workflow, and the GitHub Action. Since 0.13.0:ci plan --shards Nsplits a plan across a matrix of N jobs, balanced by past durations, andci run --shard K/Nruns one job's share. Since 0.15.0: a slow suite withall_from = "full", or every suite while the project'sphaseisearlyortesting, runs only what a change reaches on a pull request and runs whole on a schedule. Since 0.18.0:ci ledgerjudges each failure of a run as own, inherited, flaky or infra,ci report --ledgersays it once, and the workflowci workflowwrites runs it when a job fails. - Rules as the agent works:
guard check, the hookshooks installadds, anddoctor. Since 0.18.0: a commit may not add a file named like a secret (.env,*.jks,*.pemand the like) with no config at all; gitleaks runs as a guard preset; migration numbers are checked against the base fetched at a push (guard check --push); andpr checkholds a change to[pr]rules (branch name, ticket key, base, labels, body headings, required files) indone, before a push and in CI. A foreign linter withdiff = truefails only on the findings a change adds, against the base in a temporary worktree, andpr checkfails a body that says "verified" or "tests pass" with no passing receipt for the head's tree. - The change loop:
briefbefore an edit, orcontextfor the brief with the lessons, skills, README and history to read;donebefore it's finished, thenreceiptandnext;resumepicks up where the last session on a branch stopped. Since 0.18.0:doneplans the branch from its merge base with[pr] baseor the default branch, runs first the cheap steps CI history shows going red (and adds a cheap check the plan left out, saying why), and runs as the pre-push hook (done --pre-push); the receipt says where each step ran, by stage and platform, what never ran on this tree and what can't be proved here ("unverifiable here: needs macos"), andreceipt --prputs it in the pull request body. - Project memory:
learnwrites a lesson,import lessonsturns a lessons document the team already keeps into one lesson per heading, andlessonslists, reviews and checks them. The brief offers the ones a change reaches. - Skills:
[[skills.routes]]scopes each SKILL.md, and the brief lists the skills a change reaches.import rulesturns path-scoped rule files into skills and routes, and refreshes a skill when its rule changes, andskills syncwrites each skill where Claude Code, Codex and Cursor load it. - Finding things:
findsearches lessons, skills, docs headings and declared names, nearest the brief first. - Agents over MCP:
mcpserves the plan, the brief, the receipt and the other read-only commands as tools. - What agents read first:
agents synckeeps the loop, the always-on lessons and the skill index in a marked block in AGENTS.md and CLAUDE.md. - Measuring:
insights, what the event log and CI's reports say about the setup and the config changes they back;tokens, the tokens and cost of each agent session;replay, the benchmarks,skills eval, which scores skill routing on the repository's history, andskills report, the hit rate of what routing offers against what agents load. Since 0.14.0:test benchtimesbun testserial,--paralleland--parallel --no-isolateon the repository's tests, flags order-dependent ones, and recommends the fastest mode fordoctor --scoreandinsights --suggest. Since 0.18.0:ci ledger, a verdict for each red check and the default branch's red streaks, andinsights --speed. - Getting started:
initwrites a first config from what the repository shows, andinit --blueprintadds a starting point for a kind of project: config, starter skills and the CI workflow. Since 0.14.0: on Bun 1.4.4 or later,initrunsbun testin parallel (--parallel --no-isolate) and offersbun fmt --checkon the changed files as a check. Since 0.18.0:init --profile guardsets up only the guard, for any stack, including those the test planner doesn't read, such as Laravel, Rails, Flutter or Swift. - The config:
config check,showandschema. - Upgrading:
migrate, which brings the hooks, the config's version floor and the version pins to the release you installed. Since 0.7.0.
The quick start shows the first few, and the book covers each one.
Status
Pre-alpha. The latest release, v0.18.0, folds what was planned as 0.16
and 0.17 into one release. Guards work on any stack from day one
(init --profile guard), secret file names are refused at commit with no
config, gitleaks runs as a preset, and [pr] rules (branch, title, ticket,
body, base, labels, files) run in done, at push and in CI with
fairlead pr check. fairlead ci ledger answers "is this red mine?" (own,
inherited, flaky or infra) and insights --speed says where test and CI
time goes. fairlead done plans from the merge base, runs first what CI
history shows going red, records each step's stage and platform, and a
foreign linter fails only on what a change adds. 0.15.2 made done read
[[needs]] and fixed the Stop hook; 0.15.0 left slow suites to a scheduled
run. What each earlier release added is
in the changelog, and what upgrading to it asks of you is in
the upgrade guide.
Recall on real CI failures, with every miss and its cause, is in the
changelog and on the
benchmarks page.
Hooks ship for Claude Code, Codex and Gemini CLI. The roadmap
has milestones K0 to K6, each an issue
with its exit criteria.
Install
# macOS and Linux
|
# Windows (PowerShell)
# In a JavaScript or TypeScript project, as a dev dependency
# In a PHP or Laravel project, as a dev dependency
# With Homebrew (macOS and Linux)
# From crates.io, with Rust 1.95 or later
Since 0.14.0: the Homebrew tap, crates.io and Composer.
Then fairlead doctor says which binary, platform and config it would use.
Since 0.13.0: static musl builds for x86_64 and aarch64 Linux, which the
installers pick on a musl system such as an Alpine container image. On a
platform with no build, the npm package still installs, and fairlead says
how to build it from source (Install).
Since 0.14.0: the Composer package, for a PHP project without Node. A small
PHP launcher downloads the release build for the platform on its first run,
checks its sha256 before unpacking it, and caches it in vendor/
(Install).
Since 0.18.0: the npm package retries a failed download, and
FAIRLEAD_BINARY points it at a binary fetched some other way, such as from
a CI cache (Install).
Quick start
Documentation
The site is mahkassem.github.io/fairlead, and the book covers
install, configuration, the import graph, the test plan, plans in CI, guard
rules and hooks, the brief, the done gate, the receipt, replay and the
benchmarks. Its source is in docs/.
The README, the site and the book describe the latest release, and each says
which one. For an earlier release, read them at its tag, such as
v0.5.1. A release
can't be cut while any of them lags it: scripts/docs_check.py runs before the
release builds.
Brand
The logo, its colors and how to use them are in assets/brand.
License
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE)
- MIT license (LICENSE-MIT)
at your option. Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.