# v0.24.0 — Cutover guide

> **Audience:** consumer agents (and humans relaying for them)
> upgrading to `mgf-common` 0.24.0.
> **What this is:** the canonical migration document for this
> release. Per [`RELEASING.md`](../standards/RELEASING.md) **REL-09**.

This is a **documentation-only release**. The four-layer test
pyramid pattern that `mgf.common.process` proved out across
v0.21 → v0.22 → v0.23 is now codified as a standard so other
projects adopt the same shape.

**No Python-surface change.** No behaviour change. Version bump
is mechanical so consumers reading `mgf-common standards testing`
see the new rules in their installed wheel.

---

## At-a-glance

| What landed | Severity | What it covers |
|---|---|---|
| **TS-19** — registered markers | new rule | `pyproject.toml::markers` MUST declare every pytest marker the project uses. With `--strict-markers` (TS-05), typos can't silently disable a layer. |
| **TS-20** — four-layer directory shape | new rule | `tests/unit/` + `tests/integration/` + `tests/e2e/` + `tests/smoke/`. Each non-unit dir MAY apply its layer marker via top-level `pytestmark`. |
| **TS-21** — smoke layer required for libraries | new rule | Every published library MUST have `tests/smoke/` running in <100ms. Verifies the wheel imports + happy-path of public functions. CLI/desktop apps without a wheel surface MAY skip. |
| **`TESTING.md` §13** — operational layer-marker pattern | new section (~260 lines) | Directory shape, marker registration template, smoke-layer minimum-viable example, e2e bootstrap-fixture pattern, integration cross-module pattern, decision tree, mgf-common's own per-layer counts as a reference. |
| **`docs/internal/TEST_COVERAGE.md`** | new doc | Reference template showing what mgf-common tests at each layer + per-module coverage map + coverage gaps as a roadmap. |

**Pin bump:** `mgf-common = ">=0.24,<0.25"` per the 0.x window
discipline.

**Public-Python-surface change:** none.

**Behaviour change:** none.

**No deprecation cycles introduced.**

---

## What `TESTING.md` §13 says

The operational pattern (excerpt of the new §13.1 table):

| Layer | Directory | Marker | Runtime budget | What it catches |
|---|---|---|---|---|
| **smoke** | `tests/smoke/` | `@pytest.mark.smoke` | < 100 ms total | Packaging breaks: missing `__init__.py` import, typo in `__all__`, missing optional-extra dependency, broken wheel install |
| **unit** | `tests/unit/` (mirrors `src/`) | (none — default) | < 10 ms per test | Per-component logic regressions; isolated, no I/O |
| **integration** | `tests/integration/` | `@pytest.mark.integration` | < 5 s per test | Cross-module wiring; multiple subsystems composed; in-process |
| **e2e** | `tests/e2e/` | `@pytest.mark.e2e` | < 30 s per test | Full bootstrap-to-output chains; verifies the contract consumers exercise |

Each layer catches a different failure class. Adding them in
order (unit first, then integration / e2e / smoke as the
surface stabilises) avoids churning tests as the design
evolves.

§13 also includes the canonical marker registration:

```toml
[tool.pytest.ini_options]
addopts = ["--strict-markers", "--strict-config"]
markers = [
    "smoke: minimal post-install sanity (fast; no fixtures)",
    "e2e: end-to-end test exercising bootstrap + multiple subsystems",
    "integration: cross-module integration test (slower; may launch real subprocesses)",
    "slow: individually-slow test (>1s); excluded from default fast suite",
]
```

---

## Per-consumer migration notes

### VManager (Qt desktop)

**Recommended adoption:** moderate. As a desktop app without a
wheel surface (you ship via direct install / packaging), TS-21
explicitly notes you MAY skip the smoke layer. But TS-20 still
applies — you'd benefit from organising your existing tests
into the four directories so the layer markers can isolate
slow integration tests from fast unit tests.

Concrete step: register `markers = [...]` in your
`pyproject.toml::[tool.pytest.ini_options]` so any future use
of `@pytest.mark.integration` on the libvirt-touching tests
doesn't silently fail under `--strict-markers`.

Pin: `mgf-common = ">=0.24,<0.25"`.

### inthewords (Django web)

**Recommended adoption:** strong. Web apps benefit most from the
four-layer split — Django's `pytest --reuse-db` is fast for
unit tests but expensive on every test, and the smoke layer
gives you a sub-100ms "did the build pass?" pre-flight before
the heavier Django test runs.

Concrete steps:

1. Register markers in `pyproject.toml`.
2. Move tests that exercise multi-subsystem flows (Django +
   Celery + Redis) from `tests/unit/` to `tests/integration/`
   and apply `@pytest.mark.integration`.
3. The bootstrap-loaded e2e tests (testing the full request →
   middleware → exception translation → JSON-response chain)
   move to `tests/e2e/` with `@pytest.mark.e2e`.
4. Add a `tests/smoke/test_inthewords_smoke.py` checking
   imports + the four-gate Django startup.

The mgf-common §13.7 e2e example uses
`mgf.common.bootstrap_for_test`; you'd substitute your project's
equivalent (Django's `setup()` + `LOGGING` dictConfig).

Pin: `mgf-common = ">=0.24,<0.25"`.

### PlasmaMapper (FastAPI scientific platform)

**Recommended adoption:** moderate. Per your "small consumer"
scope discipline, full pyramid adoption may be more than your
project needs. Suggested minimum:

1. Register markers in `pyproject.toml`.
2. Add a `tests/smoke/test_plasmamapper_smoke.py` (5-10 minutes
   of work; per TS-21 every library needs one).
3. The current pytest fixtures that spin up Postgres for
   integration testing already match the integration-layer
   pattern — apply `@pytest.mark.integration` so they can be
   excluded from the fast smoke run.

If/when an e2e suite is worth writing (full FastAPI + DB +
spectral analysis chain), the pattern in mgf-common's
`tests/e2e/test_process_redaction_e2e.py` is the template.

Pin: `mgf-common = ">=0.24,<0.25"`.

---

## How to read the new content from your installed wheel

The standards docs ship via
`[tool.hatch.build.targets.wheel.force-include]`. Once you've
upgraded:

```bash
mgf-common standards testing
```

…surfaces the full updated `TESTING.md`, including the new TS-19
/ TS-20 / TS-21 rules and the §13 layer-markers section.

The `docs/internal/TEST_COVERAGE.md` doc lives in `docs/internal/`
which is NOT included in the wheel by design (internal artifacts
stay in the repo). Read it via the Codeberg source view:
<https://codeberg.org/magogi-admin/mgf_common/src/branch/master/docs/internal/TEST_COVERAGE.md>.

---

## Engineering note

This release closes a loop the recent rounds opened. The pattern
emerged organically:

- **v0.21.0** shipped `mgf.common.process` with 64 unit tests
  (the audit-first design).
- **v0.22.0** added 11 polish unit tests (race-condition
  hardening).
- **v0.23.0** added 29 tests across smoke/e2e/integration
  layers, proving the pyramid shape worked for at least one
  module.
- **v0.24.0** now codifies the pattern as a standard so other
  projects adopt it.

The pattern this names: **a feature first proves itself in
unit tests; once stable, polish rounds add the higher pyramid
layers; once those prove their value across at least one
module, the pattern gets promoted to a standard.** The
v0.20.0 documented-non-adoption principle had the same arc
(framing implicit across multiple carve-outs → named in a
release that codified it). The cornerstone-library framing
depends on this loop being repeatable.
