# Working in this repo

`ablate` measures whether config files like this one actually do anything. It
is therefore the one repo where an unexamined convention is embarrassing — if a
rule here does not earn its keep, this project's own tooling should be able to
show that.

## Statistics are the product

A bug in this codebase is usually a silent wrong answer, not a crash. Anything
touching `ablate/stats/` or `ablate/ablation/` gets tested against a
constructed case with a known answer, not against its own output.

Never widen a claim beyond what was measured. If a figure appears in a
docstring, a report or the README, there is a test that produces it.

## Never report a number more precisely than the evidence

A missing interval is `None`, never `0`. An unreported cost is `None`, never
`0.0`. An unmeasured token count is "not reported", never zero tokens. Every
one of these has been a real bug here, and each one made something unmeasured
look like a favourable measurement.

When an interval cannot be produced, say why in the same breath.

## Explain why, not what

Docstrings and comments carry the reasoning that is not recoverable from the
code: why this approach over the obvious one, what breaks if it changes, what
was measured to justify it. Do not restate the signature.

Prefer a comment at the decision point over a paragraph in a design doc, and
prefer amending an ADR over quietly contradicting it.

## Errors are messages, not tracebacks

Expected failure conditions raise `AblateError` with a sentence written for the
user, including what to do next. Bare `except:` is never acceptable; catching
broadly requires a comment saying what is being swallowed and why it is safe.

## Style

Python 3.11+, `from __future__ import annotations` at the top of every module.
Type annotations on public functions. `ruff` is the arbiter; line length 100.

Module-level constants that encode a decision get a `#:` comment explaining the
choice, not the value.

## Dependencies

The dependency list is a standing constraint, not a one-time choice
(ADR-0001): `click`, `rich`, `pydantic`, `PyYAML`, `numpy`. No scipy, no
pandas. Adding one requires justifying it against writing the code by hand.

## Tests

`uv run pytest -q` and `uv run ruff check .` must both pass. Tests run against
real git repositories, not mocks — the worktree behaviour is the thing most
worth testing and a mock of it proves nothing.

A test's docstring says which failure mode it guards against. Statistical tests
state the measured figure alongside the assertion, so a threshold is never
mistaken for a target.

## Cost

This tool spends real money on the user's behalf. Any code path that can
trigger an agent run shows the estimate, the run count and the effect size the
design can resolve *before* executing, and honours `--max-spend`. In CI,
exceeding the ceiling is a hard failure, never a silent continue.

## Commits

Explain the reasoning, not the diff. If a change corrects an earlier decision,
say what the earlier decision got wrong. If a bug was found by running
something, say what running it revealed.
