# v1.0 stability audit (Wave 4 / E1)

**Status:** ✅ RESOLVED 2026-05-20 — maintainer signed off Q-11. See the
"§3 resolution" note below for the actual outcome.
**Generated:** 2026-05-18 (post-Wave-3, mgf-common 0.37.0 + sibling minors).
**Owner:** mgf-common.
**Constraint:** v1.0 is **NOT** being cut from this audit. This document is the
*proposal* for what the AP-09 stability tier should look like **when** the
maintainer green-lights v1.0. Promotion decisions stay with the maintainer.

> **⚠ The §1 / §3 / §4 tables below are the original 0.37.0-snapshot
> proposal and are retained as the historical record. They were stale by
> the time of sign-off (the surface moved with v0.38.0 + the N818 rename).
> The §3-resolution note records what was actually decided + executed
> against the current 0.38.0 surface.**

> See: [`release_waves_plan.md`](release_waves_plan.md) Phase E.
> See: [`pre_release_check.md`](pre_release_check.md) §10.
> See: [`pre_release_additions.md`](pre_release_additions.md) (consumer evidence).

---

## §1 Federation tier snapshot (today)

Tier counts derived from each repo's `PUBLIC_API.md`. Numbers include the
Wave 1/2/3 surfaces shipped this cycle.

| Repo            | stable | experimental | deprecated | % stable |
|-----------------|-------:|-------------:|-----------:|---------:|
| mgf-common      |   113  |     132      |       0    |   46 %   |
| mgf-apiprobe    |     1  |      81      |       0    |    1 %   |
| mgf-fastapi     |     0  |      22      |       0    |    0 %   |
| mgf-django      |     0  |       5      |       0    |    0 %   |
| mgf-http        |     0  |       4      |       0    |    0 %   |
| mgf-sqlalchemy  |     0  |       3      |       0    |    0 %   |
| mgf-alembic     |     0  |       1      |       0    |    0 %   |
| **Federation**  | **114**| **248**      |   **0**    | **31 %** |

**Reading.** mgf-common carries the only real stable surface today. The six
siblings ship 100 % experimental because they were all carved out of
mgf-common in the last two release cycles — none have had two minor cycles of
consumer burn-in yet. That is the dominant signal: **siblings are not ready
for stable promotion at v1.0 cut.** mgf-common is partially ready.

---

## §2 v1.0 promotion-readiness gate (criteria)

For a name to be a candidate for promotion **experimental → stable** at v1.0
cut, it MUST meet *all* of:

| #   | Gate                                                                    |
|-----|-------------------------------------------------------------------------|
| G-1 | Public surface has not changed shape across **two consecutive** minors. |
| G-2 | At least **one** consumer project depends on it in production code.     |
| G-3 | Covered by unit + (where applicable) integration tests in this repo.    |
| G-4 | Listed in `PUBLIC_API.md` with a description that survives `--strict`.  |
| G-5 | No open PAPER or ROADMAP item proposes a breaking change to its shape.  |
| G-6 | Audit-emitting calls follow LG-04 + SC-17 (where the call mutates state).|

A name that misses **any** gate stays experimental for one more MINOR cycle.

---

## §3 mgf-common per-module verdict

