Contributing code
How to set up a local Flux development environment, run the tests, and land a pull request.
Flux is open source under Apache 2.0. The framework lives at github.com/edurdias/flux. Bug fixes, new examples, additional LLM providers, and docs improvements are the easiest contributions to land. Architectural changes and breaking changes need a design conversation first — open an issue before writing code.
This page covers the framework repository (flux-core on PyPI). For documentation changes on this site, see Contributing docs. For new examples in the examples/ folder, see Contributing examples.
Repo layout
flux/
├── flux/ # the framework package
│ ├── tasks/ # built-in tasks + AI agent harness
│ ├── domain/ # ExecutionContext, events, schedule, resource_request
│ ├── security/ # auth providers, encryption, bootstrap tokens
│ ├── observability/# OpenTelemetry + Prometheus integration
│ ├── agents/ # first-class AI agent system
│ └── cli.py # Click command groups
├── examples/ # runnable workflow examples
├── tests/
│ ├── flux/ # unit tests
│ ├── e2e/ # end-to-end (spawn server + worker subprocesses)
│ ├── examples/ # validate every file under examples/
│ └── security/ # auth and permissions
├── docs/ # legacy MkDocs site (the new site lives in a separate repo)
├── flux.toml # default operator config (ships with the repo)
├── pyproject.toml # Poetry, ruff, pytest, poe configuration
└── CLAUDE.md # the canonical architecture and command reference
The framework is one Python package and three runtime entry points: flux start server, flux start worker, and flux start mcp. Most contributions touch one of flux/server.py (HTTP routes), flux/worker.py (claim loop), flux/tasks/ (built-in primitives), or flux/cli.py (Click commands).
flux-src/CLAUDE.md is the most useful single file in the repo to read before making changes. It documents the two execution paths, the server-worker handshake, the configuration layering, and the known gotchas (module-cache collisions, base64-encoded workflow source, custom _FluxModule class). AGENT.md documents the workflow conventions — branch off main, bump version, run pre-commit before pushing.
Fork, clone, install
git clone git@github.com:<your-fork>/flux.git
cd flux
# Python 3.12+ is required (CI tests 3.12-3.14); pyenv or asdf works.
pyenv install 3.12
pyenv local 3.12
# Poetry manages dependencies. Do not introduce uv — it is excluded on purpose.
pip install poetry
poetry install
poetry install --extras observability # what CI installs for lint + unit
poetry install --extras postgresql # adds psycopg (v3) if you touch PG code
poetry install --extras ai # Ollama / OpenAI / Anthropic / Gemini providers
poetry install gives you a virtual environment with the dev tools — ruff, pyright, pytest, pre-commit, and the AI provider SDKs as dev-only deps. The base install is SQLite-only with no telemetry; opt into extras when your work needs them.
After poetry install, install the pre-commit hooks:
poetry run pre-commit install
This wires the lint hooks to run on every commit locally — the same checks CI runs.
Running the tests
The test suite has four layers. Run them in this order when developing:
# Unit tests (the layer most contributions extend)
poetry run pytest tests/ --ignore=tests/e2e
# A single file
poetry run pytest tests/flux/test_worker.py
# A single test
poetry run pytest tests/flux/test_worker.py::TestWorker::test_start_registers
# End-to-end (spawns real server + worker subprocesses)
poetry run pytest tests/e2e/ -m "not ollama" -v
# PostgreSQL-tagged tests (requires Docker)
make test-postgresql
The E2E suite takes about 10 minutes locally and uses pytest’s conftest.py to spawn flux start server and flux start worker as subprocesses on a free port. Set FLUX_E2E_KEEP_LOGS=1 if a test fails and you want the server and worker logs preserved for inspection.
Tests tagged ollama require a local Ollama install with a compatible model loaded; they are auto-skipped when ollama list fails. The postgresql marker requires the test Postgres container — make test-postgresql brings it up, runs the tests, and tears it down.
When a PR touches behavior that crosses the server-worker boundary, the CLI, or the scheduler, add an E2E test. The patterns are in tests/e2e/test_*.py.
Linting and formatting
Flux uses Ruff (100-char line length, double quotes) as the formatter of record and Pyright for type checking. Pre-commit is the gate that runs both.
# Full sweep (matches CI)
poetry run pre-commit run --all-files
# Single file or path
poetry run pre-commit run --files flux/worker.py
Do not run ruff or pyright directly during a contribution — the pre-commit hooks set the right arguments and the right ignore lists, and CI runs pre-commit run --all-files, not the underlying tools. Skipping pre-commit with --no-verify is not allowed; if a hook fails, fix the underlying issue or open a separate PR to adjust .pre-commit-config.yaml.
Pylint, Pyflakes, Bandit, and Prospector are available as dev dependencies for deeper analysis, run as standalone tools, but they are advisory; pre-commit and Pyright are the blocking checks.
Pull request workflow
1. Branch off main git checkout -b feature/<short-name> or fix/<short-name>
2. Make the change keep it small and focused; one logical change per PR
3. Add tests unit minimum; E2E if you crossed server/worker/CLI
4. Bump pyproject.toml patch for fixes, minor for features (CI enforces)
5. Run pre-commit poetry run pre-commit run --all-files
6. Run unit tests poetry run pytest tests/ --ignore=tests/e2e
7. Run e2e tests poetry run pytest tests/e2e/ -m "not ollama"
8. Open the PR describe the why, not the what
9. Respond to review commit, push, then reply — do not reply with promises
The version bump in step 4 is the most common reason PRs fail CI on first push. The version-check job in .github/workflows/pull-request.yml reads the target branch’s pyproject.toml version and fails the PR if your branch’s version is not strictly greater. Patch bump for a bug fix (0.56.0 → 0.56.1), minor bump for a feature (0.56.0 → 0.57.0).
If your change touches the ORM models in flux/models.py, ship an Alembic migration with it — generate one with poetry run alembic -c /dev/null --raiseerr revision --autogenerate -m "describe change" (or hand-write it under flux/migrations/versions/), and keep it static so old revisions behave consistently regardless of later model changes. The schema is migration-managed; there is no create_all fallback.
PR titles follow no rigid convention but are read by humans — fix: scheduler double-fire when two replicas run is clearer than Update scheduler.py. Describe the why in the PR body; the diff already shows the what.
Do not add Co-Authored-By: lines on the Flux framework repo. The docs site repo allows them; the framework repo does not.
What lands easily
- Bug fixes with a failing test that the fix flips green. The smaller the diff, the faster the review.
- New examples in
examples/that demonstrate a workflow pattern the existing examples do not cover. See Contributing examples. - New LLM providers for the agent harness. Each provider is a
(factory, formatter)pair influx/tasks/ai/<provider>.pyagainst the ABC informatter.py. The existing Ollama, OpenAI, Anthropic, and Gemini providers are the templates. - New built-in tasks in
flux/tasks/builtins.py. Re-export throughflux/tasks/__init__.pyand add tests undertests/flux/tasks/. - Documentation fixes on the legacy MkDocs site in
docs/. The new Astro site lives in a separate repo — see Contributing docs.
What needs discussion first
- New decorator options on
@taskor@workflow. Each option has to be plumbed through_with_options, the persistence model, the event encoders, and the docs. Open an issue to confirm the shape before writing code. - New event types or execution states. The replay determinism tests in
tests/examples/test_determinism.pyguard the event log shape; adding to it without a design discussion will break replays of existing executions. - Breaking changes to the SDK, the CLI, or the REST API. Flux is pre-1.0 but still has users running workflows in production. Breaking changes need a versioning conversation — major bump, migration note, and a deprecation period if practical.
- Architectural changes — database schema rewrites, swapping the SSE transport for WebSockets, replacing Pydantic with attrs. Open an issue.
- New top-level dependencies. The dependency tree is intentionally narrow. New runtime deps need justification; new dev deps need to clear pre-commit.
Where to ask
- GitHub Issues — github.com/edurdias/flux/issues for bug reports, enhancement requests, design proposals, and “is this the right way to do X” before opening a PR.
- Security — see Support and community for the disclosure process.
When in doubt, open an issue before opening a PR. Reviewing a small issue takes ten minutes; reviewing a large PR that took the wrong approach takes a week.