* docs(readme): reposition around large MoE models in general, add logo and TOC The front page led with gpt-oss-120b and read as a single-stunt repo. It now leads with what the engine is for: running MoE models past RAM on phones and PCs, on llama.cpp's public API, with every llama.cpp quantization coming for free. The 120B stays as the most extreme proof, not the pitch. Structure follows the usual professional layout: logo (chip-buddy, light and dark variants under docs/assets/logo/), badges, table of contents, a "Why this exists" section that covers all three regimes (far past RAM, just past RAM, and models that barely fit, which streaming keeps inside a chosen budget), and Features grouped the way the app's Settings groups them. Benchmark tables and the evidence sections are unchanged in substance; the test device is phrased generically. * docs(agents): make AGENTS.md the canonical agent guide, CLAUDE.md points to it The agent guide lived in CLAUDE.md with AGENTS.md as a stub pointing at it, which is backwards: AGENTS.md is the cross-tool convention (Codex, Copilot, Cursor and others read it), CLAUDE.md is one tool's name for the same file. The full guide now lives in AGENTS.md and CLAUDE.md is a one-line import. While moving it, the guide gains the release rules that were only tribal knowledge: release APKs come from the release-apk workflow, never a local build; every released feature bumps versionCode/versionName in the same PR; release titles are the bare version; on-device validation before a release. The style section now names the CI clang-format version (18), since a mismatched local formatter passes locally and fails the check. * docs(readme): state per-platform host status honestly Linux is CI-verified, Windows is where the desktop bench ran (but needs CMake directly, not the bash script, and MSVC's Release output path), macOS builds from the same sources but is unvalidated and has no O_DIRECT.
5.8 KiB
Working on BigMoeOnEdge (agent guide)
Read this before making changes. It captures the invariants that keep this project clean.
It follows the AGENTS.md convention, so any coding agent picks it up;
CLAUDE.md just points here.
What this is
A ports-and-adapters engine that streams MoE experts from flash so >RAM models run on
device, built on top of llama.cpp's public API. The whole value proposition is that
we do not fork llama.cpp. See docs/architecture.md and docs/seam.md.
Project map
core/include/bmoe/— ports (interfaces) + config. Pure policy, no llama.cpp include.core/src/io/—platform_io(cross-platform O_DIRECT reads + reserve/commit/evict VM);file_reader(pooled positioned reader, per-consumer O_DIRECT — used by both the expert stream and the dense loader).core/src/moe/—gguf_offsets,arch_registry,expert_stream_source,router_hook;dense_weights(the non-expert weight policy: mmap / warm / anon, plus the residency sensor).core/src/engine/runtime.cpp— composition + greedy generation loop.cli/main.cpp—bmoe-cli; the ONLY place environment variables are read.third_party/llama.cpp— stock upstream submodule.tests/— byte-identity gates.examples/android/— the demo APK.
Build and test
git submodule update --init --recursive
scripts/build-host.sh
cd build && ctest --output-on-failure # byte-identity gates (needs python3 + gguf)
Android CLI: pwsh scripts/build-android.ps1 (needs the NDK), then build the APK in
examples/android.
Hard rules
- Never patch llama.cpp in-tree. Everything goes through the public eval-callback and
public gguf/model APIs. If a change seems to need a llama.cpp edit, stop and discuss —
the fallback is a separate 1-commit fork branch on
Helldez/llama.cpp, never an in-tree diff, and only after agreement. Upgrading llama.cpp must stay a submodule bump. - Repack stays off. The engine loads with
use_mmap=true, use_extra_bufts=false. The streamer rebindstensor->datato the native gguf layout; repacking breaks it. This is load-bearing, not a tunable. - No env vars in the library.
core/never callsgetenv. Config flows throughRunConfig; the CLI resolves any env overrides before building it. - No hardcoding. New architectures are recipe rows in
arch_registry.cpp; expert counts, strides and offsets are discovered at runtime. No model-specific constants in the streaming path. - Gates must pass before merge.
bmoe_moe_gatesproves streamed == resident. If you touch the streamer, the seam, or bump the submodule, run them. - Docs and changelog ship with the change. Every PR updates
CHANGELOG.mdand the docs it invalidates, in the same PR — never as a later sweep. A release gets its own dated## [X.Y.Z] - YYYY-MM-DDsection; nothing accumulates under[Unreleased]. Check in particular: the README benchmark tables and model list, thedocs/architecture.mdlayer map,docs/seam.mdwhen the llama.cpp boundary moves,docs/telemetry.mdwhen CSV columns or theBMOE_*protocol change,docs/roadmap.mdwhen a listed future item ships, andexamples/android/README.mdwhen the catalog, settings or build flow change. Docs that name a file the code no longer has are worse than no docs. - Review exactly what is being published, every time. This repo is public and every push
is permanent record. Before any commit, push, PR or release: run
git status --shortand stage by explicit path only — nevergit add -A/git add .; untracked files in the working tree are not yours to publish. Logs, CSVs and bench evidence get a scan for identifying data (device model codes, local paths, addresses) before landing indocs/; phrase the test device generically. After a squash-merge, verify the landed tree (git ls-tree) before pushing anything else.
Conventions
- Commits: Conventional Commits (
feat:,fix:,docs:,refactor:,test:,build:,ci:,chore:). Author is Helldez only — do NOT add AI co-author or session trailers to commits. - Group commits. One commit = one coherent change. Never a commit for a trivial tweak on its own — fold small fixes, doc touches and follow-ups into the change they belong to. If several small things accumulate, batch them into one commit.
- Language: all code, comments, docs, and commit messages in English.
- Style:
.clang-format(LLVM base, 4-space, 120 col). CI checks with clang-format 18; match that version locally (pip install clang-format==18.*) or formatting that looks clean can still fail the check. - Comments explain why / invariants, not what.
- No milestone codenames (M0…Mn) in docs — describe capabilities thematically.
Releases
- Release APKs come from CI, never from a local build. The
release-apkworkflow runs when a release is published: clean checkout of the tag, NDK build, signed with the stable key from repository secrets, assets attached to the release. Do not hand-upload an APK. - Every released feature bumps the app version:
versionCode+versionNamein the Android app's Gradle config, in the same PR as the change being released, matching the tag. - Release title is the bare version —
vX.Y.Z, no description after it. - Validate on device before releasing. The host gates prove correctness, not speed or app behaviour; a release that changes the engine or the app gets a run on a real phone first.
Where numbers come from
Benchmark figures in the docs are measured (12 GB / UFS 4.x test phone,
Qwen3-30B-A3B-Q4_K_M and friends). Don't invent or round them silently; if you re-measure,
update docs/benchmark-method.md and the README table together. Phrase the test device
generically in anything public.