CONTRIBUTING¶
Dev setup¶
git clone https://github.com/FirstCastSolutions423/carrel.git ~/projects/carrel && cd ~/projects/carrel
uv sync # creates .venv from pyproject.toml + uv.lock
uv run carrel doctor # which optional binaries you have + apt install hints
uv run pytest # 501 tests; binary-gated tests skip (with reason) when a binary is absent
Python ≥3.12, managed by uv. Optional external binaries
(pandoc, tesseract, ocrmypdf, weasyprint, pdftotext, pdftoppm, espeak-ng, ffmpeg, gpg,
exiftool) unlock more tests and commands — carrel doctor lists them all; nothing is
required to run the core suite.
Coding standards¶
The binding rules live in CLAUDE.md (repo root) and
ARCHITECTURE.md §Global contracts. The short version:
- Type hints on public functions,
pathlib.Pathover strings, f-strings, no global state. CLI framework is click — not argparse. - Every command: working
--help,--jsonwhen output is data (one JSON object/array on stdout, nothing else), ≥1 test, graceful degradation when an optional binary is missing. - No stubs, no TODOs in shipped commands. Can't finish it? Cut it and document the cut in FEATURES.md.
- Product name comes from
/product.json— never hardcode "carrel" in code or generated output (docs prose is fine).
The adapter-layer rule (most-enforced rule in review)¶
External binaries are called only through src/carrel/core/adapters.py
(have() / require() / run(), one ADAPTERS registry that doctor reads).
Command modules never import subprocess. MissingDependencyError is caught centrally
in cli.py and becomes an actionable stderr message + install hint, exit 3. The
integration reviewer greps for violations; the one documented exception is watch
running user-supplied shell actions.
Exit codes (memorize these)¶
| code | meaning |
|---|---|
| 0 | success |
| 1 | general/unexpected error (stderr message; traceback only with --debug) |
| 2 | bad usage/arguments |
| 3 | missing optional dependency (message names the binary + install hint) |
| 4 | input file missing / unreadable / unsupported type |
| 5 | empty result with --fail-empty |
How to add a command¶
- Spec first. Add/extend a spec in
specs/with an Owns line (your exact write boundary) and an Acceptance section. - Module. Create
src/carrel/commands/<name>.pyexporting a click command namedcmd. Use the core library:core.adapters(binaries),core.output.emit/fail/ExitCode(output + errors),core.filetypes,core.textextract,core.db. - Register it. Add
"<cli-name>": "<module_name>"to theCOMMANDSdict at the top ofsrc/carrel/cli.py. Commands are lazy-imported — a broken import must only break its own command, so keep import-time work at zero. - Tests.
tests/test_<name>.py, driving the real CLI on fixtures fromtests/fixtures/(regenerate viatests/fixtures/generate.py; never hand-edit binaries). Tests needing an optional binary use theneeds()skip helper from conftest. Run them:uv run pytest tests/test_<name>.py -q. - Verify by hand before claiming done:
uv run carrel <name> --help, one real fixture invocation,--jsonpiped throughpython -m json.tool, and the failure paths (missing file → 4; missing binary → 3). - Optionally wrap it in a plugin command — see PLUGIN_AUTHORING.md.
Commit convention¶
Branch main. History is checkpointed, not noisy:
phase(N): ...— phase-gate commits (plan, test report, finalize).wave(N): ...— a verified wave of parallel module work (see AGENTS.md for how waves ran).
For ordinary contributions a plain imperative subject is fine; group your work into
one coherent commit per logical change. Never commit generated junk (.gitignore);
generated fixtures are committed.
PR expectations¶
uv run pytestfully green — includingtests/test_marketplace.pyif you touchedplugins/(andclaude plugin validate .passing when you have the CLI).- No stubs/TODO/placeholder code; cut-and-document instead.
- Exit codes and
--jsonshapes match the contracts above. - New/changed flags reflected in any plugin command markdown that wraps them (doc drift is a review finding — it has happened).
- Claims verified by execution: paste real command output in the PR description, the way the builder agents do in their reports.