| Module                       | stb | exp | Verdict for v1.0                                       |
|------------------------------|----:|----:|--------------------------------------------------------|
| `mgf.common` (top-level)     |   9 |   0 | **Stable as-is** — `bootstrap`, identity, lifecycle.   |
| `mgf.common.exceptions`      |  35 |   1 | **Promote the 1** — `add_help_note` has been stable for 4+ cycles. |
| `mgf.common.config`          |   2 |  13 | **Promote 10 / 13** — `BaseAppSettings` + layered loader used by all 4 consumers; pydantic discriminated-union vault configs are 1-cycle-old → hold 3. |
| `mgf.common.observability`   |  14 |  23 | **Promote 18 / 23** — setup_logging, redaction, crash sink, OTel are battle-tested. Hold 5 (OTel batch tuning + crash-sink hook signatures may still shift). |
| `mgf.common.settings`        |   6 |   3 | **Promote all 3** — `MgfSettings`, helpers used everywhere.            |
| `mgf.common.typing`          |   4 |   0 | Stable as-is.                                                          |
| `mgf.common.progress`        |   5 |   0 | Stable as-is.                                                          |
| `mgf.common.vault`           |   6 |   4 | **Promote 2 / 4** — env + file backends solid; keyring + factory signature 1 cycle old → hold 2. |
| `mgf.common.versioned`       |   2 |   0 | Stable as-is.                                                          |
| `mgf.common.registry`        |   0 |   2 | **Hold both** — only 1 consumer (PlasmaMapper), API may grow.          |
| `mgf.common.cli`             |  15 |   7 | **Hold 7** — the 7 experimentals are v2.8 federation-discipline blocks (Phase A); 1-cycle-old. |
| `mgf.common.pytest_plugin`   |   0 |   6 | **Promote 4 / 6** — bootstrap fixtures used by every sibling + every consumer. Hold 2 (audit-capture helpers, 1 cycle old). |
| `mgf.common.db`              |   0 |   5 | **Hold all 5** — sync engine + URL helpers; only 2 consumers, both Django path. Recommend 1 more MINOR. |
| `mgf.common.alembic`         |   0 |   1 | **Hold** — `import_versioned_models`; PlasmaMapper-only.               |
| `mgf.common.django`          |   0 |   5 | **Hold all 5** — 1 consumer (inthewords). Recommend 1 more MINOR.      |
| `mgf.common.providers`       |   2 |   0 | Stable as-is.                                                          |
| `mgf.common.fastapi`         |   0 |  16 | **Hold all 16** — 1 consumer (PlasmaMapper); API still evolving.       |
| `mgf.common.http`            |   0 |   7 | **Hold all 7** — extracted to `mgf-http`; this surface is the in-tree compat shim — will be deprecated, not promoted. |
| `mgf.common.process`         |  13 |   0 | Stable as-is.                                                          |
| `mgf.common.flags`           |   0 |   6 | **Hold all 6** — shipped this cycle (v0.35.0).                         |
| `mgf.common.schedule`        |   0 |   7 | **Hold all 7** — shipped this cycle (v0.35.0).                         |
| `mgf.common.plugins`         |   0 |   3 | **Hold all 3** — shipped this cycle (v0.36.0).                         |
| `mgf.common.doctor`          |   0 |   6 | **Hold all 6** — shipped this cycle (v0.36.0).                         |
| `mgf.common.rate_limit`      |   0 |   2 | **Hold all 2** — shipped this cycle (v0.37.0).                         |
| `mgf.common.idempotency`     |   0 |   4 | **Hold all 4** — shipped this cycle (v0.37.0).                         |
| `mgf.common.health`          |   0 |   7 | **Hold all 7** — shipped this cycle (v0.37.0).                         |
| `mgf.common.cache`           |   0 |   4 | **Hold all 4** — shipped this cycle (v0.37.0).                         |

**Promotion tally (mgf-common):** ~38 names move stable, ~94 names hold.
That brings mgf-common to ~151 stable / ~94 experimental ≈ **62 % stable**
at v1.0 cut. Wave 1+3 surfaces (38 names) all hold for one more MINOR; that
is the deliberate burn-in cycle.

**Recommended deprecation at v1.0:** `mgf.common.http` (7 names) — already
extracted to `mgf-http`; keep the import shim through v1.x but mark
deprecated on the v1.0 tag. Tracked under PAPER-04 / federation-extract.

---

## §3 resolution — what was actually decided + executed (2026-05-20)

The §3 table above was a 0.37.0 snapshot and was stale by sign-off time
(v0.38.0 + the N818 rename moved the surface; `add_help_note` was mis-
attributed to `exceptions` — it is in `mgf.common` top-level and already
stable). The audit was **regenerated against the current 0.38.0
`PUBLIC_API.md`** and walked module-by-module with the maintainer.

**Decision: promote-all for the six candidate modules.** The maintainer
reviewed the regenerated per-module proposal — including a flagged
concern that `register_log_handler` / `register_span_processor` register
into registries with no reader yet (settings-driven activation is unbuilt
"Phase 5" work) — and chose to promote all of them (option B): the
decorator + registry shapes are the stable contract; the future
auto-activation layer is a separate name when it ships. The two affected
`PUBLIC_API.md` descriptions were reworded so a `stable` row no longer
references a non-existent `MgfSettings` field.

