# Contributing to Santh
Thanks for reading this. Before you contribute, the short version:
1. **Know what this repo is.** Check the status badge in the README (`experiment`, `prototype`, `alpha`, `beta`, `stable`, `deprecated`). Different statuses have different standards and expectations. Don't file polish PRs against an `experiment`.
2. **One PR, one focused change.** No mixed refactors. No "while I was in there." If you find a second thing to fix, open a second PR.
3. **Every Santh crate must pass `santh-conform check .` before merge.** The CI runs this automatically. If you're writing against master locally, run it yourself: `cargo install santh-conform && santh-conform check .`.
4. **Every public symbol needs a doc comment.** Every error message needs a `Fix:` hint. Every test that fails is a finding, not a bug in the test - fix the code, not the assertion.
5. **No stubs.** A function returning `Ok(vec![])` where it should do work is a lie. A missing feature is honest. If you can't implement it fully, delete it - don't ship a fake.
6. **Supply chain discipline.** Adding a new external dep requires a matching row in `TRUSTED_DEPS.md` at the workspace root, a pin in `[workspace.dependencies]`, and a one-paragraph justification in the commit message. Read [SANTH_STANDARD.md](../SANTH_STANDARD.md) for the full rules.
## The full standard
Read [`SANTH_STANDARD.md`](https://github.com/santh-project/santh-standard/blob/main/SANTH_STANDARD.md). It is the authoritative spec for what a Santh crate must look like. Everything else on this page is a summary.
## How to propose a change
- **Small fix (typo, doc clarification, test addition)**: open a PR directly.
- **Code change to a `beta` or `stable` crate**: open an issue first, link it from the PR. We want to agree on direction before you spend time.
- **New feature, new tool, new crate**: open an issue, describe the problem you're trying to solve, wait for a maintainer response. We reject features that don't map to a real user problem.
- **Change to the Santh standard itself**: PR against `santh-project/santh-standard`. Changes propagate to every Santh repo via shared libs and shared CI workflows. Be deliberate.
## Code style
- Formatted by `rustfmt` with the shared `.rustfmt.toml`. No exceptions.
- Clippy clean with `-D warnings` against the shared `.clippy.toml`. No exceptions.
- `unwrap()`, `expect()`, `todo!()`, `unimplemented!()`, `panic!()` are denied in production code. Use `Result` or delete the code path.
- Functions that return `Option` or `Result` must document both cases. Errors go to `santh-error`'s typed errors with a `Fix:` hint.
- Tests use `proptest` for invariants, `criterion` for benchmarks, and plain `#[test]` for deterministic cases.
## Commit and PR hygiene
- One change, one commit (unless history genuinely requires more). Prefer small atomic commits.
- Commit messages: imperative mood, one-line subject, body explains the "why" not the "what."
- PR titles: short, descriptive, no `[feat]`/`[fix]` prefixes (commit conventions do that work).
- No co-authored-by tags, no AI attribution. This is in the standard for a reason.
- Rebase, don't merge-commit, when updating your PR branch. `git pull --rebase origin main`.
## Testing your change
- Run the full local check before requesting review: `cargo check && cargo clippy -D warnings && cargo test && cargo deny check && santh-conform check .`.
- If the crate has fuzz targets, run `cargo fuzz run <target>` for at least 60 seconds on the relevant targets.
- If you touched an op, run the relevant conform tests in addition to the general suite.
## Reporting security issues
**Do not file a public GitHub issue for security vulnerabilities.** See [`SECURITY.md`](./SECURITY.md) for the disclosure process.
## Code of conduct
Be direct. Be kind. Disagreements about technical substance are welcome; personal attacks are not. If a conversation is going poorly, walk away and come back tomorrow. The full policy is in [`CODE_OF_CONDUCT.md`](./CODE_OF_CONDUCT.md).
## License
Santh crates are dual-licensed under the **Apache License, Version 2.0** and the **MIT license**, at the user's option. The license files live at the repo root:
- [`LICENSE-APACHE`](./LICENSE-APACHE) - Apache License, Version 2.0
- [`LICENSE-MIT`](./LICENSE-MIT) - MIT License
A small number of repos deliberately ship under a different license (e.g. AGPL-3.0 for tools we want copyleft-protected); in that case the repo root carries a single `LICENSE` file and this section is overridden by the repo's own `CONTRIBUTING.md`. If both `LICENSE-APACHE` and `LICENSE-MIT` are present, the dual-license terms below apply.
### Inbound = Outbound
Unless you explicitly state otherwise, any contribution you intentionally submit for inclusion in this project - as defined in section 5 of the Apache License, Version 2.0 - shall be dual-licensed under Apache-2.0 OR MIT, without any additional terms or conditions. You retain copyright in your contributions; you grant Santh and downstream users a license to use them under those terms.
No CLA, no DCO sign-off line, no separate copyright assignment. The act of opening a PR against a Santh repo is the license grant.
### What this means in practice
- You can use any Santh crate under either Apache-2.0 or MIT - pick whichever fits your project.
- Your contributions automatically reach users under the same terms.
- If you contribute code that you do not have the right to license under both Apache-2.0 and MIT (e.g. it is GPL-only, or you do not hold the copyright), do not submit it - say so in the PR and we will discuss.
- If a file or directory carries a different `// SPDX-License-Identifier:` header, that file's header wins (used sparingly for vendored snippets).