Skip to content

Contributing ​

Pull requests are welcome. This page gets a checkout of Humanize ready to change: installed, checked the way CI checks it, and with the rules every change is held to. Bugs and feature requests go through the issue forms; for a large change, open a feature request first.

How a pull request is proposed and reviewed is in CONTRIBUTING.md, and who decides, and how to become a reviewer or maintainer, in the organization's GOVERNANCE.md. Everyone taking part follows the Code of Conduct.

First time here?

Your first patch takes one small change from clone to pull request, with every command written out.

Before you start

git and uv. Nothing else: uv sync brings Python with it.

Set up ​

sh
git clone https://github.com/humanfia/humanize.git
cd humanize
uv sync                    # ①
uv run pre-commit install  # ②
  1. uv sync makes the project's environment in .venv, exactly as uv.lock pins it, Python included.
  2. pre-commit install checks each commit before it is made, with the hooks CI runs.

Run tools through uv run, not uvx, so you get the versions uv.lock pins. To check the setup, run one package's unit tests:

sh
uv run pytest tests/unit/flows

The checks ​

sh
uv run pre-commit run --all-files   # format, lint, types: seconds
uv run pytest                       # unit and integration: minutes
sh
uv run pytest tests/unit/<package>  # the fast loop
sh
uv run pytest tests/system/test_flows_ralph.py   # real agents, real tokens

Both commands under Before you push must pass, and they are what CI runs. uv run pytest runs tests/unit and tests/integration, and leaves tests/system out.

Your machineCI
the pre-commit hooks✓✓ static, every push and pull request
tests/unit/✓✓ each package alone, when it changed, on Linux and macOS
tests/integration/✓✓ each topic alone, when what it runs changed, on Linux and macOS
tests/system/✓ by hand, a file at a time, when your change needs itnever
the macOS app✓ on a Mac, when you change desktop/✓ desktop, on macOS, when desktop/ changed

CI has every job, and how to read a run.

The macOS app ​

desktop/ is Humanize Desktop, the macOS app: a window on hmz web's page, in Rust with Tauri, held to its spec. Working on it takes a Mac, Rust, Node and pnpm. Built from main, its version is 0.0.0, and it installs nothing: pnpm dev opens it against the hmz on your PATH, and HUMANIZE_HMZ names another, such as this checkout's:

sh
cd desktop
pnpm install
HUMANIZE_HMZ="$PWD/../.venv/bin/hmz" pnpm dev

To try how a release installs hmz, build it with the version of a release on PyPI whose hmz web takes --attached: pnpm tauri build --bundles app --config '{"version":"<it>"}'.

Before you push a change to it, run what CI's desktop job runs:

sh
cd desktop/src-tauri
cargo fmt --check
cargo clippy --all-targets -- -D warnings
cargo test
cd .. && pnpm tauri build --debug --bundles app,dmg

The disk image a release attaches, for both architectures, is pnpm tauri build --target universal-apple-darwin --bundles dmg, once rustup target add aarch64-apple-darwin x86_64-apple-darwin has run. Finder lays it out, so the first build asks to let your terminal control Finder. Its background is src-tauri/dmg/background.svg, with a plate on opening the app unsigned that a release signed with a Developer ID leaves off; after changing it, pnpm dmg-background remakes background.tiff and background-signed.tiff, the two the bundler takes.

Where a test goes ​

A test goes in exactly one of three directories, by how much of Humanize it puts together. Its directory is what decides when it runs: no marker to write.

HoldsRules
tests/unit/<package>/ CIOne package of src/hmz/ alone, in the directory named for it: cli, coganchor, daemon, flows, runtime, sdk or tuiOnly the package's public names, never a _private name or module. Every other hmz package mocked. No subprocess, socket or network. A job of its own must finish in 3 minutes
tests/integration/test_<topic>_*.py CIPackages wired together against fakes this repository wrote: a stand-in CLI, a fake app server, a loopback socket, the mock LLM serviceFlat, no subdirectories. The topic is one of core, agents, tui, daemon, anchor and web, a job each, which must finish in 10 minutes
tests/system/ youReal agents on real tasks: an installed coding agent CLI, signed in, working on tests/system/sample/, with docker or ssh where the test needs them. And in tests/system/web/, hmz web's page drawn and clicked in real browsers through Playwright, on the stand-ins integration uses5 to 30 minutes and real tokens a test; minutes and no tokens in web/. Never in CI

ruff's SLF001 and pyright's reportPrivateUsage fail a test that reaches a private name. Mock another package where the test imports it, so the test holds this package to what it promises rather than to what the others happen to do.

Run a system test only when your change touches the path it covers, such as a backend's driver, a built-in flow or a machine, and before a release. Never all of them on every change: they take hours together and spend real money. Name the file, as under When a system test covers it above; each is named test_<area>_<what>.py, for flows, harness, machines or tui. A test skips, saying why, where what it needs is not on this machine: a CLI, its sign-in, docker, ssh.

Never copy a sign-in into a test's home

Codex's ChatGPT login and Claude Code's subscription login renew themselves, and a renewal cancels the token the other copies hold: a container or temporary home signed in with a copy of ~/.codex/auth.json or ~/.claude/.credentials.json signs your machine out. A test that needs one runs the CLI as local, or mounts the CLI's own home where it is (--volume=DIR:DIR). The run fails if any file under pytest's temporary directories holds one of those refresh tokens (_copies_no_sign_in in tests/conftest.py).

What the code is held to ​

  • pyright in strict mode, over src and tests. # type: ignore is switched off: a suppression names a pyright rule.
  • ruff with every rule on, less the ones pyproject.toml gives a written reason for.
  • Google-style docstrings, and type annotations everywhere.
  • Popular, well-maintained libraries before a custom implementation.
  • Each layer imports only what the layering table gives it. See Architecture.
  • specs/ is the contract. Change the code to match a SPEC. Do not edit one unless you were asked to.
  • A change to how flows run also keeps the flows humanfia/flowverse lists working.
  • A change to behaviour updates the docs in the same pull request. Working on these docs has the rules.

Commits ​

Conventional Commits: fix(agents): …, docs(contributing): …, with a ! before the colon for a breaking change. A change and its tests go in one commit.

Next steps ​

ToRead
make a first, small change end to endYour first patch
add or change a page of these docsAdd a page to these docs
find where a change goes, or add a backend or a commandArchitecture
read what CI ran on a change, and whyCI
hold a page to the rulesWorking on these docs

Released under the Apache-2.0 licence.