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.
Branches and owner review¶
Use a short-lived branch and open a PR against main. Final review and manual
merge belong to @Maryarty. See
branches and owner review 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 for the exact split and commands.
The website publishing guide covers the unified site, GitHub Pages and DNS setup separately from package publishing.
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 and the
CLI reference.
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.
Guarantee ledger. Every guarantee and capability is an entry in
guarantees.yml with the tests that prove it
(reference); 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. SBOM, SAST baseline and PR dependency review:
Supply-chain gates.
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.
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 and
the tutorial.
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
sqlalchemyimports, no ORM models — DTOs + services that usedb(). - Reference other modules' entities by plain UUID; no cross-module FKs.
- Never call
db.commit()/db.rollback()— the Unit of Work owns the boundary. - See Versioning & Stability for the public-API and event-schema compatibility policy.