Keel keeps a way of building software in one place, and carries it into every project that uses it.
Overview
Keel keeps a way of building software in one place, and carries it into every project that uses it. The way of working came from four projects: isocan, ledger, duo and cajones. Each one copied it from the one before, by hand, and the copies drifted. Keel makes it one versioned thing. It does three jobs: 1. Ways of working, each with the failure it prevents, live in practices/. 2. keel init and keel adopt install the practice. keel update and keel fleet update bring each new practice version as a pull request a person reads, or a local diff with keel update --local. 3. A project sends what it learned (keel lessons). Keel turns that into proposals (keel learn), a person decides, and the next release carries the change to everyone. A project never depends on keel at runtime. Its workflows run from its own repo, with its own copies of the scripts they need. It prints a cold start: what keel is, every verb on one line, and the rules that bite.
README
Keel
Keel keeps a way of building software in one place, and carries it into every project that uses it.
The way of working came from four projects: isocan, ledger, duo and cajones. Each one copied it from the one before, by hand, and the copies drifted. Keel makes it one versioned thing. It does three jobs:
- It is the source of the practice. Ways of working, each with the
failure it prevents, live in
practices/. - It carries changes out to projects.
keel initandkeel adoptinstall the practice.keel updateandkeel fleet updatebring each new practice version as a pull request a person reads, or a local diff withkeel update --local. - It is where lessons come home. A project sends what it learned
(
keel lessons). Keel turns that into proposals (keel learn), a person decides, and the next release carries the change to everyone.
A project never depends on keel at runtime. Its workflows run from its own repo, with its own copies of the scripts they need.
If you are an agent, run
keel --agent-helpfirst. It prints a cold start: what keel is, every verb on one line, and the rules that bite.keel --agent-helpopens the details, andallprints everything. It ships with the CLI, so it always matches the keel you have. The rest of this page is the same story, told for people, and the guides tell it one situation at a time.Every keel project carries a short
keelskill at.agents/skills/keel/SKILL.md(linked from.claude/skills/keel). An agent working there finds keel and runskeel --agent-helpwithout being told.
Install
Node 24.21.0 or later, and git. No registry, no build step.
git clone https://github.com/dalmaer/keel ~/code/keel
npm install -g ~/code/keel
Then keel --version prints the CLI’s version, its commit, and the
practice version it carries. The global install is a link to the checkout,
so updating keel is a git pull there, and keel update does that for you
first when the checkout can fast-forward cleanly.
The CLI version moves with every release. The practice version moves only when practices, migrations or the lessons catalogue change; a CLI-only release leaves projects current. See Keep the fleet current.
What a project gets
A project that uses keel carries .keel/keel.json, which records:
- its name;
- the gate command that must pass (
check); - the practice version it is on;
- the practices it has switched on;
- where its nightly health page goes (
health, defaultdocs/health;keel doctorflags a directory the project git-ignores); - how the test ledger judges a slower test (
tests:window20,factor2,floorMs200); - anything it does its own way, as a local variant with a reason.
Existing agent guides are preserved: adoption selects AGENTS.md, then
CLAUDE.md, or an explicit --guide . See the adoption guide.
Projects can also keep GitHub milestones as their plan with
"phases": {"source": "milestones"}. Keel projects them into next, the
board and the night without editing GitHub or claiming acceptance from closure.
Local phase-homing in Loop and proof writeback are inactive for that source;
archived phase files remain unchanged.
The practices:
| Practice | What it gives a project | The failure it prevents |
|---|---|---|
base |
The project’s skeleton: Node pinned in .nvmrc, a package.json whose check is the gate, an ignore file |
Each project starting from a different floor |
phases |
One file per phase in docs/phases/, owning its status; docs/goals.json; a generated roadmap that CI checks, which also refuses a phase still holding the template’s text; a new phase names the check behind each acceptance box and where it runs for real |
Status written in two places, drifting; a spec that cannot be proven |
evidence |
built needs an evidence file that says what was actually checked |
Claiming what was never run |
lessons |
docs/lessons.md: failure shapes, each with the guard that now catches it; docs/keel-lessons.md, generated: keel’s catalogue filtered for the project’s stack |
Paying for the same bug twice, or one keel already paid for |
conduct |
The conductor skill: brief a builder, verify the named proof yourself, record, commit | Trusting a subagent’s “tests pass” |
agents-md |
Practice sections in the selected agent guide, preserving existing project instructions | Instructions copied per harness, ageing |
ci |
A check workflow running the project’s own gate, keeping each run’s test ledger as an artifact |
A green laptop that isn’t the build |
night |
keel-night.yml measures the project nightly, opens at most one PR, and goes red only when something is broken; a test ledger that remembers every node --test run and ends it naming any flaky or slower test |
Guards that fire into an empty room; a flaky test rerun until green |
claude (optional) |
@claude on issues and PRs: it opens PRs and never pushes to main |
Agents landing unread changes |
renovate |
Dependency updates in four lanes: small ones merge on green, majors wait for a person | A pile of dependency PRs nobody reads |
personas (optional) |
Goals the project sets itself (a number, a bound, the command that prints it), measured into a dated review page; a missed goal becomes a candidate for the night’s plan | Standards stated once and never measured again |
reconciliation (optional) |
Local and read-only GitHub record checks, PR impact declarations, manual health proposals | Merges mistaken for acceptance; obsolete next work and decisions |
loop (optional) |
Stitch Loop findings triaged as files: the ranking is ours, an agent proposes, a person decides | Outside findings taken as verdicts |
climb (optional) |
Climb nights: on a schedule, an agent improves one number (the suite’s time, first) under a shared protocol; a script makes every measurement and keep-or-revert, and a person merges the one PR | A speed-up nobody measured against noise; a test quietly gone |
cross-review (optional) |
An available provider reviews another provider’s PRs, falling back to self-review when necessary; inline P1/P2/P3 findings validated against the code, read-only and budgeted, never approves or merges | Review tied to one provider or running in only one direction |
Who owns which file. Every file keel writes is one of three kinds:
- Managed files are keel’s. They are re-rendered on update.
.keel/lock.jsonrecords what keel wrote. - Blocks are the `` regions of the selected agent guide. The rest of that file is the project’s.
- Seeded files (your phases, goals and lessons) are written once, and are yours from then on.
If you change a managed file, keel treats it as signal, not an error.
keel render refuses to overwrite it. keel doctor shows the diff and
offers three moves: keep yours (eject), take keel’s (restore), or send yours
home as a lesson.
An edit that keel has since made too, such as Renovate bumping an action in your copy of a keel workflow before keel ships the same bump, is not treated as yours: the next update simply takes keel’s file.
The loop
- Start.
keel initin an empty directory, orkeel adoptin an existing repo. Adopt switches on only what the repo already satisfies. Everything else is recorded as a local variant with a proposal, never overwritten and never faked. - Work. Phases are the plan.
keel nextlists the phases that are ready, and which of them another session already holds. A session takes one withkeel phase claimbefore it builds, so two sessions never build the same phase. In Claude Code,/conductwalks them: one phase at a time, claimed first, proof verified, one commit each. - Review. A reviewer reads the PR (with the cross-review practice on,
the other provider does), and
keel reviewmakes sure every comment is validated and answered. A review that is still drawing findings after three rounds stops there:keel reviewhands you the choice (merge with the rest tracked, split, or close) with the open findings sorted. See Conduct a phase for the whole path. - Overnight. The project’s own nightly runs
scripts/keel/improve.mjs. It writes a dated health page, opens one PR with it, and proposes one change. You read it in the morning. - Lessons go home.
keel lessonsfiles the project’s new lessons, its edits to keel’s files, and practice-shaped commits as issues on keel’s inbox (a private repo, so a private project’s lessons stay private), each exactly once. - At home.
keel learnturns those issues, and any upstream source that moved, into proposals. A person decides.keel releasecuts a version. - Out again.
keel fleet updateopens the update PR in each project that’s behind.keel updateruns it: the CLI first, then migrations, re-render, the project’s own gate, and a byte-for-byte restore if anything fails.
keel fleet shows every project at once: practice version, health, CI,
unsent lessons, and open machine PRs.
Guides
Each guide starts from a situation, says why keel does what it does there, then gives the commands and every flag by the reason you’d pass it.
| Guide | Read this when… |
|---|---|
| Start a project | you have an idea and an empty directory |
| Bring a project under keel | a repo already exists, perhaps with its own copy of the practice |
| Plan with phases and goals | you are lining up the work and saying what done means |
| Conduct a phase | a phase is specified and you want it built without trusting the builder’s word |
| The night shift | you want to know each morning whether the practice is holding |
| Climb and tend | you want an agent to improve a number, or keep the records true, while you sleep |
| Lessons and learning | a bug turned out to have a shape, or lessons are waiting at home |
| Keep the fleet current | the practice changed and every project should have it |
| The robot | Preview agent issues and opt in to bounded build/review work |
| Reviews and PRs | a PR from keel arrived, or a reviewer commented on one |
| When something is red | a night, an update, a doctor or a climb did not go green |
| Project canvas | you want a source-linked view of the project’s lifecycle and improvement |
| Session handoff trials | you want to measure and tune session handoffs within a bounded experiment |
| Loose ends and the retro | you are picking work back up, or a phase just finished |
| The board | you are asking “what’s next?”, or what is waiting on you |
The verbs
Every verb takes --json. Exit codes are the same everywhere:
- 0: done.
- 1: it ran and found something.
- 2: usage error, or not where it can run.
- 3: it needs a yes. Run it again with
--yes.
In a project:
| Verb | What it does |
|---|---|
keel status, keel next |
Where the project stands; the next free phase, and which ready ones are claimed |
keel goal list|show|add|retire |
Goals as outcomes. Progress is derived from phases, never stored |
keel phase new|list|claim |
Scaffold the next free phase under a goal; claim a phase before building it |
keel init, keel adopt |
Start a project, or bring one under keel |
keel render |
Write the practices onto the project; --check only compares |
keel doctor |
What the project changed of keel’s files, and practice rules broken. Changes nothing without --fix |
keel improve |
Measures against bounds, including worktree/draft/file hygiene; --report writes the health page and its proposal |
keel persona run [|--all] |
Measure the goals a project sets itself in .agents/personas/.md: each command’s one number against its bound, met, missed or broken; writes docs/reviews/-.md and nothing else |
keel time [--weeks N] |
Retained weekly gate/test timing, load coverage, failure memory and local worked-around test summaries |
keel test --stalls |
Run test files paused at random moments, and without; name each test that judges the wall clock rather than the code |
keel update |
Take the next practice version, as a branch for a PR (or --local) |
keel lessons |
Send what this project learned home to keel |
keel drain |
Keep one open PR per machine queue (the nightly runs this) |
keel night plan, keel night tick |
Write tonight’s default plan before the night runs; tick one of its items with a status, a note and a link |
keel goodnight |
You shape tonight’s plan: what was settled and left open, the default plan, things to try; add, drop, edit, budget or skip items; --yes pushes it to the plan branch |
keel morning |
The night’s brief: done, tried, failed and why, left alone, your next; --send opens the assigned “Good morning” issue, once a night; --card keeps the plan as a live canvas card |
keel retro sessions |
Reserve and record bounded handoff trials; audit, promote and roll back within an explicit envelope (keel improve --sessions reads the same evidence) |
keel retro |
After a phase that changed more than docs: a worksheet from the session’s transcript (counts and pointers, never its text) and seven areas to answer; the owner picks what becomes a check |
keel evidence rederive |
Preview structured measurements from a phase’s evidence; --yes reruns them and compares dated old/new values without rewriting history |
keel prove --fix ... |
A fix is proven by its test failing without it: runs the test with the fix reverted in a scratch copy of the repository, then with it, and prints VERIFIED, NOT WORKING or INCONCLUSIVE; --trailer gives the Proven-by: line a fix: commit carries. Never touches your tree |
At home, in keel’s own checkout:
| Verb | What it does |
|---|---|
keel learn |
Lesson issues and moved sources become proposals: propose is an agent’s, decide is a person’s |
keel release |
Release the CLI; bump the practice only when project-facing files changed, with a what’s-new for the recipient |
keel fleet, keel fleet update |
Every project at a glance; open update PRs where they’re behind |
Across projects:
| Verb | What it does |
|---|---|
keel canvas |
Project lifecycle snapshots and isocan canvas publishing; preview changes before applying them |
keel loose-ends |
What you started and didn’t finish, across keel and each fleet checkout: chats, files, branches, PRs, owner steps; mark one resume, park or drop |
keel issue new --agent |
Preview a rubric issue; --yes creates it, unlabelled while robot is OFF |
keel review # |
A PR’s review comments and which are answered; --wait for its reviewers; --close answers one fixed, tracked or not valid, only once it was read; --class sorts a finding without answering it. At three review rounds on an open PR it exits 3 and gives you the choice. Not a gate |
keel board |
Whose turn it is: every open item as yours, broken, the agent’s or waiting on time, gathered from the roadmap, loose ends, reviews, the health page, the inbox and the fleet. A page on 127.0.0.1, or --json |
keel walk done|decide |
Settle the owner’s turn: done checks a phase’s ⚑ walk and writes the read into its evidence (built when nothing is left open); decide accepts or declines a health proposal; time proposals require --instance, create/recover their issue on acceptance, and support reviewed --map transitions. Leaves a diff to commit |
Where a person decides
Keel stops and asks, with the price, before anything that:
- creates a repo;
- sets a secret or changes a repo setting;
- opens a PR or an issue elsewhere;
- spends money or model tokens on a schedule.
Outward verbs print their plan and exit 3 until they get --yes.
Judgements are a person’s too. An agent may propose a lesson, a rank or a
fix, and keel learn decide and Loop’s decide are a person’s. Nothing
marks a phase lived-in except someone who used the thing.
Working on keel itself
Keel is built with its own practice. AGENTS.md is the working guide, docs/design.md is the argument, and docs/ROADMAP.md is where it stands.
npm run next # next phase and action
npm run check # tests, roadmap, rendered practices and inbox views
npm ci --ignore-scripts # development-only type tooling
npm run typecheck # checked JSDoc, no emitted JavaScript
In Claude Code, /conduct walks the phases.
The CLI still runs directly from its .mjs source with zero runtime
dependencies. Type checking covers selected production evidence contracts;
it does not compile the CLI or change what adopted projects must install.
See measuring CLI cost and checking contracts.
Lineage and license
The practice was invented in isocan, ported to ledger, then to duo and cajones. Keel adapts isocan’s conduct skill and its measurement ideas under Apache-2.0, and credits them in NOTICE and in each adapted file’s header. Keel itself is Apache-2.0.
keel time --weeks 8 --json shows retained gate/test timing, machine load
coverage and local worked-around counts. Timing and the night.
Adoption previews include monthly weighted-minute estimates for newly added
workflows, with history and coverage. keel improve reports ci_minutes;
ci.gateWorkflow can reuse completed CI on the exact clean default-branch SHA.
These usage estimates are not invoices. CI usage and reuse.
Recommended Tools
Try a different keyword or remove a filter.
Install
npx skillfish add dalmaer/keel