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. - 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.14.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. - Rules as the agent works:
guard check, the hookshooks installadds, anddoctor. - 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. - 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. - 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. - 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.14.0, measures and uses Bun 1.4.4: init
writes the parallel test runner and offers a per-change bun fmt check, and
fairlead test bench times Bun's test modes on your own tests and says which
to use. A named import through a barrel now depends on the file that declares
the name, so a change behind a package's index.ts selects only the tests
that use it. Fairlead installs with Composer too, and releases are ready to
publish to crates.io and a Homebrew tap. 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 project, as a dev dependency
Since 0.14.0: with Homebrew, brew install mahkassem/tap/fairlead, or from
crates.io, cargo install --locked fairlead.
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).
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.