# Federation test-supervisor rollout tracker

**Status:** ✅ COMPLETE 2026-05-26 — extraction + Phase C migration done.
**Goal:** all six federation siblings adopt the supervisor via copy-paste, then extract to `mgf-test-supervisor` as a published sibling library. After extraction, the six adopted siblings (+ mgf-common itself) migrate from copy-paste to `pip install mgf-test-supervisor`.

**Where the supervisor lives now:** [`mgf-test-supervisor`](https://codeberg.org/magogi-admin/mgf-test-supervisor) v0.1.0 (Codeberg; PyPI publish deferred). Installable via `git+ssh` source in dev extras; will move to PyPI plain dep in a future cut. All 7 federation siblings now consume the published library; no in-tree `scripts/_mtest/` copies remain anywhere.
**Recipe:** [`docs/recipes/test-supervisor-adoption.md`](../recipes/test-supervisor-adoption.md).
**Standards:** TS-22..TS-27 in [`docs/standards/TESTING.md`](../standards/TESTING.md) §19.

This tracker is the **single source of truth** for the rollout. Every sibling adoption updates this file. Lessons learned, recipe improvements, supervisor improvements, and FEEDBACK papers all log here.

---

## Per-sibling status

| Order | Sibling | Role | Status | PR / commit | Notes |
|---|---|---|---|---|---|
| #0 | **mgf-hrb** | originator | ✅ ships supervisor | n/a | Where it all started. |
| #1 | **mgf-common** | first adopter | ✅ adopted | `35ac498` + `9a0aef0` | The cornerstone adopted it. CI shows green; supervisor self-checks. |
| #2 | **mgf-fastapi** | sibling-#2 adopter | ✅ adopted 2026-05-26 | (this commit) | 153 tests pass; `--self-check` PASS; `--tier 0` selects 1 (smoke); surfaced PAPER-01 (run_test_app cleanup). |
| #3 | **mgf-django** | sibling-#3 adopter | ✅ adopted 2026-05-26 | `430e07f` | 55 tests pass; no debt surfaced; clean adoption. |
| #4 | **mgf-sqlalchemy** | sibling-#4 adopter | ✅ adopted 2026-05-26 | `b8b282b` | 102 tests pass; no debt (the DP-13 fix from earlier this session paid off — supervisor + fix together close the PAPER-60 cascade class). |
| #5 | **mgf-alembic** | sibling-#5 adopter | ✅ adopted 2026-05-26 | `2c32a35` | 42 tests pass; no debt. |
| #6 | **mgf-http** | sibling-#6 adopter | ✅ adopted 2026-05-26 | `96623b0` | 103 tests pass; no debt. |
| #7 | **mgf-apiprobe** | sibling-#7 adopter (final) | ✅ adopted 2026-05-26 | `fb11a68` | 849 tests pass; **surfaced supervisor bug** (`--no-cov` failed when pytest-cov not installed); fix landed in supervisor.py + propagated to all 6 siblings. |

**All 7 adopted.** Extraction to `mgf-test-supervisor` as published sibling library is now triggered (per TESTING.md §19.6 + PROPOSAL §4).

---

## Phase A — pre-extraction hardening (2026-05-26)

Lessons L1-L7 from the 7-sibling adoption funnel surfaced one real supervisor bug (L6) + one quieter UX gap (collection failures returning empty silently). Both fixed + pinned by tests BEFORE extraction so the published library's v0.1.0 surface ships with the fixes baked in, not as a patch-release follow-up.

- mgf-common commit `3728643` — supervisor surfaces pytest stderr when `collect_tests()` returns empty AND pytest exited non-zero; new `tests/unit/testing/test_supervisor_plugin_hygiene.py` pins L6 + the visibility behavior as regression contracts (4 tests).

## Phase B — extraction (2026-05-26)

Published library: [mgf-test-supervisor v0.1.0](https://codeberg.org/magogi-admin/mgf-test-supervisor). Stdlib-only per TS-23. Console scripts: `mgf-test-supervisor` (canonical) + `mtest` (alias). 14 tests pass (4 plugin-hygiene + 5 repo-root discovery + 4 alignment-with-mgf-common + 1 smoke self-check).

Architectural changes vs the embedded form:
- **Repo-root discovery walks up from CWD** looking for `pyproject.toml` (the embedded form used `Path(__file__).parents[N]` which points at site-packages once the library is installed).
- `MGF_TEST_SUPERVISOR_REPO_ROOT` env var override for CI runners that invoke from outside the project.
- Missing pyproject is an actionable SystemExit, not a silent fallback.

## Phase C — sibling migrations off in-tree copy-paste (2026-05-26)

All 7 sibling adopters migrated from `scripts/_mtest/` (copy-paste) to `mgf-test-supervisor` (dev dep). Each sibling's commit deletes ~2,000 lines (the 10-file supervisor copy) and adds ~20 lines (dep declaration + smoke test update + hatch direct-ref opt-in).

| Sibling | Phase C commit | Tests after migration | Notes |
|---|---|---|---|
| mgf-common | `9151a98` | 1771 passed | Canonical template. Also updated `cli/_test.py` shim + `tests/unit/testing/test_supervisor_alignment.py` (imports from `mgf.test_supervisor` now); deleted `test_supervisor_plugin_hygiene.py` (covered by mgf-test-supervisor's own copy). |
| mgf-http | `2b2ff07` | 103 passed | First non-mgf-common migration. |
| mgf-fastapi | `044a8b3` | 153 passed | |
| mgf-django | `2b14291` | 55 passed | |
| mgf-sqlalchemy | `7dab65b` | 102 passed | |
| mgf-alembic | `cb183b0` | 42 passed | |
| mgf-apiprobe | `3d755c4` | 849 passed | **Bare-coverage adopter** — verifies the L6 fix in the published library works for the exact shape that originally surfaced the bug. |

**Verification across all 7:** every sibling's `mgf-test-supervisor --self-check` reports PASS. The alignment contract (mgf-common's `test_supervisor_alignment.py` + mgf-test-supervisor's `test_alignment_with_mgf_common.py`) enforces wire-format equality from both sides; CI failure in either repo on a schema drift.

## Net result

- Source line count delta: −2,076 LOC of duplicated supervisor source across 7 siblings, +~2,100 LOC of canonical library in mgf-test-supervisor (one copy).
- Drift risk eliminated: 7 copies → 1.
- Future fixes ship as `mgf-test-supervisor` version bumps; consumers walk their dev-extras pin forward at their own pace per AP-03 / pre-v1.0 discipline.

---

## Order rationale (hardest-first)

User's call (2026-05-26): adopt in order of difficulty descending.

> mgf-fastapi → mgf-django → mgf-sqlalchemy → mgf-alembic → mgf-http → mgf-apiprobe

Rationale: validate the abstraction against the messiest shape (async + DI + ORM + framework) early. If the supervisor handles mgf-fastapi cleanly, every other sibling will too. The recipe + shared code get stress-tested before the easy cases land.

---

## Adoption commit pattern (per sibling)

Each sibling gets two commits:

1. **`adoption`** — Bump mgf-common pin to `>=0.40,<0.41`; copy `scripts/_mtest/` + `scripts/mtest` + smoke test + DIAGNOSING_HANGS.md; flip the four knobs; update pyproject (timeout / faulthandler / 13-marker registration / pytest-timeout dev dep); add `.gates/` to `.gitignore`. Verify `--self-check` + `--tier 0` exit 0.

2. **`debt-fix`** (only if supervisor surfaces real debt) — Whatever the supervisor surfaces. Typically: sqlite cleanup, N818 deprecation imports, unraised pytest warnings. Cited PAPER if cross-cutting.

Per-sibling effort: 1-2 hours (~30 min copy + 30-90 min debt fix + verification).

---

## Lessons learned (running log)

> Logged in chronological order. Each sibling's adoption may add entries.

### 2026-05-26 — sibling-#2 (mgf-fastapi)

**L1 — `timeout = 30` is the right default but surfaces existing slow-by-design tests.** mgf-fastapi has one test (`test_startup_timeout_raises_timeout_error`) that intentionally waits ~60s as part of testing a TimeoutError path. With the federation's `timeout = 30` per-test default, the test hangs. Workaround: `@pytest.mark.timeout(120)` on the specific test. The proper fix surfaced as PAPER-01 in mgf-fastapi (the underlying `run_test_app` cleanup doesn't cancel the lifespan task cleanly).

**Recipe implication:** add a §10 pitfall row for "a single test legitimately exceeds the 30s default — per-test `@pytest.mark.timeout(N)` override is the workaround; the real bug is usually in the production code being tested." Done in this commit.

**L2 — `--tier 0` selects very few tests in adopted siblings.** Only the `tests/smoke/test_mtest_self_check.py` is marked smoke; the other 157 tests are unmarked → tier-1 by default. The supervisor works correctly but the smoke layer is sparse. Operators expect "tier-0 = the build-broken signal" — meaning a few hand-picked sanity checks, not 100+ tests. So this is OK for now; siblings can mark additional smoke tests as the rollout reveals the need.

**Recipe implication:** §10 should mention "tier-0 sparseness is normal until you mark more smoke tests" so adopters don't think the supervisor is broken when only the self-check appears.

**L3 — the 4-knob adaptation worked perfectly.** Only knob 2 (env_fingerprint import) needed change (`mgf.common` → `mgf.fastapi`); knob 1 (DAEMON_PROCESS_NAME stays None — no daemon), knob 3 (MARKER_TIER unchanged), knob 4 (pyproject — see §5 of recipe) all clean. The 30-minute estimate held.

### 2026-05-26 — siblings-#3..#6 (django / sqlalchemy / alembic / http)

**L4 — adoption time dropped to ~10-15 min after #2.** Knob 2 sed-substitution, pyproject edit (3 sections), .gitignore add, sync, verify, commit. The recipe was right; the variance is mostly waiting for `uv sync --all-extras` (~10s per sibling, longer with heavy deps).

**L5 — sqlalchemy's sqlite cleanup work paid off.** mgf-sqlalchemy's sibling-#4 adoption was expected to surface the PAPER-60-shape sqlite-leak class (per the rollout plan). It didn't — the DP-13 fix shipped in commit `e3bf7a1` earlier this session (dispose-engines-in-fixtures) closed the leak before the supervisor's strictness could surface it. Net: 102/102 pass, no follow-up needed. Supervisor + DP-13 are complementary; without DP-13 the supervisor would have triggered the same cascade in mgf-sqlalchemy as mgf-fastapi's PAPER-01.

### 2026-05-26 — sibling-#7 (mgf-apiprobe)

**L6 — first REAL supervisor bug found in rollout (PAPER-02-shape).** mgf-apiprobe's `--self-check` failed on all 4 scenarios with "collection returned no tests". Diagnosis: supervisor passed `--no-cov` (a pytest-cov-specific flag); mgf-apiprobe uses `coverage` directly rather than `pytest-cov` in dev extras, so the unknown flag aborted pytest before collection.

This is exactly the kind of friction the hardest-first / many-sibling rollout was designed to surface. All 6 earlier adopters had pytest-cov installed; only mgf-apiprobe used bare coverage. Without sibling-#7, the abstraction would have been extracted with the bug — every future adopter using bare coverage would have re-hit it.

**Fix:** replace `--no-cov` with `-p no:pytest_cov` (pytest's standard "disable plugin if loaded; silent no-op if not" pattern). Three call sites in `scripts/_mtest/supervisor.py` (chunk runs, `collect_tests()`, `collect_test_markers()`). Landed in mgf-common's canonical copy + propagated verbatim to all 6 sibling adopters. After the fix, mgf-apiprobe's self-check passes; all 7 siblings remain conformant.

**L7 — recipe §10 gained a pitfall row** for "self-check fails with 'collection returned no tests' when project uses `coverage` instead of `pytest-cov`". Future adopters using bare coverage will see the symptom + the fix in one row.

---

## Recipe improvements (running log)

> Friction encountered while adopting → recipe improvement → benefit for subsequent adopters.

### 2026-05-26 — sibling-#2 (mgf-fastapi)

- **§10 pitfall row added** for the "single slow-by-design test exceeds 30s default" case. Symptom: pytest hangs on a specific test. Cause: per-test timeout fires correctly but the test legitimately takes longer. Fix: `@pytest.mark.timeout(N)` override on the specific test. Bonus: this often surfaces a real bug in the production code being tested.
- **§10 pitfall row added** for "tier-0 selects only the self-check test." Symptom: `--tier 0` returns 1 test. Cause: most existing tests aren't marked smoke. Fix: mark additional sanity-check tests with `@pytest.mark.smoke` over time; the supervisor isn't broken.

### 2026-05-26 — sibling-#7 (mgf-apiprobe)

- **§10 pitfall row added** for "self-check fails on every scenario with 'collection returned no tests'." Symptom: supervisor self-check exits 4 with all scenarios FAIL. Cause: project uses bare `coverage` instead of `pytest-cov`; pre-fix supervisor passed `--no-cov` which is unrecognized. Fix: update `scripts/_mtest/supervisor.py` from mgf-common main (post-2026-05-26-supervisor-fix) which replaces `--no-cov` with `-p no:pytest_cov` (silent no-op if plugin not loaded).

---

## Shared-code improvements (running log)

> Improvements landed in `scripts/_mtest/` (the supervisor) or `mgf.common.testing` (the schema) as a result of adoption work.

### 2026-05-26 — sibling-#7 (mgf-apiprobe) surfaced supervisor bug

**`scripts/_mtest/supervisor.py`** — three call sites updated:

- `Supervisor._run_chunk()` (chunk pytest invocation)
- `collect_tests()` (collection-only pytest)
- `collect_test_markers()` (per-marker collection)

In each: `"--no-cov"` → `"-p", "no:pytest_cov"`. Reason: `--no-cov` is a `pytest-cov`-specific flag that fails when pytest-cov isn't installed (e.g., projects using bare `coverage`). `-p no:pytest_cov` is pytest's standard "disable plugin if loaded, silent no-op if not loaded" pattern — works regardless of whether pytest-cov is installed.

Propagated to all 6 sibling adopters' `scripts/_mtest/supervisor.py` in the same session so no copy drifts.

---

## FEEDBACK papers filed during the rollout

> Cross-cutting issues that needed formal triage rather than inline fixes.

*(Entries land here as papers are filed.)*

---

## Extraction plan (at the end)

After all six siblings adopt:

1. **New repo:** `magogi-admin/mgf-test-supervisor` (NM-03 hyphen slug). I create the Codeberg repo via web UI; user authorises the org-create.

2. **Source:** the most-evolved `scripts/_mtest/` copy (likely mgf-common's after the rollout has polished it).

3. **PyPI:** `mgf-test-supervisor` v1.0.0 from day one. Inherits schema stability from `mgf.common.testing` (which is already `stable (v0.40.0)`). The sibling carries the runtime supervisor; the schema types stay in mgf-common.

4. **Migration:** each adopted sibling drops `scripts/_mtest/`, adds `mgf-test-supervisor>=1.0,<2` to `[project.optional-dependencies].dev`, and `mgf-common test` becomes a re-export of the sibling's `mtest` entry point.

5. **Migration tracker:** this file extends with a "post-extraction" section showing each sibling's migration status.

---

## Cross-references

- **Proposal:** [`docs/inprogress/PROPOSAL_test_supervisor.md`](PROPOSAL_test_supervisor.md)
- **Standards:** [`docs/standards/TESTING.md`](../standards/TESTING.md) §19
- **Recipe:** [`docs/recipes/test-supervisor-adoption.md`](../recipes/test-supervisor-adoption.md)
- **On-call playbook:** [`docs/testing/DIAGNOSING_HANGS.md`](../testing/DIAGNOSING_HANGS.md)
- **Memory entry:** `project_test_supervisor_2026_05_26.md` (agent-private)
- **Originating message (archived):** [`mgf-hrb/docs/testing/MGF_COMMON_ADOPTION_MESSAGE.md`](https://codeberg.org/magogi-admin/mgf-hrb/src/branch/main/docs/testing/MGF_COMMON_ADOPTION_MESSAGE.md)
