powercut 0.1.1

Prove an acknowledged write survives a power cut or process crash, in cargo test. A database-agnostic crash-injection harness that derives the durable set from the fsyncs that actually happened.
Documentation
# 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.