# Contribution Guide

Sandloom enforces its conventions in CI. The quality gates must be green before any commit; pre-commit hooks run the fast ones automatically. To get a working checkout, start with [Set up the repository](<https://sandloom.dev/docs/contributing/setup/index.md>).

## Branches and owner review

Use a short-lived branch and open a PR against `main`. Final review and manual merge belong to [@Maryarty](<https://github.com/Maryarty>). See [branches and owner review](<https://sandloom.dev/docs/contributing/branches/index.md>) for the workflow, protection configuration and verification procedure.

## CI profiles

Automatic PR checks use Python 3.12 and select checks by changed area. The full compatibility matrix and heavy scenarios run explicitly before release. See [CI profiles and minute budget](<https://sandloom.dev/docs/contributing/ci/index.md>) for the exact split and commands.

The [website publishing guide](<https://sandloom.dev/docs/contributing/website/index.md>) covers the unified site, GitHub Pages and DNS setup separately from [package publishing](<https://sandloom.dev/docs/contributing/publishing/index.md>).

## Quality gates

Run from the repository root:

```
docker compose -f deploy/compose/test.yml up -d --wait   # test Postgres + Redis
uv sync                                                  # pinned dev tools
uv run sandloom test <module>                                 # iterate: module tests, fast suite, gates
uv run sandloom test --full                                   # once, at the end: full suite + gates
```

`sandloom test` runs the tests in parallel, then ruff, ruff format, mypy, `sandloom check`, the architecture tests, `lint-imports` and the contract and route snapshots; `sandloom test --plan` prints the steps. Do not re-run a serial `pytest -q` after each fix. See [Testing](<https://sandloom.dev/docs/toolchain/testing/index.md>) and the [CLI reference](<https://sandloom.dev/docs/reference/cli/index.md>).

`uv sync` installs the `dev` dependency group (ruff, mypy, pytest pinned exactly and locked in `uv.lock`); CI uses `uv sync --frozen` and the hooks `uv run --locked`. Bump a tool by editing its pin, running `uv lock`, and fixing its new findings in the same change.

Lint suppressions are ratcheted — new `# noqa` markers fail unless argued in review, and `BLE001` / bandit markers must carry a reason: see [Lint policy](<https://sandloom.dev/docs/contributing/lint-policy/index.md>).

**Guarantee ledger.** Every guarantee and capability is an entry in `guarantees.yml` with the tests that prove it ([reference](<https://sandloom.dev/docs/reference/guarantees/index.md>)); `tests/architecture/test_guarantee_ledger.py` gates it. Every change that removes or infers a declaration (a marker, a dependency, a policy flag) keeps the ledger green and either shows the same enforcing tests passing (`uv run pytest -m guarantees src`) or justifies each changed enforcing test in the PR; deleting an entry needs a `removed:` record with the reason and the owner's approval.

When GitHub Actions cannot run, verify a branch/PR with the local Docker CI runner before merging: `tools/ci/ci_local.sh <git-ref>` reproduces the CI jobs (act + a fallback runner) on an exported ref — see [Local CI runner](<https://sandloom.dev/docs/contributing/local-ci/index.md>). SBOM, SAST baseline and PR dependency review: [Supply-chain gates](<https://sandloom.dev/docs/contributing/supply-chain/index.md>).

## The one rule that matters

**Modules are atomic and isolated.** A business module may import only the public `sandloom.sdk` modules, generated contract stubs and contract DTOs, and its own package. Never import one business module from another. AST-based architecture checks block violations (`sandloom check`, SANDLOOM-C001 / SANDLOOM-C002). See [Modules](<https://sandloom.dev/docs/modules/index.md>).

## Adding a module — the golden path

```
uv run sandloom add module <snake_case_name>
uv run sandloom test <snake_case_name>
```

The module is discovered: no `SANDLOOM_MODULES` or `testpaths` edit is needed. Then generate its migration in its own chain (`sandloom migrate new`) and replace the example domain. Full walkthrough: [Add a module](<https://sandloom.dev/docs/modules/create/index.md>) and the [tutorial](<https://sandloom.dev/docs/getting-started/tutorial/index.md>).

## Documentation

Docs live in `docs/` and build with Zensical:

```
uv sync --extra docs
uv run --extra docs python tools/docs/docs_site.py serve
uv run --extra docs python tools/docs/docs_site.py build
```

The SDK reference pages are generated from the `sandloom.sdk` modules (their `__all__`, `__beginner__` and docstrings); the configuration reference is generated from the settings model and the error catalogue from the error-code registry. `llms.txt` and processed Markdown copies are generated by Zensical. CI uploads the built site as an artifact. Deployments are separate from documentation builds. When you change the public surface, update the docstrings and run `sandloom docs --write`; don't hand-write a second copy.

Published documentation is English. Historical reviews/design drafts preserve their original language and date; they are evidence, not the current reference. `CHANGELOG.md` is the authoritative implementation changelog; the portal changelog is a reader-facing digest and must be reconciled before tagging a release. Do not infer release readiness from an old benchmark or design note.

## Conventions recap

- Event channels are versioned: `<module>.<event>.v1`.
- API layer: no `sqlalchemy` imports, no ORM models — DTOs + services that use `db()`.
- Reference other modules' entities by plain UUID; no cross-module FKs.
- Never call `db.commit()` / `db.rollback()` — the [Unit of Work](<https://sandloom.dev/docs/modules/database/units/index.md>) owns the boundary.
- See [Versioning &amp; Stability](<https://sandloom.dev/docs/contributing/versioning/index.md>) for the public-API and event-schema compatibility policy.
