Contributing to Lemon
August 10, 2026 · View on GitHub
Thank you for your interest in contributing. Lemon is a BEAM-native platform for building agents, and the parts that are easiest to extend — channels, engines, storage, memory — are the parts we most want contributions to. This guide gets you from a clone to a merged extension.
If you only read one section, read Your first contribution.
Before You Start
- Run
mix lemon.doctor— ensures your environment is set up correctly. - Read
docs/contributor/public_repo_basics.md— branching, commit style, feature flags. - Read
docs/contributor/ownership.md— code ownership lanes and CODEOWNERS rules.
Prerequisites
- Elixir 1.19 / OTP 28. The umbrella dev build requires Elixir
~> 1.19(pinned to 1.19.5 / OTP 28.5 in.tool-versions, same as CI). The published packages declare a lower~> 1.15floor for consumers, but building this repo needs 1.19 — 15 of the umbrella apps require it, so an older Elixir will fail at compile. - A version manager (
asdformise) is the easy way to get that toolchain; the Quick Start assumes one is installed. Or install Elixir 1.19.5 / OTP 28 directly. - A C toolchain (
cc/gcc+make). The SQLite-backed store compilesexqlite's native NIF from C source, somix compileneeds a working compiler —build-essentialon Debian/Ubuntu,base-develon Arch, Xcode command-line tools on macOS.
A clean checkout to a green mix lemon.doctor is about 2-3 minutes on a warm Hex cache (a
first-ever mix deps.get downloads ~73 packages, so budget a little more cold).
Quick Start
git clone https://github.com/z80dev/lemon.git
cd lemon
asdf install # or `mise install` — toolchain is pinned in .tool-versions (Elixir 1.19.5 / OTP 28.5, same as CI)
mix deps.get && mix compile
mix lemon.doctor
Full setup: docs/user-guide/setup.md
Development
scripts/test fast # compile with warnings as errors + ExUnit excluding integration
scripts/test path apps/lemon_core/test
scripts/test quality # lint + architecture boundaries + doc freshness
scripts/test quality runs the same mix lemon.quality lane CI runs, and it is
the gate most PRs need to pass. See docs/testing.md for the
canonical local test lanes and how they map to CI.
Your first contribution
The best first contribution to Lemon is a new channel adapter — a Slack adapter, an SMS provider, an internal chat bridge. It touches one well-defined behaviour, it ships with a ready-made compliance suite, and it can live in this repo or in your own. Here is the whole on-ramp:
-
Scaffold a project with the generator, so you have a running agent to attach a channel to:
cd installer && MIX_ENV=prod mix archive.build mix archive.install lemon_new-0.1.0.ez mix lemon.new my_agent -
Follow the guide. Add a channel walks from the console loop the generator gives you to a registered
LemonChannels.Plugin, callback by callback. Its companions — Build your first agent, Add a tool, and Persist memory — cover the neighbouring extension points. -
Prove it with the contract kit (below). A channel adapter that passes
LemonPlatformTest.PluginCaseis a channel adapter we can review quickly and merge with confidence.
You do not have to contribute the adapter back — the X integration lives in its own package and registers itself at boot, and yours can too. But if it is general-purpose, open a PR; it is exactly the contribution this project is shaped to receive.
Extension points
Every extension point is a behaviour with a published compliance suite in the
lemon_platform_test kit. Implement the behaviour, then run the matching case
against your module — the suite is the specification in executable form, so a
green run is most of what a reviewer needs.
| You want to add | Implement | Compliance suite | Guide |
|---|---|---|---|
| A channel (Slack, SMS, chat bridge) | LemonChannels.Plugin | LemonPlatformTest.PluginCase | Add a channel |
| A coding/agent engine | LemonGateway.Engine | LemonPlatformTest.EngineCase | — |
| A storage backend | LemonCore.Store.Backend | LemonPlatformTest.BackendCase | — |
| A memory provider | LemonMemory.Provider | LemonPlatformTest.ProviderCase | Persist memory |
Each behaviour's moduledoc is the contract in prose; each case's moduledoc
explains what it exercises and why. The built-in implementations are the worked
examples — Telegram/Discord/email for channels, EtsBackend/SqliteBackend for
storage, LemonMemory.Providers.Local for memory.
Running the contract kit
Add lemon_platform_test as a test-only dependency and write a one-file test
that hands your module to the matching case. The suites are parameterized use
macros; the options tell the suite what to run and which probes are safe.
# test/my_agent/slack_channel_test.exs
defmodule MyAgent.SlackChannelComplianceTest do
use LemonPlatformTest.PluginCase,
async: false,
adapter: MyAgent.SlackChannel,
deliver_probe: {__MODULE__, :unsupported_payload},
inbound_fixtures: {__MODULE__, :updates}
# `:deliver_probe` has no default on purpose: only you know which payload
# cannot reach a real user. A kind your adapter does not support is the right
# choice — a compliance suite that posts to a live workspace is worse than none.
def unsupported_payload(_context), do: # ... an OutboundPayload your adapter rejects
def updates(_context), do: # ... raw inbound fixtures your normalize_inbound/1 accepts
end
mix test test/my_agent/slack_channel_test.exs
The other suites follow the same shape:
use LemonPlatformTest.EngineCase, async: true, engine: MyApp.MyEngine, registry: false
use LemonPlatformTest.BackendCase, async: true, backend: MyApp.MyBackend
use LemonPlatformTest.ProviderCase, async: false, provider: MyApp.MyProvider
Look at apps/lemon_platform_test/test/compliance/ for a runnable example of
each — those are the platform's own implementations held to the same suite you
will run.
Commit Style (Conventional Commits)
<type>(<scope>): <short description>
Types: feat, fix, docs, refactor, test, chore
Scope: app name or domain (lemon_core, lemon_skills, config, etc.)
Feature Flags
All new non-trivial features must be gated behind a flag in [features].
Use LemonCore.Config.Features.enabled?/2 — not System.get_env.
Skills and Generated Artifacts
- Skills live in
~/.lemon/agent/skills/or<project>/.lemon/skills/ - Auto-generated skill drafts must go through human review before promotion
- Do not commit skill draft files or personal
~/.lemon/content
Pull Requests
- Branch from
main; branch name:<type>/<short-description> - PRs require approval from the CODEOWNERS of affected files
- Cross-cutting changes (
mix.exs, shared schemas) require@z80sign-off mix lemon.qualitymust be green (lint + architecture boundaries + doc freshness)- Register any new docs files under
docs/indocs/catalog.exs - Changed a published package's
lib/? Add an entry to that package'sCHANGELOG.mdin the same PR — the change is visible to third parties who installed it from Hex. A CI job annotates PRs that miss this (advisory, perdocs/platform-split.md§4.4); it does not block the merge, but reviewers do.
The pull request template has the full checklist.
Reporting Security Issues
See SECURITY.md.
License
By contributing, you agree your contributions will be licensed under the MIT License.