Flaiwheel
Self-hosted memory & governance layer for AI coding agents. Turn every bug fix into permanent knowledge. Zero cloud. Zero lock-in.
🚀 Why Flaiwheel Exists
AI coding agents forget everything between sessions. That leads to repeated bugs, lost architectural decisions, and knowledge decay.
Flaiwheel ensures:
- Agents search before coding
- Agents document after fixing
- Commits automatically capture knowledge
- Memory compounds over time
Every bug fixed makes the next bug cheaper.
🧠 How Flaiwheel Is Different
- Persistent AI Memory That Compounds — knowledge doesn't reset between sessions.
- Git-Native Automation — commits automatically become structured knowledge.
- Governance, Not Just Storage — quality gates + enforced documentation.
- Hybrid Search + Reranking — high-precision context for real codebases.
- Fully Self-Hosted — single Docker container, no external infrastructure.
- Zero Lock-In — all knowledge stored as structured flat files in Git.
✅ Who Flaiwheel Is For
- Engineering teams using AI coding assistants in real projects
- Codebases where repeated bugs are expensive
- Teams requiring full data control
- AI-native development environments
❌ Not For
- Small hobby projects under a few thousand lines
- Developers who just want better autocomplete
- Pure SaaS workflows with no interest in self-hosting
🆚 Where Flaiwheel Fits
- AI coding tools generate code.
- RAG tools retrieve documents.
- Flaiwheel governs and compounds structured engineering knowledge inside your own infrastructure.
It does not replace your AI assistant. It makes it reliable at scale.
📄 Whitepaper (PDF) — Vision, architecture, and design in depth.
⚙️ Key Technical Features
Flaiwheel is a self-contained Docker service that operates on three levels:
Pull — agents search before they code (search_docs, get_file_context)
Push — agents document as they work (write_bugfix_summary, write_architecture_doc, …)
Capture — git commits auto-capture knowledge via a post-commit hook, even without an AI agent
- Indexes your project documentation (
.md,.pdf,.html,.docx,.rst,.txt,.json,.yaml,.csv) into a vector database - Provides an MCP server that AI agents (Cursor, Claude Code, VS Code Copilot) connect to
- Hybrid search — combines semantic vector search with BM25 keyword search via Reciprocal Rank Fusion (RRF) for best-of-both-worlds retrieval
- Cross-encoder reranker — optional reranking step that rescores candidates with a cross-encoder model for significantly higher precision on vocabulary-mismatch queries
- Behavioral Directives — AI agents silently search Flaiwheel before every response, auto-document after every task, and reuse before recreating — all without being asked
get_file_context(filename)— pre-loads spatial knowledge for any file the agent is about to edit (complementsget_recent_sessionsfor full temporal + spatial context)- post-commit git hook — captures every
fix:,feat:,refactor:,perf:,docs:commit as a structured knowledge doc automatically - Living Architecture — AI agents are instructed to maintain self-updating Mermaid.js diagrams for system components and flows
- Executable Test Flows — test scenarios are documented in machine-readable BDD/Gherkin format (
Given,When,Then) for QA automation - Learns from bugfixes — agents write bugfix summaries that are instantly indexed
- Structured write tools — 7 category-specific tools (bugfix, architecture, API, best-practice, setup, changelog, test case) that enforce quality at the source
- Structured relations (v1) —
relations()andtimeline()derive a per-project knowledge graph from optional YAML frontmatter on existing docs (id,replaces,depends_on,fixes,implements,status). No second store — markdown stays canonical and Git history is the validity window - Pre-commit validation —
validate_doc()checks freeform markdown before it enters the knowledge base, including unknown-relation-key warnings - Ingest quality gate — files with critical issues are automatically skipped during indexing (never deleted — you own your files)
- Auto-syncs via Git — pulls AND pushes to a dedicated knowledge repo
- Tool telemetry (persistent) — tracks every MCP call per project (searches, writes, misses, patterns), detects knowledge gaps, and nudges agents to document — persisted across restarts and visible in the Web UI
- Impact metrics API —
/api/impact-metricscomputes estimated time saved + regressions avoided; CI pipelines can post guardrail outcomes to/api/telemetry/ci-guardrail-report - Proactive quality checks — automatically validates knowledge base after every reindex
- Knowledge Bootstrap — "This is the Way": analyse messy repos, classify files, detect duplicates, propose a cleanup plan, execute with user approval (never deletes files)
- Cold-Start Codebase Analyzer —
analyze_codebase(path)scans a source code directory entirely server-side (zero tokens, zero cloud). Uses Python's built-inastmodule for Python, regex for TypeScript/JavaScript, the existing MiniLM embedding model for classification and duplicate detection. Returns a singlebootstrap_report.mdwith language distribution, category map, top 20 files to document first ranked by documentability score, duplicate pairs, and coverage gaps. Reduces cold-start token cost by ~90% on legacy codebases. - Multi-project support — one container manages multiple knowledge repos with per-project isolation
- Includes a Web UI for configuration, monitoring, and testing
What’s New in v3.13.0 — Observability
- Flaiwheel now knows whether its knowledge repo is still connected to its remote. Everything before this reported on pushes that were attempted. The failure that hid 325 documents in a Docker volume for 2.5 months attempted nothing: the clone had drifted from its remote, so there was never anything to commit, so no push could fail, so nothing went red.
check_divergence()comparesHEADagainst@{u}and classifies the result assynced/ahead/behind/diverged/no-upstream. - The "nothing to push" path is where this matters. That branch used to return an unconditional "already in sync". It now verifies the claim. Divergence is also checked after every successful push (did the commit actually land?), after a rejected push (a rejection is the classic symptom — now named instead of leaving you to read a git error), and on every pull.
- A repo that indexes perfectly and pushes nothing is no longer "healthy".
/healthgainsdivergence_status,commits_ahead,commits_behindandlast_divergence_at, and reportsdegradedondiverged,aheadorno-upstream. Beingbehindis the normal state between two pulls and deliberately does not alarm. - The agent is told directly.
write_*results append an explicit warning when the repo has diverged — including on "nothing to push". A warning in an endpoint nobody polls does not exist; the agent that just wrote the document is the one that needs to know it never left the machine. - Tests: 316 → 335, against real temp repos including a force-pushed rewritten upstream — the real-world trigger, where a secret purge or a squash silently desynchronises every clone.
- This closes the 2026-08-19 incident completely. The one item that looked outstanding — "watcher path scoping" — was retracted as a misdiagnosis after checking the running container. Its only evidence was the log line
knowledge: update flaiwheel/telemetry.json, read as one project's file being committed into all 11 repos. The real path is.flaiwheel/telemetry.json, with a leading dot: the signature of the porcelain off-by-one already fixed in v3.12.2. Every project owns an identically-named telemetry file, so all 11 watchers logged the same mangled string at once — uniformity caused by shared code, mistaken for shared state.
Previous: v3.12.3
- Every dependency is capped below the next major. Eleven requirements were unbounded
>=X. That fails silently: the breaking release lands, existing installs keep working off a stale resolve, and it only bites on the next fresh install — CI, a Docker rebuild, a new contributor. Exactly howmcp2.0.0 broke CI and the Docker build together three weeks after release while every dev machine stayed green. A clean install resolves to identical versions as before, so this constrains the future without moving anything today. - Sustained push failure now degrades
/health.HealthTrackerkept onlylast_push_ok— a single boolean the next attempt overwrites — so one blip and a repo failing for weeks looked the same.push_failures_consecutiveescalates past 3 consecutive failures. A single failure deliberately does not degrade; crying wolf on transients is how alerts get ignored. /healthnames the failing projects. Addslast_push_ok,last_push_error,push_failures_consecutiveanddegraded_projects— previously the endpoint could saydegradedwhile showing only the default project's numbers, with no way to tell which repo was broken.- Pre-deploy image smoke test documented. An image can build cleanly and still fail every import at runtime. The README now verifies
from flaiwheel.server import create_mcp_serverinside the image before starting a container, and renames rather than removes the previous container so rollback is instant. - Tests: 308 → 316.
Previous: v3.12.2
- Auto-commit no longer drops the first worktree-modified file.
git status --porcelainemitsXY <path>where a leading space is data (" M file"). Stripping the whole output before splitting ate that space on the first line only, soline[3:]truncated the filename's first character —.flaiwheel/telemetry.jsonbecameflaiwheel/telemetry.json,git addfailed, and the commit aborted. Intermittent and file-order dependent, which is why it survived so long. - Renamed and copied files are staged correctly. Porcelain reports
old -> new; the whole string was passed togit add, so renames were never committed.
Previous: v3.12.1
- Pinned
mcp[cli]<2.0.0.mcp2.0.0 removedmcp.server.fastmcp(FastMCP→mcp.server.mcpserver), breaking every import of the server on a fresh resolve.
Previous: v3.12.0
- Auto-push now reports what actually happened.
push_pending()returns a structured result (ok/noop/disabled/failed/blocked) and everywrite_*tool renders that outcome. Previously the success line was derived from configuration (git_auto_push and bool(git_repo_url)), so it readAuto-pushed to remote: Trueeven when every push was being rejected. A failed push now says "Auto-push: FAILED — this doc is NOT on the remote" with the git error attached. - Push errors are no longer swallowed. The bare
exceptinpush_pending()that only wrote to the diagnostic log now records toHealthTrackerand returns the error to the caller. A failinggit commitis reported instead of raising through an uncheckedcheck=True. - gitleaks runs inside the container, on the write path. Flaiwheel's commits are machine-generated and never human-reviewed, so secret scanning now happens in
_push_local_changes()before the commit — not as a per-clone git hook that gets lost on re-clone.MCP_GITLEAKS_MODE=block(default) refuses to commit and reports the findings through the MCP result;
…