Fixed
- **`elbow_helper.smoothing.smooth_curve`** (`method="moving_average"`): an
even smoothing window produced an asymmetric kernel, shifting the whole
smoothed curve by half a sample (found by testing: a linear ramp's moving
average, which should reproduce the ramp exactly at every interior point,
came out shifted by +0.5 for even windows). The window is now forced to
the nearest odd size below it, matching the always-odd Gaussian kernel's
centering. Affects `preprocessing.infer_curve_direction` and
`preprocessing._direction_violation_rate`, both of which call
`smooth_curve` with an unrounded, possibly-even window
(`max(5, n // 8)`), so this could bias the auto-detected `curve`/
`direction` and the direction-violation screen on some curve lengths.
- Ralph Eyeball Loop pass on `doc/ELBOW-en.tex`/`doc/LIKELIHOOD-en.tex`:
two real layout defects found and fixed by rendering every table and
`tikzpicture` and inspecting it — a Laplace-approximation figure's curve
labels running into the plotted line at its peak (re-anchored), and two
words hyphenated mid-word inside a narrow table column (rewrapped at the
word boundary). Both documents recompile with zero overfull/underfull/
undefined-reference warnings.
Changed
- CI restructured for speed: `pytest` and `ruff` now run as two separate,
fast jobs on Python 3.12 for every push and pull request, instead of one
job sweeping four Python versions. The full 3.10-3.13 compatibility
matrix still runs, on a weekly schedule and on manual dispatch, so the
version guarantee documented in `README.md`/`LISEZ-MOI.md` holds without
slowing down the default gate.
- Added a `pre-push` git hook (`.githooks/pre-push`, opt in via `git config
core.hooksPath .githooks`, documented in the new `CONTRIBUTING.md`) that
blocks a push on a lint or test failure locally, running `ruff check`,
`ruff format --check`, and the full `pytest` suite before CI ever sees
the commit.
- Consolidated the test suite from 114 tests to 85, with higher coverage
(69 missed lines instead of 76, out of 1793). `test_cli_argparse.py`,
`test_cli_click.py`, `test_api.py`, and `test_mcp_server.py` — 45 tests
that mostly hand-duplicated the same handful of scenarios (a clear knee
accepted, the y-only shorthand, elbow rejecting that shorthand,
diagnostics returning an SVG, the locator's raw payload) once per door —
are now `test_doors.py`: one behavioral test per scenario that drives
every door (argparse CLI, click CLI, HTTP API, MCP) through a small
adapter, so each scenario verifies parity across the whole surface
instead of whichever doors someone remembered to copy it into.
`test_doctests.py`'s five one-per-module tests are now one loop. Two new
tests close a real coverage gap in `pipeline.py`'s bootstrap/null-test
gate-failure paths and its numerical-safety-net exception handler,
previously only 83% covered.