# Contributing to powercut
## The posture, stated plainly
**Bring the argument.** This crate is an oracle: it tells people that their
database lost data, and sometimes it will be wrong. The only way it gets less
wrong is if people who know their storage engine better than I do push back on
it in public.
So: any genuine argument, counterexample, engine report or feature request is
welcome here, and a well-argued improvement **will be accepted**. That includes
one that says a core design decision is wrong — the tier model, the four
verdicts, the `Coverage` asymmetry, the decision to model deletion as durable.
Those are the interesting issues, not an imposition on my time.
Concretely:
- **Open the issue.** "I think powercut mismodels X" is the most useful thing
you can file. You do not need a fix, a benchmark, or a minimal reproduction
to start the conversation — though a runnable reproduction gets it resolved
much faster.
- **Send the PR without asking first.** If it is a good change, it gets merged.
If it needs work, you will get specific comments, not a lecture about scope.
- **Disagree in the open.** If a maintainer decision looks wrong, say so in the
issue rather than forking quietly. Reach and correctness both come from other
people pushing on this.
- **New engine adapters are wanted.** Every engine that gets an adapter makes
the matrix more useful to people who never `cargo add` this crate. If your
engine exposes a storage-backend trait, the adapter is about 25 lines — see
`examples/redb_backend.rs`.
The one thing that will get pushed back on is a change that makes `powercut`
claim more than it measured. Enlarging the durable set, defaulting `Coverage`
to `Complete`, softening `NotMeasured` into a pass, or removing the teeth
control — those go the wrong way, and the wrong way is how a durability harness
becomes worse than nothing.
## "Is your harness wrong or is my database wrong?"
That is the good question and this project exists to answer it. When you file
it, please include:
1. the engine and version, the OS, and the filesystem;
2. the full `Report::to_text()` output — it contains the manifest, every
warrant, the capture's notes and the tier-1/tier-2 asymmetry, which is
usually enough to adjudicate without anyone opening a debugger;
3. whether a teeth control was present and what it did;
4. the `Coverage` your witness declared, and why.
If the answer turns out to be "powercut was wrong", that becomes a test in
`tests/harness_detects_loss.rs` and you get the credit in `CHANGELOG.md`.
## Ground rules for changes
- `cargo test` (including doctests and examples), `cargo clippy --all-targets
-- -D warnings`, `cargo fmt --check` and `cargo doc` must all be clean.
- Every public item has a **compiling** doctest. This is not a style
preference: the docs are the adoption channel, coding agents read them, and
an example that does not compile is worse than no example.
- New behaviour comes with a test that fails without it. Behaviour that could
make the crate over-claim comes with a test in
`tests/harness_detects_loss.rs` specifically.
- MSRV is 1.82 and is tested in CI. Raising it is a minor-version change and
needs a line in `CHANGELOG.md`.
- `#![forbid(unsafe_code)]` stays. If you think a change needs `unsafe`, open
an issue first — so far everything, including the cross-platform durability
primitive, has been reachable through `std`.
- Docs are prose, not bullet soup, and they say what is *not* known as
readily as what is.
## Development
```console
cargo test # unit, integration, doctests
cargo run --example read_fork_close_tear # the flagship matrix
POWERCUT_UPDATE_BASELINE=1 cargo run --example read_fork_close_tear # re-record
POWERCUT_KEEP=1 cargo test # keep scratch dirs for inspection
```
Re-recording is a local, interactive act on purpose: `POWERCUT_UPDATE_BASELINE`
is ignored when `CI` is set, because a baseline that records itself in CI is a
ratchet nobody is holding. Record it, read the diff, commit it.
## Two things to know before you touch the baseline or the dev-dependencies
- **`examples/read-fork-baseline.tsv` was recorded on macOS/APFS.** Linux and
Windows may legitimately produce different verdicts — different filesystem
semantics, a different `fdatasync`. If CI disagrees with the baseline on a
platform, read that as a finding first and a baseline to re-record second.
- **The dev-dependencies are pinned to the lines that build on the 1.82 MSRV**
(`redb = "2"`, `rusqlite = "0.32"`); redb 4 needs Rust 1.89. Nothing in the
library itself needs a newer toolchain. If you want the examples on current
majors, that is a `rust-version` bump and a `CHANGELOG.md` line, and it is a
reasonable thing to propose.
## Licence
By contributing you agree that your contribution is dual-licensed under
MIT OR Apache-2.0, matching the crate.