An opinionated, reusable way to build Clojure software with a small team of independent AI agents — a contract-first blueprint, an isolated dispatch loop, a living decision log, and quality gates ordered cheap-to-expensive.
Distilled from a live multi-agent build that ran this loop across dozens of real dispatches, including several it got wrong. What's here is the discipline that survived contact with those runs, not a design done on paper.
KIT is the short form of the Clojure Agent Kit, and is how these documents refer to it.
It is a name, not an acronym. The application template it brings with it is
ontopro/clojure-stack-lite, always named with
the tag KIT pins, as in ontopro/clojure-stack-lite at tag kit-v1.3: those tags version the
template, not KIT.
Clone it and build beside it. KIT is what a project adopts: its clone sits in the project's workspace next to the application it generates from a pinned template, upgraded with
bb upgradefrom the workspace folder - the network checked, the clone clean, the tags fetched, each newer release's requirements read from its tag and checked against the machine, the clone moved to the release, then the workspace brought up by the new version's own code, step by step, with what it leaves for the Architect said bybb doctorandbb nextuntil resolved - and nothing of the project is written into it. The first upgrade from0.6.4is by hand, since0.6.4has nobb upgrade:git -C KIT fetch --tags && git -C KIT checkout <tag>, thenbb --config KIT/harness/bb.edn upgrade --after-pullfrom the workspace folder. KIT carries a version tag at each plan's boundary -0.7.1is the current one, and the DEVLOG's heading for it says in a line what the plan added and anything a workspace made before it needs;bb initrecords the version beside the commit. There is no library to require; there is one commit of one template that a dated health check certifies with it.
What is in the clone - the method, the build docs, the harness, the tools, the skills - is listed under Five parts, in the section for KIT developers below.
Important
KIT is certified on an Apple silicon Mac (macOS, arm64) and on arm64 Linux - the health records below. Its toolchain is published for amd64 Linux too, and no record proves it there yet. An Intel Mac is not supported: cljfmt, which KIT runs, publishes no build for it.
KIT installs its own toolchain. You install the two things that run it:
| Install | Version | Route |
|---|---|---|
| Babashka | 1.12.212 or newer - it runs KIT; the true-up adds the matrix's own bb to the project's folder only when yours is older |
babashka.org → install; on macOS brew install borkdude/brew/babashka |
| git | 2.41 or newer - every read of a diff takes --attr-source, which git has from 2.41; bb doctor says an older one |
git-scm.com/downloads; on macOS xcode-select --install |
The rest comes from bb self-check (step 4 below): a JDK (Temurin 25), the Clojure CLI, bb,
cljfmt, clj-kondo, bbin and clojure-mcp-light, each at the version the download matrix names
(tools/toolchain/download-matrix.edn) and checked against
its sha256, into the project's toolchain folder - <xyx>/.kit/toolchain, beside the clone, or the
folder KIT_TOOLCHAIN_DIR names (another disk, or one folder several projects share). About 400 MB
on disk, 294 MB of it the JDK - 470 when the true-up also installs bb, which it does only when yours
is older than the matrix's (the matrix's :disk-mb, each measured after a full install). The doctor's
offer lists what this machine gets, each with its size, and the disk's free space, and asks first.
bb doctor holds every tool on the PATH to the matrix - the same version is ok, a newer one is
said, an older one or another commit fails with the command that fixes it - and shows KIT's dated
known-good set beside it (the Mac's health record, harness/health/records/macos-arm64.edn: the
versions that were actually run together). Only on a machine the matrix publishes no build for is your own JDK used: 21 or newer,
25 preferred (XTDB, an optional store, documents a minimum of 21, and 2.2.0 runs on 25 with
-Dio.netty.noUnsafe=false).
There are basically 2 steps to use KIT:
- setup - to set up the KIT adopter's workspace
- build - to build the adopter's application
One order, the same for a project and for developing KIT itself: Babashka and git; the project
folder and the clone in it; bb self-check; the keys; the eval; then bb init.
-
Install Babashka and git, as described in Before anything: Babashka and git above.
-
Create project folder
<xyx>. This will be the main working directory where all the work will be done.- recommend using a short name for the
<xyx>. - the name must be lowercase letters, digits and single hyphens, starting with a letter -
for example
my-site- becausebb inittakes it as the project's name, and it becomes folder names and the application's namespace. - The only thing you put in it yourself is KIT's clone, in step 3;
bb self-checkandbb initmake the rest.
- recommend using a short name for the
-
Open a terminal and
cdinto the newly created<xyx>. Clone this repo into it with:git clone https://github.com/ontopro/clojure-agent-kit.git KIT
- KIT is a clone of the Clojure Agent Kit (aka KIT) and it will be guiding you through the product build, test and deployment process.
-
cdinto the clone and runbb self-check, the one setup command:cd KIT bb self-check- It points the clone's git hooks at
.githooks/, and writes<xyx>/.envwith the names of the keys the default profile needs, each empty - a.envalready there is never touched. - Then
bb doctor: what the project's toolchain folder lacks, with sizes, andInstall? [y/N]. Onythe true-up installs it; on a no, self-check stops, andbb self-checkagain picks up. - Then
bb health: this KIT, this machine, the pinned template - minutes, starts JVMs. - It ends with the next three steps.
- It points the clone's git hooks at
-
Put this project's keys in
<xyx>/.env.bb self-checkwrote the names its profile needs, each empty, with what uses it and where it is sent. One key per project, with a spending limit set at the provider's console. The file is yours alone (mode600); a.enva repository holds is refused at load; and the session's file tools are denied it (.claude/settings.json- a rail, not a lock:NOTES.mdrow 236). It may hold what else the application needs too - aDATABASE_URL, aPORT. -
Put the project's toolchain first on this terminal's PATH and load the keys:
eval "$(bb toolchain-env)"
toolchain-envprints no value: it checks the.env, then prints the line that has your shell read it. A line the shell would run ($(...), a bare word) or a name a program reads its hooks from (PATH,GIT_*,BABASHKA_*...) is flagged by its line number and nothing is loaded: read it, change it, or accept the file as it is withbb env-accept. So is a name left empty (NAME=) that your shell already has set - loading it would empty the shell's: fill the line or remove it.- Each change it sees is noted in
.env-record.ednbeside it - the names added and removed, never a value - so a.envthat changed when you did not change it shows. - With mise activated in your shell, its shims come back in front of the folder's
bin/after the eval, so a.mise.local.tomlnaming another JDK would win over the folder's. The application's.mise.tomlnamestemurin-25, the matrix's major, so by default the two agree.
-
Make the workspace, move to it, and check the profile sees the keys:
bb init cd .. bb profile- By default
initnames the project after<xyx>(bb init <name>names it otherwise) and makes<xyx>-appand<xyx>-buildplus a few other directories and files in<xyx>- always the folder the clone is in. It writes no.env: it says which names the profile needs that the file lacks, if any. <xyx>is where you will be doing all the work from.
- By default
Important
The keys reach a session only through the shell that starts it. Run the eval in the terminal you
start claude from, and again - with claude restarted - after you change the .env.
At this point you are all set to build your application.
- Make sure you are in the
<xyx>directory, in the terminal step 6 loaded. - Start a Claude Code session there (
claude) and type/kit-start. - Claude walks you through:
- the workspace directory structure
- the approach: the opening of KIT's
method.md, then the steps in order fromworkflow.md - the roles and the models they run on -
harness/roster.mdhas the when and why - and the model and effort it recommends for this session, the Architect's - the first step: scoping, stage 0's first step, in phase B, PROVE
- At any time after,
/kit-where-am-isays where the project stands - the phase, the stage, what is done and open - and reads into whichever part you pick.
Rendered by bb health-sync from harness/health/records/, one record per platform - its latest run, every run kept beside it under records/<platform>/ - each
written by bb health --record after a run in which every check passed: the selfcheck project and an
application generated from the pinned template, gates green, every gate failed on purpose, one
task through the loop, the application served, a headless Firefox opening it through KIT's browser
pack (tools/browser/) where geckodriver and Firefox are installed (skipped, and the row says so,
where they are not), and KIT's security pack (tools/security/) trying from outside what the
build docs' architecture document says the template gives, and asking about every library it
ships and every line of its history. bb gates fails if this block and the records
disagree. The date is the claim; nothing here says it still holds today.
| Platform | Run on | KIT commit | Template | Checks | Time |
|---|---|---|---|---|---|
| Linux Ubuntu 24.04.5 LTS arm64 | 2026-11-09 | 5d5fc90 |
kit-v1.3 (f12268d) |
10 of 12 ok, 2 skipped: selfcheck gates, selfcheck red, selfcheck loop, app gates, app red, app loop, app serve, app browser, app security, app image SKIPPED, xtdb embedded, xtdb pgwire SKIPPED | 99s |
| macOS 27.0.1 arm64 | 2026-11-09 | 5d5fc90 |
kit-v1.3 (f12268d) |
12 of 12 ok: selfcheck gates, selfcheck red, selfcheck loop, app gates, app red, app loop, app serve, app browser, app security, app image, xtdb embedded, xtdb pgwire | 179s |
One record per platform actually run, the latest run on it; a platform not in the table has none. bb health --record on such a machine writes one - commit it, and bb health-sync.
It pays for itself when a project has enough scope to amortise setting the process up once, and a correctness bar worth a dedicated, independent verification step. For a weekend build or a single-session prototype, skip to a plain coder loop plus gates plus one human review — the full role model and the decision log are overhead you don't need yet.
It scaffolds on stock Clojure Stack Lite (HTMX, AlpineJS, TailwindCSS, SQLite/PostgreSQL), with XTDB v2 as a SQL-compatible alternative datastore.
NOTES.md is the honest state of KIT — what is deliberately deferred, and
where the weak points are.
| What it is | |
|---|---|
method.md |
The method. Five phases — Setup, Prove, Build, Pre-release, Release — each a group of stages by kind, run as one lean-agile discipline: lean decides what to build, agile decides how, and the flow is Kanban (pulled, WIP-limited, no timeboxes). Roles, the task-packet contract, the rule source, the decision log, the gate order, and a field guide of twelve lessons each bought with a real run. |
build-docs/ |
The build docs, half-written on purpose — the source a plan derives from, an overview with a ranked risk register, requirements with MVP/post-MVP scoping, architecture and method-and-tooling each in three parts (GIVEN by adopting KIT, CHOSEN once in stage 0's plan, THEIRS the domain), the decision log, and just-in-time stage docs. bb init copies it into the project's plan repository, beside the rules overlay, the profile and the run records that repository also holds, and bb plan-check reads the filled plan before the first dispatch. |
harness/ |
The code that runs, and its health check's first subject (health/selfcheck/, a deliberately trivial project with gates that execute and fixtures that make each fail): the doctor, bb init and the health check; the two readings of a filled plan (bb plan-check, the gate; bb plan-review, the model's pass); the packet assembler, the gate runner, gate 0 and the boundary gate, three-worktree provisioning, an API-backed runner, a per-run cost report that names the three commits it ran against, the rule source with a drift gate, a bake-off that compares candidates for a role with a judge reading blind, and the loop that drives them — stopping for a person at every branch it cannot decide. Its own README is the inventory. |
tools/ |
Tool packs a project runs from KIT's clone, each with its own bb.edn and dependencies, against the project's own server whatever its framework — the way a project already runs the boundary gate. The first is tools/browser/: the stage-end checks in a real browser (screenshots as tall as the page with the overflow measure, an axe-core scan, the serve-check-stop skeleton), Etaoin under Babashka driving a headless Firefox through geckodriver. Beside it, tools/svg/ checks the SVG files a project draws, and tools/security/ tries from outside what a request can - the headers, the cookies, a POST without its token, the routes that need a login, the error pages, the static folders, TLS - at every stage's end and in bb health. Its README is the list. |
skills/ |
One skill per step of the workflow that is a conversation with a person and has a defined output - kit-scoping, kit-plan, kit-stage-plan, kit-stage-end, and beside them kit-start, the orientation a project's first session begins with, and kit-where-am-i, at any time where the project stands and a guided reading into it - in one anatomy (when it runs, reads, refuses when, asks, writes, done when), each a thin layer over the method section it cites. Claude Code's mechanism, loaded from .claude/skills/: bb init renders them into a workspace's, where kit-start is the first conversation; bb skills-sync, run from the workspace folder, renders them again after an upgrade (bb upgrade runs it), and its --check in KIT's gates holds every cited heading to the method. The other seats follow the same sections by hand. Its README says the rules. |
Each of the repository's own documents has one job, and none of them repeats another:
NOTES.md |
what is missing, weak or open now |
DEVLOG.md |
what changed, when, and why — newest first |
portability.md |
running KIT from a seat other than Claude Code, and the dispatch design that follows |
harness/README.md |
the harness: what is in it, how a project adopts it, what is deliberately left out |
workflow.md |
the method as it runs on KIT, in order — setup, then stage 0 (scope, plan, implement, deploy locally), the stages with their four steps and their reviews, the loop, pre-release and release — as one diagram and a table of steps |
harness/roster.md |
who acts in a build — every review, gate and dispatch — from which role, on which model |
CLAUDE.md |
working rules for an agent changing this repository |
That separation is not tidiness. Two of these carried the same forward-looking list for a while and both went stale; the rule now is that a fact lives in one of them and the others link to it.
The same order as a project's, with the two seats in place of bb init:
- Babashka and git (Before anything).
- A folder
KIT/, and this repository cloned into it:KIT/clojure-agent-kit/. The folder above the clone holds the toolchain (KIT/.kit/toolchain) and the keys (KIT/.env), as<xyx>/does for a project. bb self-checkin the clone - the hooks,KIT/.env, the doctor with its offer, the health check.- The keys in
KIT/.env;eval "$(bb toolchain-env)"in the terminal that starts the sessions. .local/wording/names.txt- the project names no committed line may carry, one a line; an empty file to start. The commit check refuses every commit while it is missing, and says how.- The two seats: the KIT session,
claudein the clone, which writes; and the reading session,claude --agent kit-reader -n kit-reader --model fablein a tab of its own. Thekit-sessionskill (.claude/skills/kit-session/, never shipped) is the routine they follow, andkit-security-reviewthe KIT's own security review, run when the person asks.
Important
The order is the point: the containers clone HEAD and see only what is committed, so the health records are committed before the gates run on the commit the tag will name.
- The plan's work committed,
bb kit-statusread, and the tree clean. - The health records on that commit:
bb health --recordinharness/, thenbb docker-healthat the root, thenbb health-sync; the records and the README block committed. A record names the commit it ran on, and a tag on code its records never ran is a tag on an untried claim. They make known-good the pins:bb tag-check'sversionsline holds the download matrix, the pack's pins and the Dockerfile's to what the runs recorded, the JDK apart - the KIT an adopter gets has its pins equal to known-good. - The tag's documents: its heading in
DEVLOG.md(## <tag> — <what the plan was>), with its line - what the plan added, and anything a workspace made before it needs ("How it is versioned" there) - and this README's sentence naming the current tag. - On the commit the tag will name:
bb repair && bb gatesgreen, thenbb docker-gatesgreen - the gates on a clone of that commit in the KIT's toolchain image, a fresh Linux machine in Docker with only the KIT's toolchain: the check on a fresh machine until a workflow runs it on push. - The person's, by hand: bear-claw's secrets scan, a second rule set the KIT's scan does not
carry; Docker Scout's scan of the KIT's toolchain image (a Docker login),
docker scout cves local://clojure-agent-kit-toolchain:<as-of>-<digest> --only-severity critical,highasbb tag-checkprints it, a HIGH or CRITICAL against a pinned tool moving that pin before the tag; and the KIT's pins (bb kit-pins), a go or no-go per row, a row that goes moving before the tag with its health runs. - The merge, the tag and the push, by the person.
bb tag-check <tag> at the root is this list's mechanism: a line per check and no tag unless every
one holds. --run does steps 2 and 4 where they are stale, stopping for the commit between them;
--landed checks origin after step 6. It names step 5's scans and runs none of them.
bb self-check # from the root of the clone, the one setup command: the hooks, the .env template, bb doctor
# with its offer to install, then - when it says yes - bb health
bb doctor # from the root of the clone: every tool, its version, and for anything unusable
# the command or page that fixes it. Run it, follow it, run it again - until both
# verdicts say yes: "The KIT's gates can run here" and "A loop can run here"
bb health # then, once: is this KIT healthy here, with the template it pins? Minutes, starts JVMs:
# the selfcheck project and a freshly generated application, gates green, gates failed on purpose,
# one task through the loop, the application served, a Firefox opening it (skipped, and said, with no geckodriver)
bb init # then: the workspace, named after the folder the clone is in - see harness/README.md
bb gates # KIT's own gates: doctor -> format -> lint -> boundary -> containment -> shell lines -> rules -> skills -> the register -> inventory -> health block -> the records (reports, stage reports, bake-offs) -> test
bb hooks-install # the pre-commit hook, which runs `bb commit-check` - bb self-check sets it too
bb commit-check # what the hook runs: refuse unless the last `bb gates` was green on exactly this tree, or on one
# differing only in DEVLOG.md/NOTES.md (the register checked), and while .local/wording/names.txt is missing
bb kit-status # at an item's end or before a restart: the working tree, the Next line, flaky tests, stale health records, open register rows, the plan's log
bb tag-check <tag> # before a tag: the checklist above for <tag> on HEAD (--run does what is stale, --landed after the push)
bb docker-gates # the gates on a clone of HEAD in a fresh Linux container; bb docker-health the health check there
cd harness
bb example # the whole loop shape in one run — no model calls, no networkThe root bb.edn is KIT's own task list - setup (self-check, init), the gates and the KIT's
development checks - most of them handed to harness/, where the harness and the rest of its tasks
live. A project runs its KIT commands from its workspace folder, through the front door bb init
writes there (<xyx>/bb.edn); bb gates there runs the workspace's own checks, and bb front-door-sync
writes it again; bb upgrade runs it among its steps.
bb example is the 60-second tour: it assembles a task packet, shows the Tester's context
being stripped of the implementation, dispatches to a scripted runner, repairs the output,
runs the gates until one fails, and checks the runner against the contract.
- The clone. A workspace's clone of KIT is
KIT/by the README's steps, but the name is not fixed:workspace.ednrecords the folder, so it may be renamed. It stays in the workspace:bb inittakes no folder, the workspace is always the one above the clone. - The template.
ontopro/clojure-stack-liteis KIT's fork of the upstream template: itskitbranch is KIT's line of it, taggedkit-v1,kit-v1.1,kit-v1.2, …, and itsmasteris an untouched mirror of the upstream.
harness/PROVENANCE.md records what was extracted from
the private harness it came from and every place this copy deliberately diverges — each with
its reason. It is a fork, not a mirror; "sync with upstream" is not a supported
operation, and the divergences are fixes rather than drift.
MIT. Six rules in the harness's rule source are adapted from
github/awesome-copilot (MIT), and the application
template KIT pins is a fork of abogoyavlensky/clojure-stack-lite
(MIT); see NOTICE for the attributions and the upstream licence texts. An application
generated from it carries no licence file: choosing one is its owner's first decision.