Haft: a local governance layer that ages project decisions for AI coding agents
Engineering decisions engine that know when they're stale. Frame, compare, decide — with evidence decay and parity enforcement. For Claude Code, Cursor, Gemini CLI, Codex and more.
At a glance
- What is it?
- Haft records the framing, the compared options and the human decision behind engineering work, then marks the evidence as it goes stale. It is built on the First Principles Framework and installs per host, with Claude Code and Codex as the only stable targets.
- Who is it for?
- Adopt Haft if you run Claude Code or Codex on a project where decisions outlive the conversation that produced them, and you are willing to treat the ledger as a real artifact with migrations attached. Do not adopt it if you need Cursor, Gemini CLI or OpenCode as your primary host, since the README files those as experimental or legacy adapters, or if you cannot accept a SQLite ledger and a versioned FPF source in your working context.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository last received commits 35 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
The problem Haft names: decisions that outlive the conversation
An AI coding agent forgets. A session ends, context is discarded, and the next session reopens the same question with no record of why the previous answer was chosen. Haft's README describes the target state plainly: durable project memory covering what problem is being solved, which options were compared, which decisions the human made, what evidence supports them, and what has gone stale. The stated split is that small, reversible reasoning stays in conversation, while results that later work must rely on become typed project records. That boundary is the whole design idea, and it is a deliberate one. Haft is not trying to persist every token. It is trying to persist the subset of reasoning that downstream work will depend on, and to make the dependency explicit. The audience follows from that: engineers using agent hosts on projects long enough that a decision made in March still constrains what is possible in June. Single-session scripting does not need this. Multi-month systems work does.
FPF as the substrate, and what Haft adds on top
Haft is built on the First Principles Framework by Anatoly Levenchuk. The README is candid about the size mismatch: FPF is described as a rigorous architecture for thinking about systems, and not small. Haft positions itself as the practical handle, bringing the versioned FPF source into the agent's working context and adding project-local skills, MCP gates, and memory. That framing matters for evaluation. Haft does not reimplement FPF; it ships a version of it and wires it into hosts through MCP. The consequence is that adopting Haft means adopting an external specification's vocabulary and revision cadence alongside the tool. The README treats the FPF source as versioned, which implies the source can move independently of the binary. Whether the two are pinned together is not stated in the supplied material, and that is a question worth answering before a team standardizes on it.
Install and the init flag matrix
Installation is a single shell pipeline: curl -fsSL https://raw.githubusercontent.com/m0n0x41d/haft/main/install.sh | bash. Initialization is where the design becomes visible. Bare haft init opens an interactive multi-select and preselects no host. In a non-interactive context it fails before writing files rather than guessing. That is a defensible choice for a tool that writes into configuration files, and it means CI must name its intent. The flags are explicit: haft init --core-only for project core and ledger with no host carriers, haft init --claude for MCP plus skills plus a CLAUDE.md section, haft init --codex for the equivalent Codex path writing .codex/config.toml and an AGENTS.md section. Adding --local moves skills into the repository instead of the user home, which is what you want if the skills should be shared. The --mcp-only modifier is documented as a compatibility flag: it requires an explicit host flag or --all, and it suppresses that host's skills and managed instruction section. --agents is a skills-only target with no MCP and no instruction publication. --all means exactly the full Claude and Codex integrations, not a union of every adapter. That last point is easy to misread, and the README says it twice.
What init writes, and the markers that keep it from eating your files
Re-running init replaces recognized legacy Haft skills and updates only the content between <!-- haft:start --> and <!-- haft:end --> in project instruction files. Everything outside those markers stays project-owned. The README also states that a foreign file colliding with a desired Haft-owned skill path causes a failure before writes rather than an overwrite. Both behaviours point the same way: the tool would rather stop than clobber. The per-host table is worth reading before choosing flags, because the carriers differ. Claude Code writes .mcp.json and a managed section in CLAUDE.md. Codex writes .codex/config.toml and a managed section in AGENTS.md. Zed is different in kind: it writes ~/.config/zed/settings.json under context_servers.Haft, has no skills target and no project instructions, and its settings are global, so the server may start outside the workspace cwd. The README notes that haft init --zed compensates by writing HAFT_PROJECT_ROOT and HAFT_EXPECTED_PROJECT_ID for the project where init ran. Project-scoped configs such as .mcp.json, .codex/config.toml and .grok/config.toml use portable project-root paths and are described as safe to commit. Zed and Antigravity settings are not.
Migrations, snapshots and the startup-safe boundary
Upgrading is the part of Haft with the sharpest edges. After installing a new binary you must fully restart or reconnect the coding-agent host. A new haft serve process applies only migration boundaries the release explicitly marks as startup-safe, and the README states the current chain covers 57 -> 58 and 58 -> 59. At each boundary Haft first publishes a verified 0600 SQLite snapshot beside the project ledger. A current database is a no-op. The README is emphatic that re-running haft init is not routine database maintenance, which is a useful warning because init is the command a user already knows. If startup reports a manual migration boundary, the fallback is the exact command from that diagnostic: haft project migrate --project-root /absolute/project/root --project-id qnt_........ The README notes it verifies the exact project binding and shares the same migration lease as haft serve and haft init, so concurrent runs do not race. It also warns that future-schema, missing-binding, integrity and stale WAL/SHM diagnostics have different recovery paths and should not be collapsed into a generic migration run. That is a lot of operational surface for a tool whose pitch is project memory, and it is the cost of storing decisions in SQLite with a schema that evolves.
Host support: two stable targets and a long experimental tail
The README is unusually direct here. Claude Code and Codex are the stable supported hosts. Grok, Pi, Hermes, Zed, Antigravity, Cursor, Gemini CLI and OpenCode are described as experimental or legacy adapters, each with its own flag. The repository description lists Cursor, Gemini CLI and Codex among the supported agents, and the topics include cursor-ai and gemini-cli, but the README's own stability statement is the more precise claim. Two host-specific notes are worth carrying forward. For Grok, the native project .grok/config.toml takes precedence over Claude and Cursor compatibility MCP sources, so a stale global haft entry in ~/.claude.json no longer shadows the project server; you reload MCP with /mcps then r, or start a new session. For Cursor, the README says Cursor adds MCP servers disabled by default, so after init you open Settings, then MCP, find haft and enable the toggle. That is a manual step no init flag can perform, and it is the kind of detail that decides whether a first run succeeds.
Where Haft is the wrong tool, and what it competes with
Haft is the wrong tool when the decision record does not need to outlive the task. If you are asking an agent to rename a function or write a test, the typed-record layer is overhead with no payoff, and the README's own framing supports that: small, reversible reasoning is meant to stay in conversation. It is also the wrong tool if your primary host is one of the experimental adapters and you need a support commitment. The comparison that matters is against the memory features already inside the hosts. Claude Code and Codex both carry project instruction files, and Haft writes into CLAUDE.md and AGENTS.md through managed markers rather than replacing them. So Haft is additive to the host's own memory, not a substitute. The difference in approach is the decay and parity layer: host memory is prose that stays as written, while Haft stores typed records with evidence that is marked stale and enforces parity between them. Whether that difference earns a SQLite ledger and a migration chain depends on how long your decisions actually live. A team that has never lost the reasoning behind a choice will not feel the benefit. A team that has re-litigated an architecture question three times will.
Licence and maintenance questions the material leaves open
The repository reports its licence as NOASSERTION, which means the licence could not be determined from the standard metadata. Haft is built on FPF, a separate project by a different author, and the README does not state how the two licences interact or what the versioned FPF source is distributed under. That is a real gap for anyone embedding Haft in a commercial workflow, and it is not something to resolve by reading a README. On maintenance, the release cadence visible in the supplied material is tight: v9.0.2 on 2026-08-08, v9.0.3 on 2026-08-11, v9.1.0 the same day. A major version line moving that fast means the migration chain is not theoretical. The README's own instruction to restart the host after installing a new binary, and to use the exact fallback command from a diagnostic rather than a generic migrate, tells you the upgrade path expects attention. Budget for that. If your team pins the binary and skips releases for a quarter, expect to cross several migration boundaries at once, each of which writes a 0600 snapshot beside the ledger.
Editorial conclusion
Adopt Haft if you run Claude Code or Codex on a project where decisions outlive the conversation that produced them, and you are willing to treat the ledger as a real artifact with migrations attached. Do not adopt it if you need Cursor, Gemini CLI or OpenCode as your primary host, since the README files those as experimental or legacy adapters, or if you cannot accept a SQLite ledger and a versioned FPF source in your working context. Before committing, verify one thing: run haft init with an explicit host flag in a scratch repository, inspect the managed block between <!-- haft:start --> and <!-- haft:end --> in CLAUDE.md or AGENTS.md, and confirm the ledger path and the 0600 snapshot behaviour match what your team expects to commit.
Community notes