**50 names promoted experimental → `stable (v1.0.0)`**, one commit per
module:

| Module | Promoted | 
|---|---|
| `mgf.common.exceptions` | 1 — `HmacVerificationError` |
| `mgf.common.config` | 13 — loader/path helpers + pydantic re-exports |
| `mgf.common.observability` | 23 — span helpers, crash-sink / redaction / log-handler / span-processor registries, `AsyncJsonHandler` |
| `mgf.common.settings` | 3 — `LogLevel`, `LogFormat`, `PresetName` |
| `mgf.common.vault` | 4 — backend-registry trio + `BuiltinVaultBackendConfig` |
| `mgf.common.pytest_plugin` | 6 — the five fixtures + `decode_json_logs` |

Modules NOT in scope of this sign-off stay experimental as the original
§3 recommends (cli federation-discipline blocks, db, django, fastapi,
registry, alembic) — each promotes on its own later cycle. `mgf.common.http`
deprecation is a separate cut-time action (see "Recommended deprecation"
above), not done here.

Tier edits applied to `PUBLIC_API.md` only — the stability tier has no
source-code coupling. public-API name-sync tests stayed green throughout.

---

## §4 Sibling verdict

| Sibling          | At v0.x today | v1.0 verdict                                                                  |
|------------------|--------------:|-------------------------------------------------------------------------------|
| mgf-http         | v0.4.0        | **Hold all experimental.** Single consumer (PlasmaMapper); resume-download just shipped (v0.4.0) — needs one cycle of mileage. |
| mgf-sqlalchemy   | v0.4.0        | **Hold all experimental.** AuditTrailMixin / uuid7 just shipped; needs consumer use. |
| mgf-apiprobe     | v0.5.0        | **Promote the existing 1 stable; hold the 81 experimental.** Internal-consumer-only library; no external pressure to promote en masse. |
| mgf-fastapi      | v0.6.0        | **Hold all 22.** Rate-limit middleware shipped this cycle; one consumer (PlasmaMapper). |
| mgf-django       | v0.4.0        | **Hold all 5.** Throttle middleware shipped this cycle; one consumer (inthewords); not on PyPI yet (B-1). |
| mgf-alembic      | v0.4.0        | **Hold the 1.** Drift detector just shipped; needs migration-cycle exposure. |

**No siblings are ready for any stable promotion at v1.0 cut.** They will
each need their own v1.0 cut later, gated on each one independently meeting
G-1..G-6. The mgf-common v1.0 cut does **not** force siblings to cut v1.0.

---

## §5 Decision matrix — ✅ RESOLVED 2026-05-20

The sign-off process below was followed. Outcome: see the "§3 resolution"
note above — promote-all for the six candidate modules, 50 names moved to
`stable (v1.0.0)`, one commit per module. G-2 (consumer-in-prod) was taken
as asserted per the maintainer's chosen approach; the public-API name-sync
tests were run after each module's edit and stayed green.

*Original process (retained for the record):*

1. Open `PUBLIC_API.md`. For each candidate name flagged in §3:
   - Walk the call sites in **each of the 4 consumers** (VManager, inthewords,
     PlasmaMapper, iosRecovery).
   - Confirm G-2 (one consumer in prod), G-3 (test coverage in this repo).
2. If sign-off: change `experimental` → `stable (v1.0.0)` in PUBLIC_API.md.
3. If hold: leave row untouched.
4. Run the public-API tests after editing — they diff-check the table
   against `PUBLIC_API.json` so a typo gets caught.

A separate pass should annotate `mgf.common.http` with `deprecated (v1.0.0,
remove v2.0.0)` for the 7 names.

---

## §6 What this audit deliberately does NOT do

- Does not bump any version number.
- Does not edit `PUBLIC_API.md`, `PUBLIC_API.json`, or any source file.
- Does not promote / demote any name.
- Does not propose API changes.
- Does not gate sibling v1.0 cuts — each sibling will get its own audit
  when its maintainer is ready to cut.

This is a *recommendation* document. The audit is complete when the
maintainer reads it and either signs off the promotions or requests
adjustments. Then a separate "v1.0 cut" task does the actual edits.
