# Changelog

All notable changes to `mgf-common` are documented here.

The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/);
the project follows [SemVer](https://semver.org/spec/v2.0.0.html). Per
the 0.x window ([SemVer 0.x](https://semver.org/#spec-item-4) and rule
**AP-03**), MINOR releases MAY break the public API. Pin tightly:

```toml
mgf-common = ">=0.X.0,<0.Y"   # one minor at a time
```

Each release section cross-references the [`FEEDBACK.md`](FEEDBACK.md)
entry and (where applicable) the consumer-side migration recipe. The
release-engineering discipline that produces this changelog is in
[`RELEASING.md` (now in `mgf-standard`)](https://codeberg.org/magogi-admin/mgf-standard/src/branch/main/docs/standards/RELEASING.md).

---

## [Unreleased]

## [0.50.0] - 2026-07-04

### M2 Wave-2 Phase 1: THE HONEST GATE (testing surface)

#### Removed
- **`mgf.common.testing.MARKER_TIER` no longer maps `slow`** — the legacy
  TS-18 marker completed its deprecation cycle (founder Wave-2 tier sitting;
  announced "Removed in v2.0", enacted with the taxonomy correction). The
  canonical taxonomy is **twelve markers + the explicit `tier(N)` override**;
  `functional` was verified to have NEVER existed in this mapping (doc-only
  phantom, struck from TESTING.md). Consumers: drop the `slow` row from
  `[tool.pytest.ini_options].markers` and migrate any `@pytest.mark.slow` to
  the specific tier-3 marker (`perf` / `stress` / `e2e`) — or let the
  supervisor's auto-tier promote from observed durations. This package's own
  markers + addopts no longer mention `slow`.

#### Added
- **PAPER-23 gets loud** (`mgf.common._plugin_probe`): when `mgf.common` is
  used inside a pytest run while the `pytest11` plugin never loaded AND no
  entry point is visible (the editable-install silence), one
  `MgfPytestPluginMissingWarning` per process is emitted — under the
  federation `filterwarnings = ["error"]` default that's a hard failure, not
  a silent fixture/perf no-op. Probed at `mgf.common` import (mid-test) and
  from `bootstrap_for_test`. Explicit `-p no:mgf_common` stays silent;
  `MGF_COMMON_NO_PLUGIN_WARN=1` is the kill switch.
- **Doc↔code pin, code side** (`tests/unit/testing/test_testing_doc_alignment.py`):
  the installed standards corpus' TESTING.md (via `mgf.standard.read_standard`)
  must carry exactly this package's MARKER_TIER + tier budgets — the drift
  class that produced three answers for `integration`'s tier dies two-sided
  (mgf-standard's `check_testing_doc.py` gates the doc side). Arms once the
  honest-gate corpus (mgf-standard >= 0.1.62) is installed.
- Marker-taxonomy pins: exact twelve-key set, `slow` absence, `functional`
  never-existed regression pins in `tests/unit/testing/test_markers.py`.

#### Changed
- **Supervisor alignment suite extended + release-dance guarded**
  (`tests/unit/testing/test_supervisor_alignment.py`): now pins the unified
  `TierBudget` FIELD NAMES (the supervisor adopted this package's dataclass
  shape — the NamedTuple double-shape is gone), the full
  outcome→severity MAPPING (`quarantined` = WARN on both sides; the
  supervisor previously said SKIP), `SECTION_ORDER` /
  `SECTION_HEADER_FMT` byte-pins (the supervisor vendors them and its bundle
  writer now emits through them), and `effective_tier` ↔ `tier_for_markers`
  equivalence incl. override semantics. Feature-detect module skip against a
  pre-Wave-2 supervisor keeps CI green through the lockstep window (arms at
  supervisor >= 0.1.18).

#### Fixed
- Docstring truth sweep (H1 recon): DoctorFinding provenance corrected to the
  one real parent — `mgf.hrb.diagnostics.DoctorFinding` (the previously cited
  `mgf.common.observability.DoctorFinding` never existed); `Severity`
  docstring no longer contradicts `DEFAULT_SEVERITY` (WARN = quarantined,
  SKIP = by-design skip) and documents why DoctorFinding's DEFERRED
  deliberately has no test-outcome counterpart; `FailureBundle`'s
  field-to-section map now matches `serialize()` exactly (headers are the
  bare SECTION_ORDER names); `docs/testing/DIAGNOSING_HANGS.md` example
  record updated to `schema_version 1.1.0` + the nullable `perf` field, and
  its `mgf.hrb` example paths (extraction-era leftovers) now say
  `mgf.common`; stale `mgf-common test` CLI mentions → `mgf-fed test`
  (verb moved in the v0.47 CLI extraction) in `mgf.common.testing` +
  pyproject comments.

### Conformance
- AP-16 ledger row added (cornerstone variant: the release-side
  `ping-siblings` fan-out is the duty; no mgf-common pin exists to window) —
  mgf-standard 0.1.61 entered AP-16 into the released MUST index and DOC-15
  required the row. `fed-resources/generated/` regenerated (`fedres check`
  IN_SYNC; the architecture projection had drifted).

## [0.49.2] - 2026-07-02

_No library-code changes — this release ships the CI, governance, and
documentation work accumulated since 0.49.1, and is the first cornerstone
release to carry the AP-16 `ping-siblings` fan-out (so it also serves as the
end-to-end verification that a cornerstone tag re-runs the sibling
compat-matrices)._

### Changed
- **CI: the cornerstone fires the AP-16 `ping-siblings` fan-out on tag** (#66).
  On every `mgf-common` release the pipeline now pings each pinned sibling's
  Woodpecker so it re-runs its `mgf-common` compat-matrix against the freshly
  published cornerstone — the enforcement half of the AP-16 wide-pin window
  (`>=<floor>,<1.0`). This is the first release to exercise the step.
- **CI: the `fed-resources/` freshness gate is wired into the pipeline** (#64).
- **Governance: the verified ownership boundary is written into `SCOPE.md`**
  (Item 2, #63) and the conformance ledger now accounts for **AG-20**
  (owned-infra discipline).

### Docs
- **PAPER-103 — cross-language payload schemas published next to the code**
  (#65), for the sooth cross-family review.

## [0.49.1] - 2026-06-26

### Changed
- **`webhooks.verify_svix_hmac` HMACs the RAW body bytes** (`f"{id}.{ts}.".encode() + body`) instead of decoding + re-encoding to UTF-8. Byte-identical for UTF-8 JSON (every real webhook), but avoids two full passes over the payload — a large-body hot path (closes a ~237% perf regression the pre-consolidation mgf-fastapi verifier flagged) — and correctly handles arbitrary body bytes (Svix signs raw bytes). A non-UTF-8 body now fails as an HMAC mismatch rather than a decode error.

## [0.49.0] - 2026-06-26

### Added
- **`mgf.common.webhooks` — framework-neutral Svix webhook HMAC primitive
  (PAPER-26).** `verify_svix_hmac()` + `decode_svix_secret()` +
  `WebhookHeaderSchema`/`SVIX_SCHEMA` + `WebhookHmacError`. The Svix/Clerk
  signature dance was hand-rolled identically in `mgf-auth` and `mgf-fastapi`
  (and had drifted three ways); this is the single shared home for the crypto.
  Merges the hardenings from both copies: whitespace-split key rotation, the
  non-UTF-8-body guard, and correct `whsec_` base64 decoding. Consumers keep
  their domain/transport value-add and delegate the compare here.
- **Three closed-box promotions (AP-14)** so siblings stop reaching into
  `mgf.common._*`: `set_identity()` (lightweight identity without the full
  `bootstrap()` stack — for CLIs/tools), `reset_context()` (context manager
  clearing the process-wide bootstrap context for benchmarks/tests), and
  `RUNTIME_SNAPSHOT_ENV_VAR` (the `MGF_DOCTOR_RUNTIME_PATH` constant). These
  retire the last three `mgf-fed` `_DEFERRED` private reaches.

## [0.48.0] - 2026-06-25

**Two new public primitives + the cornerstone reaches the L3 bar.** Both
additions are experimental-tier (AP-11); the rest of the importable API is
unchanged, so existing consumers are unaffected (and pin `<0.48`, so this is
opt-in).

### Added
- **`mgf.common.retry`** — the canonical **EH-11** resilience primitive:
  `retry` + async `aretry` with exponential backoff + **full jitter** + a
  **finite cap** + a **per-attempt `audit=true` log** (`event=retry.attempt` /
  `retry.exhausted`). For typed transient failures only (timeouts, 503s, lock
  contention) — never validation errors. (ERROR_HANDLING §13.1 reference shape.)
- **`mgf.common.io.atomic_write`** — the crash-safe atomic write (**CF-04**),
  promoted from the private `_io` so federation siblings adopt the public path
  instead of reaching into the closed-box surface (**AP-14 / DP-13**).

### Changed
- **Conformance → L3.** mgf-common now meets the L3 bar on every surface it
  controls: all owned MUSTs evidenced (EH-11 closed by `retry`), all 8 SHOULD
  rules audited (met / N-A / justified), runtime enforcement wired. The lone
  remaining item — **WF-11** (CI install via `uv sync --frozen`) — is a
  fleet-infra task it does not own (generated, drift-checked template + the
  devpi backfill gap). See `docs/inprogress/MGF_STANDARDS_CONFORMANCE.md`.

## [0.47.0] - 2026-06-16

**Breaking — `mgf-common` is now a pure importable library.** The federation
reorg (Wave 3) moved the entire end-user / governance CLI out of the library
and into `mgf-fed`. `mgf-common` keeps only the public CLI *infrastructure*
(exit codes, the `run_and_translate` translator, `field_provenance`, the
subcommand registry). The importable API is otherwise unchanged — consumers
that `import mgf.common.*` (including `mgf.common.cli.run_and_translate` /
`build_cli` / `register_cli_subcommand`) are unaffected; the diff to
`PUBLIC_API.json` is the version string alone.

### Removed
- **The `mgf-common` console-script.** All subcommands moved to `mgf-fed`:
  `pip install 'mgf-fed[cli]'`, then `mgf-fed <verb>`. Two verbs were renamed
  to avoid clashing with mgf-fed's native fleet verbs: `doctor` →
  **`mgf-fed app-doctor`** (mgf-fed `doctor` is the federation self-check) and
  `feedback` → **`mgf-fed feedback-howto`** (mgf-fed `feedback` relays
  outboxes, FB-05). All others keep their names (`mgf-fed explain` /
  `validate` / `migrate` / `standards` / `agents` / `catch-up` / `logs` / …).
- The 22 command-implementation modules under `mgf/common/cli/` (`_version`,
  `_explain`, `_doctor`, `_standards`, `_agents`, `_catch_up`, `_init_project`,
  `_migrate`, `_mgfcli`, …). The reader side of `doctor --live`
  (`_doctor_live`) moved too; the bootstrap-side snapshot *writer* already
  lives at `mgf.common._doctor_snapshot` (v0.46 A0 prep) and stays.

### Changed
- `mgf.common.cli.build_cli()` no longer pre-registers any built-in
  subcommands — it returns an empty parser until a consumer registers its own.
  (The four former built-ins were registered by the now-moved `_mgfcli`.)
- `STARTHERE.md`: the embedded command references updated `mgf-common <verb>`
  → `mgf-fed <verb>` (the agent briefing's canonical mirror).

### Migration
- Replace any `mgf-common <verb>` invocation with `mgf-fed <verb>` (install
  `'mgf-fed[cli]'`); use `app-doctor` / `feedback-howto` for the two renames.
- No source changes are needed for code that imports `mgf.common`.

## [0.46.12] - 2026-06-16

The PAPER-102 cornerstone slices — `doctor`/`catch-up` gained the AG-03
commit-trailer enforcement support. Pairs with `mgf-standard` 0.1.58 (AG-18 v2.24):
with both pinned, the AG-18 `prepare-commit-msg` doctor check flips from inert to
enforcing.

### Added
- **`doctor --standards`: the AG-18 `prepare-commit-msg` hook check (PAPER-102 slice 3).**
  `_check_commit_hook` verifies `.githooks/prepare-commit-msg` is present —
  **self-gating + inert by design**: it enforces ONLY when the pinned `AGENTS`
  corpus text references `prepare-commit-msg` (the tightened AG-18, mgf-standard
  ≥ 0.1.58), so it lands GREEN on older corpora and activates on repin.
- **`catch-up`: distribute the AG-03 commit-trailer scaffold (slice 4).** `catch-up
  --apply` now writes the canonical `.githooks/prepare-commit-msg` + `scripts/
  check_commit_trailers.py` into a repo, resolved via `mgf.standard.read_scaffold`
  (single source of truth = mgf-standard's wheel data; degrades gracefully on a
  corpus < 0.1.57). The cookiecutter ships both for new repos.

### Changed
- **Config: `[tool.mgf.ci]` → `[tool.mgf-fed]`.** The CI carve-out moved out of the
  `[tool.mgf]` namespace (which `MgfSettings` reads with `extra="forbid"`, so a key
  there crashed `bootstrap()`); the federation tooling config now lives under
  `[tool.mgf-fed]`. Fixes a latent fleet-wide bootstrap collision.

## [0.46.11] - 2026-06-14

### Changed
- **Briefing + catch-up point at the generated `.woodpecker.yml` (WF-13).** The `agents`
  briefing §5 (+ the `STARTHERE.md` mirror) now states the `.woodpecker.yml` is a GENERATED
  artifact — rendered by `mgf-fed scaffold` from the canonical template, enforced by
  `mgf-fed drift-check`; never hand-edit it — and points at the `run-ci` recipe. The
  WF-rule range auto-bumped to `WF-01..WF-13` (PAPER-67 dynamic render) now that
  `mgf-standard` (>=0.1.55) carries WF-13. `catch-up`'s Woodpecker check stays
  presence-based but its message now guides to `mgf-fed scaffold` instead of hand-authoring.
  Inert behaviour change — purely the federation tooling guidance.

## [0.46.10] - 2026-06-13

### Added
- doctor + catch-up support for AG-17 (docs/memory/) and AG-18 (.gitignore must ignore my_stuff/);
  agents briefing mentions both. Inert until the pinned corpus carries the rules (mgf-standard >=0.1.54).

## [0.46.9] - 2026-06-13

### Added
- **`mgf-common onboard-prompt` + `/onboard`** — the fourth federation entry-point prompt: bring an
  EXISTING external (non-MGF) project INTO the federation as a member (decide join shape + add mgf-common
  + conform existing code via the sync engine). Completes the entry-point set with `sync` / `new-project`
  / `adopt`. Flag-less, self-updating from `mgf-standard` (>=0.1.53) via `read_recipe`.

## [0.46.8] - 2026-06-13

### Added
- **`mgf-common new-project-prompt` + `adopt-prompt`** — the two federation entry-point prompts that
  flank `sync-prompt`, completing the trio so an agent gets the RIGHT prompt for its case:
  existing member → `sync-prompt`, empty repo → member → `new-project-prompt` (Case B), non-member
  adopting → `adopt-prompt` (Case C). Each is flag-less and resolves its recipe from the pinned
  `mgf-standard` (>=0.1.52) via `read_recipe` — self-updating, single source of truth — and degrades
  with an actionable error if the `[standards]` extra is absent.

## [0.46.7] - 2026-06-09

### Changed
- **Federation rename: `mgf-doc-standard` → `mgf-standard`.** The [standards] extra now depends on `mgf-standard`; the agents/standards/catch-up/federation/sync-prompt/doctor-standards CLI now `import mgf.standard` (was `mgf.doc_standard`). uv.lock regenerated. No public API change. (Test fixture `_write_project` now seeds LESSON_LEARNED.md so the DOC-19-clean fixture is conformant.)


## [0.46.6] — 2026-06-07 — depth-audit gap closing (PATCH; 1 behavior fix)

The 2026-06-07 cornerstone depth audit (169 ✅ / 14 ⏳ / 35 N/A) was followed by
this gap-closing pass: **11 of 14 gaps closed, 3 deferred** → ledger now
**180 ✅ / 3 ⏳ / 35 N/A** (98% of applicable MUSTs evidenced). See
`docs/inprogress/MGF_STANDARDS_CONFORMANCE.md`.

- **Behavior fix — CF-02 config precedence.** `MgfSettings` ranked the on-disk
  `[tool.mgf]` pyproject source ABOVE `.env`, inverting CF-02 (which mandates
  `.env` → on-disk config file). Corrected: precedence is now CLI → env →
  **`.env` → `[tool.mgf]`** → defaults. Only affects you if you set the *same*
  field in both places expecting pyproject to win (rare). See
  `docs/cutover/v0.46.md`.
- **PR-01** — added an import-linter contract forbidding UI frameworks in
  `mgf.common` (mechanical enforcement, not just a clean tree).
- **OP-04** — README headline corrected (version 0.42.0→0.46.5; PlasmaMapper→TelePlasma).
- **OP-03** — `.woodpecker.yml` lint gains a no-placeholder-URL grep.
- **TS-07 / TS-08 / TS-13** — added tests: every exception is test-referenced;
  the provider catch-all (unknown-vendor) path; redaction strategies extracted to
  `_strategies.py` + an AWS-key (AKIA) value-pattern test.
- **PKG-08** — new `scripts/package_retention.py` (quota report + retention prune,
  dry-run by default).
- **REL-09** — `docs/cutover/v0.46.md` consumer-notification artifact.
- **EH-13 / PR-07** — recorded EH-13's retry/amplification bound; classified PR-*.
- **Deferred (3):** EH-11 retry primitive (→ 0.47.0 MINOR, FEEDBACK PAPER-100),
  WF-11 (CI install-from-lock, pending Woodpecker activation), REL-01 (re-annotate
  4 historical lightweight tags, pending force-push authorization).


## [0.46.5] — 2026-06-07 — doctor --standards LESSON_LEARNED.md presence gate (PATCH)

- **`doctor --standards` now gates `LESSON_LEARNED.md` presence** (FEEDBACK
  **PAPER-99**; closes the federation's DOC-19 promotion). DOC-19 (the
  append-only per-repo lessons log) became a MUST in standards **v2.21**; this
  adds `_check_lesson_learned_present` — a root-level check that fails when the
  file is missing. **Presence is the bar:** a seeded file with no real lessons
  yet is conformant. **Corpus-aware + inert by design:** the gate fires ONLY
  when the resolved `mgf-standard` carries `DOC-19` in its Index of MUSTs
  (≥ 0.1.20), so a project pinning an older corpus is unaffected — it activates
  exactly when the project's pinned corpus makes DOC-19 a MUST. (The CFM-03
  ledger-completeness gate already requires a DOC-19 row for the same corpora —
  it parses the Index dynamically, no code change.) Library-API-neutral; stays
  inside the `<0.47` cohort window.

## [0.46.4] — 2026-06-07 — catch-up registry-snapshot provenance (PATCH)

- **`catch-up` now states its federation-registry SNAPSHOT provenance
  explicitly** (FEEDBACK **PAPER-98**, relayed from mgf-standard's
  federation-sync red-team / its PAPER-05). The mgf-* currency checks resolve
  OFFLINE from `mgf.standard.libraries()` — a snapshot of the installed
  `mgf-standard` that "sees only as far as that pin." When the project's
  `mgf-standard` pin lags the latest release, that snapshot is itself stale,
  so `_check_federation_position` could report "pins keep pace ✅" against an
  out-of-date roster — a confident, potentially-wrong verdict. The
  federation-position finding now appends, on both the ok and lagging branches:
  *"Registry snapshot = mgf-standard `<ver>`; this audit sees only as far as
  that pin — to confirm it is the latest PUBLISHED, run `mgf-common sync-prompt`
  STEP-0 ASSERT-LATEST or check the Codeberg index."* **No network call** — the
  CLI stays offline by design (preserving PAPER-65/68); the live registry check
  is performed agent-side by the sync-prompt, which has the configured index
  creds. Library-API-neutral (CLI output only); stays inside the cohort's
  `<0.47` pin window — no pin-walk.

## [0.46.3] — 2026-06-06 — doctor --standards flat / multi-package layouts (PATCH)

`doctor --standards` now audits projects beyond the strict `src/<pkg>` shape:
flat / multi-package layouts (e.g. a Django app: `config/` + `apps/` at the
repo root) and deep single-child namespaces (`vm-vmanager-agent` →
`src/vmanager/agent`). `_discover_project` returns a shape for any project with
a `[project]` name (root-level checks like the DOC-15 ledger gate always run);
`package_root` is best-effort and package-level checks (AP-09/AP-13/DP-12)
degrade to N/A when there is no single import package.


## [0.46.2] — 2026-06-06 — doctor --standards deep-namespace detection (PATCH)

`doctor --standards` now detects deep PEP-420 namespace packages — e.g.
`mgf-vm-core` → `src/mgf/vm/core` (no `__init__.py`, `py.typed` only). `_find_package_root` adds a full hyphen→path candidate and accepts a package by
`__init__.py` / `py.typed` / any `.py`. Fixes the conformance gate for the
`mgf-vm-*` family (surfaced walking vm-vmanager-app's dependencies).


## [0.46.1] — 2026-06-06 — doctor --standards shape detection (PATCH)

`mgf-common doctor --standards` now audits applications whose distribution
name doesn't derive the import package (e.g. `vm-vmanager-cli` → package
`vmanager`): when name-inference finds nothing, `_discover_project` falls back
to the single importable package under `src/`. Previously such projects bailed
with 'cannot detect project shape' and the conformance gate couldn't run.


## [0.46.0] — 2026-06-06 — federation-sync hardening (the CLI wave)

MINOR. New `federation` / `design` subcommands + a `doctor --standards`
partial-sync gate; loud-degrade of the federation CLI; `mgf-common[standards]`
is now the canonical install. No breaking changes.

### Federation-sync hardening (the CLI wave)

- **Loud degrade (WU3).** When the optional `[standards]` corpus extra is not
  installed, `standards` / `sync-prompt` now print the exact fix
  (`pip install -U 'mgf-common[standards]'`) and `agents` prints a loud stderr
  warning instead of silently omitting the federation map (it still emits the
  briefing and exits 0). No federation CLI command degrades silently.
- **`[standards]` is the canonical install (WU4).** The `agents` briefing +
  `STARTHERE.md` startup checklist now lead with
  `pip install -U 'mgf-common[standards]'` and point at `mgf-common sync-prompt`;
  `docs/recipes/new_project.md` updated; added `.claude/commands/sync.md` — a
  thin `/sync` delegator that runs `mgf-common sync-prompt` (no inline copy to
  drift).
- **New commands (WU5):** `mgf-common federation` prints the federation contract
  (`FEDERATION.md`) and `mgf-common design [<name>]` prints/lists the foundation
  design papers — both resolved from the `mgf-standard` corpus seams
  (`federation()` / `read_design()`, 0.1.11+), with the same loud degrade.
- **Partial-sync gate (WU6):** `mgf-common doctor --standards` gains a DOC-15
  check that parses the corpus *Index of MUSTs* and FAILS (non-zero) when the
  project's `MGF_STANDARDS_CONFORMANCE.md` doesn't account for every current
  MUST — turning a partial conformance ledger into a hard, mechanical failure
  instead of an invisible omission.

## [0.45.2] — 2026-06-04 — catch-up advice + PKG-12 grep gate

PATCH. Two follow-ups to the 0.45 Codeberg cutover; no public-API change.

### Added

- **`scripts/check_no_pypi_surface.py` + the `no-pypi-surface` CI step** —
  mechanical PKG-12 enforcement. Fails CI if any `mgf-*` publish / token /
  index / verify surface (`.woodpecker*.yml` + `pyproject.toml`) targets
  pypi.org, allowlisting comments, `not pypi.org` negations, and
  `--extra-index-url` third-party fallbacks. Runs as an early gate (after
  `lint`); stdlib-only so it behaves identically under `woodpecker-cli exec`.

### Changed

- **`catch-up` federation-position advice** — when the registry isn't
  resolvable it now says HOW to enable the check: add the
  `mgf-common[standards]` extra, or (to track the latest from Codeberg) depend
  on `mgf-standard` directly and map it to the `codeberg-mgf` index — uv
  does not apply index pins to transitive deps (PACKAGE_DISTRIBUTION.md §6).

## [0.45.1] — 2026-06-04 — make `mgf-standard` optional (fixes 0.45.0 resolvability)

**`mgf-common 0.45.0` was effectively uninstallable by consumers** — this
fixes it. `mgf-standard` (the standards corpus) moves out of the hard
runtime dependencies into a new optional extra, **`mgf-common[standards]`**.

### Why

`mgf-standard` is published only to the Codeberg registry, and uv does
**not** apply `[tool.uv.sources]` index-pinning to *transitive* dependencies.
So a consumer of `mgf-common 0.45.0` (which hard-depended on it) resolved
`mgf-standard` from public PyPI (≤0.1.4), which fails its `>=0.1.6`
requirement — and uv backtracked `mgf-common` to 0.44.5. The cornerstone's
core (exceptions / config / logging / observability / vault) needs no `mgf-*`
dependency at all, so demoting the corpus to an extra removes the transitive
burden: core consumers now resolve `0.45.x` cleanly.

### Changed

- **`mgf-standard` is now the optional `[standards]` extra**, not a hard
  dependency. The federation CLI (`standards` / `agents` / `catch-up` /
  `sync-prompt`) already degrades gracefully when it is absent; install
  `pip install 'mgf-common[standards]'` to enable those commands. It remains
  mapped to the `codeberg-mgf` index (a direct extra dep, so the pin applies).

### Migration

- A consumer using only mgf-common's core: **no change** — and `0.45.x` now
  resolves where `0.45.0` silently fell back to `0.44.5`.
- A consumer using `mgf-common standards/agents/catch-up/sync-prompt`: add the
  extra — `mgf-common[standards]` (or depend on `mgf-standard` directly +
  map it to the `codeberg-mgf` index).

## [0.45.0] — 2026-06-04 — Codeberg package-distribution cutover (PKG-01..12)

`mgf-*` packages now publish to and resolve from the **Codeberg PyPI
registry** (the `magogi-admin` account), **not** `pypi.org` — the
federation-wide cutover defined by `mgf-standard` 0.1.6 /
[`PACKAGE_DISTRIBUTION.md`](https://codeberg.org/magogi-admin/mgf-standard/src/branch/main/docs/standards/PACKAGE_DISTRIBUTION.md)
(rules **PKG-01..PKG-12**). Third-party deps still resolve from public PyPI.
`mgf-common` is the federation template for this change. Browse the standard
with `mgf-common standards pkg`.

### Changed

- **`mgf-*` dependencies resolve from Codeberg (PKG-06).** Added a
  `[[tool.uv.index]]` named index `codeberg-mgf` (`explicit = true`) +
  `[tool.uv.sources]` pinning `mgf-standard` + `mgf-test-supervisor` to it,
  so a public package can never shadow an `mgf-*` sibling. Floor bumped to
  **`mgf-standard>=0.1.6,<0.2`** (0.1.5/0.1.6 exist only on Codeberg).
- **CI publishes to Codeberg (PKG-04/05/09).** `.woodpecker.yml` `publish`
  uploads to the Codeberg registry (`codeberg_package_user` /
  `codeberg_package_token` secrets); the new `verify-codeberg` step polls the
  Forgejo REST API for HTTP 200 (replaces `verify-pypi`). Dropped the
  `pypi_token` secret + the obsolete "when OIDC reaches Forgejo" block (there
  is no OIDC on Forgejo — PKG-03). New stdlib-only `scripts/verify_codeberg.py`.
- **`catch-up` version/pin checks are offline + Codeberg-sourced (PKG-11).**
  The `pypi.org` JSON-API lookups are gone: version-currency reads the shipped
  federation registry (`mgf.standard.libraries()`); the cross-sibling
  pin-compat check reads the *installed* sibling's metadata
  (`importlib.metadata.requires`) — never `pypi.org`.
- **`mgf-common standards pkg`** surfaces the new `PACKAGE_DISTRIBUTION.md`
  (aliases `pkg` / `codeberg` / `packaging` / `distribution` / `registry`). The
  agent briefing + `STARTHERE.md` §5 gain a **Packages** entry (PKG-01..12).
  README badges + the `CLAUDE.md` / `docs/release/ci.md` release runbooks
  retargeted to Codeberg.

### Removed

- **BREAKING (pre-1.0, AP-03): `mgf-common catch-up --check-pypi` /
  `--no-check-pypi`.** Their only job — gating an outbound `pypi.org` HTTP
  call — no longer exists (the checks are offline). Drop the flags from any
  script that passed them.

## [0.44.5] — 2026-06-03 — catch-up: non-Python `docs` kind (PAPER-97)

### Added

- **`catch-up` detects a non-Python repo as `kind: docs`.** A repo with no
  `pyproject.toml` / `setup.py` / `setup.cfg` **and** no `*.py` anywhere (a
  documentation / brand / content repo) was previously mis-classified as
  `kind: library` and audited as a Python library — a wall of inapplicable
  findings, and on `--apply` the verbatim Python-rules `CLAUDE.md` template
  written into a code-free tree. Now such a repo is `docs`: the six
  language-specific rows — pyproject pin, four gates, the DOC-17 library
  doc-contract, the test-supervisor, the marker taxonomy, and Woodpecker — are
  reported **N/A** (ℹ️), not 🔴/⚠, and `--apply` renders a thin
  federation-orientation `CLAUDE.md` (the `mgf-common agents` pointer, no Python
  recipes). The language-agnostic rows (CLAUDE.md/AGENTS.md presence, settings,
  federation position, `uv.sources`, private-import leaks) run unchanged.
  `catch-up --kind docs` is accepted explicitly. Closes **FEEDBACK PAPER-97**
  (hit by the `magogi-foundation-brand` docs repo). `init-project` scaffolding
  for `docs` is out of scope (catch-up-only).

### Changed

- Internal: the duplicated finding lists in `catch-up` (the audit run and the
  `--apply` verify pass) are unified in a single `_collect_findings()`, so the
  two can no longer drift.

## [0.44.4] — 2026-06-02 — sync-prompt: the /sync recipe, resolved not copied

### Added

- **`mgf-common sync-prompt`** — prints the federation sync/conformance prompt
  (the procedure the `/sync` slash command runs), resolved through
  `mgf.standard.read_recipe("federation-sync-prompt")` rather than a
  hand-copied command body. The single source of truth is now the
  `mgf-standard` version a project pins, so the prompt can't drift. The
  prompt leads with the resolve-don't-copy entry points (`mgf-common agents` /
  `standards` / `catch-up`) and classifies a project as NEW / IN-SYNC /
  OUT-OF-SYNC, so one command serves every onboarding state. Degrades with an
  actionable error if `mgf-standard` is older than 0.1.3.

### Changed

- The `/sync` slash command (`.claude/commands/sync.md`) now leads with
  `mgf-common sync-prompt` (resolve-from-installed-package) instead of reading
  the recipe from a sibling checkout / Codeberg, with that read kept as the
  fallback.
- **Dependency floor raised** to `mgf-standard>=0.1.3` — where the recipes
  seam (`recipes_dir` / `list_recipes` / `read_recipe`) lands.

## [0.44.3] — 2026-06-01 — catch-up: federation-position check (wired onto libraries())

PATCH. `mgf-common catch-up` now audits a project's **place in the federation**
and whether its `mgf-*` dependency pins keep pace with the current roster —
resolved from `mgf.standard.libraries()`, not a scrape or a hand-copied
list. Advisory and read-only: it never auto-bumps a pin (that stays a
deliberate human call). The registry is a snapshot from the installed
`mgf-standard`; upgrade it for the freshest roster, then re-pin.

### Added

- `catch-up` "Federation position" finding (`_check_federation_position`,
  `_project_mgf_deps`): flags `mgf-*` pins behind the roster and surfaces the
  project's reverse-deps. Completes the federation-awareness mechanism
  (`mgf-standard` 0.1.2 registry data → `agents` map → `catch-up`).

## [0.44.2] — 2026-06-01 — agents: live federation map (the other projects)

PATCH. `mgf-common agents` now appends a **live federation map** — the roster
of the other `mgf-*` projects plus "you are here" (this repo's dependencies and
reverse-deps) — resolved from `mgf.standard.libraries()` (new in
`mgf-standard` 0.1.2), not a hand-copied list. This is the drift-proof way
an agent learns the *other* federation projects; it degrades silently if the
registry can't be resolved (the static briefing never depends on it).

### Added

- `mgf-common agents` federation-map block, via a new
  `mgf.common.cli._standards._resolve_libraries()` helper over the
  `mgf-standard` registry seam.

### Changed

- Dependency floor raised to `mgf-standard>=0.1.2,<0.2` (where
  `libraries()` lands).

## [0.44.1] — 2026-06-01 — finish-pass: federation artifacts relocated + briefing routing

PATCH. Completes the `mgf-standard` extraction. The remaining
federation-level artifacts that still lived in `mgf-common` moved to the
sibling (now a declared dependency, so still reachable), and the agent
briefing's feedback-routing now points standards work at `mgf-standard`.
Pairs with [`mgf-standard` 0.1.1](https://codeberg.org/magogi-admin/mgf-standard).

### Changed

- **Agent briefing (§7 routing).** `mgf-common agents` / `STARTHERE.md` now
  route standards / federation-contract / registry feedback to
  `mgf-standard`'s `FEEDBACK.md`; project-specific friction still goes to
  that project's own `FEEDBACK.md`. Lockstep-pinned by `test_agents`.
- **CLI doc pointers.** `mgf-common test` / `doctor` strings that cited the
  moved `test-supervisor-adoption` recipe now point at `mgf-standard`.

### Removed

- Federation-level artifacts relocated to `mgf-standard`:
  `docs/reference-consumers/`, the cookiecutter new-project scaffold
  (`templates/`), the PR-conformance auditor
  (`scripts/audit_pr_conformance.py`), and five process recipes/templates
  (`deps-md`, `feedback-md`, `pr-standard-test-plan`, `library-app-separation`,
  `test-supervisor-adoption`).

### Fixed

- Reference sweep (DOC-18): ~22 docs repointed from the moved content to
  `mgf-standard`.

## [0.44.0] — 2026-06-01 — standards corpus + federation contract extracted to `mgf-standard`

MINOR (breaking restructure; per **AP-03**, MINOR MAY break in 0.x). The
engineering-standards corpus (`docs/standards/`), the federation contract
(`FEDERATION.md`), the library registry (`MGF_LIBRARIES.md` + its generator),
the federation-pattern paper, and the federation-process recipes moved OUT of
`mgf-common` into the new
[`mgf-standard`](https://codeberg.org/magogi-admin/mgf-standard)
sibling — the federation's standards/contract source of truth. `mgf-common`
now **depends on `mgf-standard>=0.1,<0.2`**; `mgf-common standards <topic>`
and `mgf-common agents` resolve the rules through
`mgf.standard.standards_dir()`.

**Consumer impact: none at the import level** — `mgf.common`'s public API is
unchanged (`PUBLIC_API.{md,json}` identical). `mgf-standard` is pulled in
transitively, so the `standards`/`agents` CLI keeps working with no action.
What changed: the standards Markdown no longer ships in the `mgf-common`
wheel/repo (links to `docs/standards/X.md` now resolve via the CLI or the
`mgf-standard` repo), and `FEDERATION.md` is now a pointer stub to the
canonical contract in `mgf-standard`. No codemod required. Full guide:
[`docs/cutover/v0.44.0.md`](docs/cutover/v0.44.0.md).

## [0.43.0] — 2026-05-30 — TS-28 per-test perf attribution (TestRecord schema 1.1.0) + L3 self-conformance audit + local CI

MINOR (additive public API). Headline for the federation: `TestRecord` gains
the nullable `perf` field and `SCHEMA_VERSION` moves 1.0.0 → 1.1.0 (TS-28),
which `mgf-test-supervisor` 0.1.8 already consumes — so this release is what
re-aligns the two. Also lands the full L3 self-conformance audit (security +
logging + operability) and the local-CI tooling.

### CI hardening — running the Woodpecker pipeline locally (2026-05-29)

Surfaced by running the actual CI steps locally (`gitleaks`, `pip-audit`, and
`woodpecker-cli exec`), which exposed steps that had never genuinely run:

* **Fixed (CI):** the `test` step declared a **step-level `matrix:`**, which
  Woodpecker silently ignores (matrix is a top-level key) — `${PYTHON_VERSION}`
  never expanded and the 3.11/3.12/3.13 fan-out never ran. Replaced with three
  explicit `test-3.11` / `test-3.12` / `test-3.13` steps.
* **Fixed (CI):** the test steps installed `.[dev,observability]`, omitting
  cryptography (the `[vault]` extra) — so the 13 `EncryptedFileVault` tests
  **errored** instead of running. The extra is now installed in the test steps.
* **Fixed (CI):** allow-listed the t002 redaction-test fixture in
  `.gitleaks.toml` (a fake Bearer token the redactor test deliberately embeds),
  which was turning the secret-scan red.
* **Perf (PF-01):** migrated the last absolute-budget pin
  (`tests/unit/test_logging_perf.py`, an absolute 100 µs/record wall-clock
  budget that false-positived on a loaded CI container) onto the relative
  baseline harness as
  `tests/perf/test_perf_pins.py::TestFileSinkLoggingPerf` (median-based;
  excluded from the default gate, run in the nightly perf sweep).
* **Added (PF-04):** implemented the documented **`mgf-common bench rebaseline`**
  recorder (it was referenced across the docs but unimplemented).

### Fixed / Security / Added — L3 self-conformance audit (2026-05-29)

A full per-rule audit of the cornerstone against its own standards. Broadly
conformant; the highest-value gaps are fixed (the rest tracked in
`docs/inprogress/MGF_STANDARDS_CONFORMANCE.md`):

* **Security (CF-05/SC-01):** the encrypted-file-vault **passphrase is now a
  `pydantic.SecretStr`** — it can no longer be serialised in clear into
  `config.yaml` (`save_settings`) or a doctor runtime snapshot; the real value
  is read only at the crypto boundary. `passphrase`/`dsn`/`sentry_dsn` added to
  the default redaction key set.
* **Added — `mgf-common version`** (+ `--json`) and the bare **`mgf-common
  --version`** / `-V` flag: version + commit + Python/platform + key-dep
  versions (OP-01). This also unbreaks the release publish-smoke (WF-07), which
  called `--version` and aborted on exit 2.
* **Logging (LG-14/LG-17):** `JsonFormatter` now promotes the **`request_id`**
  correlation contextvar onto every record emitted inside a request, and
  auto-computes an exception **`fingerprint`** (`<module>.<qualname>`) on
  records with `exc_info`.
* **Fixed (EH-01):** `IdempotencyKeyConflictError` now subclasses
  `OperationError` (→ `AppError`) instead of bare `Exception`, so a top-level
  handler maps it to a typed exit/HTTP code.
* **Tests (TS-10):** added `tests/unit/security/` negative-pattern pins
  (no `shell=True`, `safe_load`-only, no pickle/eval/exec, no weak crypto) +
  an `__all__`-derived exception-hierarchy completeness test (TS-07/EH-01).
* **Perf (PF-01/03/04/05/07/08):** the perf pins moved to `tests/perf/` and are
  now **baseline-relative** — they compare against a generated
  `tests/perf/baselines.json` (tier-tagged, per-metric threshold) with numeric
  regression messages, instead of absolute wall-clock budgets (the PAPER-50
  anti-pattern). New `mgf-common bench rebaseline` re-records baselines (PF-04).
* **Added — log-volume-control filters (LG-13):** `RateLimitFilter`,
  `SampleFilter`, `DedupFilter` (thread-safe; bounded dedup) ship from
  `mgf.common.observability` — `handler.addFilter(RateLimitFilter(max_per_second=200))`.
* **Added — `mgf-common bundle` (OP-07):** writes a single **redacted**
  diagnostic tarball composing `version` + `explain` + `doctor` + `logs` + the
  on-disk crash reports — one artifact to attach to a bug report.
* **Operability (OP-08):** the crash-report directory is now bounded — the
  writer prunes to the most-recent 100 reports.
* **Tests (TS-01 / EH-09):** added the source↔test mirror lint
  (`tests/test_mirror.py` + `MIRROR_ALLOWLIST.txt`) and pinned `BootstrapError`'s
  actionable-hint suffix.
* **Lint (AP-08):** `from __future__ import annotations` is now mechanically
  enforced — `FA` (FA100/FA102) added to the ruff `select` (the codebase
  already passed). The API-design §14 matrix prose was corrected to match
  reality (AP-02 docstrings = DP-07 + review, not noisy ruff `D`).
* **Docs:** `docs/testing/` + `docs/archive/` bucket charters (DOC-03);
  `LogRecord` mutable-by-design rationale (DP-03); refreshed stale `DOCUMENTATION.md`
  §15 self-doc; PF-06 complexity annotations on the perf-pinned hot paths; a
  complete per-rule conformance tracker (every audit gap now closed).

### Changed — `MGF_LIBRARIES.md` is now a high-level guide + has a release gate

* **Richer registry.** `scripts/gen_mgf_libraries.py` now emits, beyond the
  at-a-glance index: a **Release & compatibility matrix** (each library's
  version + Python + the `mgf-common` window it accepts + ✅/❌ whether the
  current cornerstone satisfies it + its other mgf-* pins), and a
  **per-library detail table** for every library (version, Python, install,
  deps-with-pins, extras, third-party footprint, consumers, purpose).
* **Currency gate.** `gen_mgf_libraries.py --check` now fails on **either** an
  inconsistent pin **or** a STALE registry (committed file ≠ fresh render) —
  so "released a library but forgot to regenerate the registry" is a hard,
  catchable error. This is the mechanism behind the "update on every release"
  rule (REL-13 / DOC-18); both standards updated to reference it.

### Added — change-propagation mechanism (DOC-18) + standards v2.13

* **DOC-18 — the ripple rule.** A change to a coupled surface MUST update its
  dependents in the same commit. New in `DOCUMENTATION.md`: the rule, a
  **Change-Propagation Matrix** (§10.4 — "change X → also update Y, pinned by
  Z"), and a per-document **Propagation header** now on every rule-defining
  standards doc. Generalises the point-rules DOC-04 / DOC-08 / DOC-09 / DOC-17.
* **AG-14 — the agent-facing form.** Agents MUST walk the matrix + run
  `mgf-common doctor --standards` before reporting a change done.
* **`mgf-common doctor --standards` gained a DOC-18 check** — every
  rule-defining standards doc carries a Propagation header (fails otherwise).
* **README `Index of MUSTs` backfill** — added the supervisor family
  (TS-22..TS-28) and the documentation family (DOC-13..DOC-18) that had
  drifted out of the index; new "What's new in v2.13" section.

### Added — per-test perf attribution (TS-28)

* **`mgf.common.testing.perf_span(label)`** — a nestable, fail-open context
  manager that times a named code/log section. Costs a single context-var read
  when capture is inactive, so it's safe to leave wrapped around hot paths in
  production code, not just tests.
* **`--mgf-perf` pytest-plugin option** (env `MGF_PERF` / `MGF_PERF_DIR`) — when
  enabled, the `mgf_common` plugin captures, per test, a `cProfile`
  function breakdown **plus** any `perf_span` sections and writes one JSON
  artifact per test. The federation test supervisor (`mtest --perf`) reads
  these out-of-process and surfaces them in `run.jsonl`, the TS-27 failure
  bundle, and a run-level `perf-summary.md`. Capture is opt-in, fail-open
  (never changes a test's outcome), and bounded (top-N functions, capped spans).
* **New public schema types** in `mgf.common.testing`: `PerfSummary`,
  `PerfFunction`, `PerfSpan`, `PERF_SCHEMA_VERSION`, `DEFAULT_TOP_N`
  (`experimental`, TS-28).
* **`TestRecord` schema → `1.1.0`** — adds the nullable `perf` field carrying
  the per-test attribution (mirrors `PerfSummary.to_dict()`); fully
  backward-compatible (older records parse with `perf=None`). Defines the new
  rule **TS-28** in `docs/standards/TESTING.md`.

### Changed — at-rest log sink now emits the canonical `LogRecord` shape

* **`JsonFormatter` now embeds the canonical identity/version fields** —
  `level_no`, `app`, `app_version`, `lang`, `host`, `schema_version` — so an
  on-disk log line is a *valid canonical `LogRecord`* (parses via
  `LogRecord.from_jsonl_line` with real values, not defaults; is directly
  shippable to a collector). Closes the unified-logging review's **F1**: the
  at-rest sink no longer diverges from the live-stream / shipped shape.
  Additive (new top-level keys); `host` is resolved once at import (no per-record
  syscall); identity via a lazy cached `app_name()`/`app_version()` lookup.

---

## [0.42.0] — 2026-05-28 — unified logging: schema + cross-language emit + CLI + statistics

The first wave-set of the unified-logging program (design:
`docs/inprogress/UNIFIED_LOGGING_DESIGN.md`) — a canonical structured log
record every Magogi runtime emits, byte-identically; a `mgf-common logs`
CLI to locate/read/tail it; and per-app logging statistics. Additive
surface only (no removals) → MINOR bump; triggers a federation cohort-walk
(REL-10) widening every sibling's pin to `>=0.41,<0.43`.

### Added — unified logging (Wave 1)

* **`mgf.common.observability.LogRecord`** + **`LOG_SCHEMA_VERSION`** — the
  canonical cross-language structured log-record schema (the unified-logging
  linchpin, analog of `TestRecord`). Formalizes `JsonFormatter`'s wire shape
  (`ts`/`level`/`logger`/`msg`/…) and adds `app`/`app_version`/`host`/`lang`/
  `level_no`/`env`/`labels` + a `fields` context map. `from_python()` builds
  one from a stdlib record; `to/from_jsonl_line()` for the wire.
* **`describe_logging() -> LogInfo`** (+ `LogInfo`, `HandlerInfo`) — log
  introspection: the effective root level, active sinks + their targets,
  per-component logger levels in effect, and canonical sink locations
  ("where do my logs live?"). Reads the live `logging` tree.
* **Schema extensibility contract (OB-09):** `LogRecord` is built to grow
  without redesign or breakage — ad-hoc data goes in `fields`/`labels` (no
  schema change), typed promotion is additive + `LOG_SCHEMA_VERSION`-bumped,
  unknown keys from a newer schema are tolerated AND **losslessly preserved**
  through a round-trip (via `LogRecord.extra`), and only rename/remove/retype
  is breaking (MAJOR bump). Codified as standard **OB-09**.
* All `experimental`. Design: `docs/inprogress/UNIFIED_LOGGING_DESIGN.md`.
  **Note:** additive public surface → the next release is a MINOR (0.42.0)
  and will trigger a federation cohort-walk (REL-10).

### Added — unified logging (Wave 2)

* **`mgf-common logs`** — the command-line half of "access any Magogi app's
  logs at runtime and afterward" (decision D2: at-rest + local real-time
  first). Three actions over the rotating JSON sink:
  * `logs info` — where the logs live (file + crash/state dirs) and what
    components/levels appear (bounded tail-scan so it stays fast on big logs).
  * `logs tail` — print the last *N* records (at-rest read).
  * `logs follow` — stream new records live (`tail -f`, rotation-aware).
* Filters: `--level` (threshold — `WARNING` shows WARNING and above) and
  `--component` (logger-name substring). Target via `--file` or `--app NAME`
  (resolves the app's default `<state>/<app>/logs/<app>.log`). `--json` emits
  raw JSONL; otherwise a compact pretty form.
* In-process handler state remains `describe_logging()` (Wave 1); this reads
  the on-disk artifact. The per-app web log surface is Wave 3.

### Added — unified logging (Wave 1.5: cross-language emit)

* **Cross-language conformance contract (OB-10)** — `LogRecord` is the one wire
  shape *every* runtime emits (decision D1: Python + Zig + TS). mgf-common is
  now the pinned source of truth for that shape:
  * **`log_schema_manifest()`** (`mgf.common.observability`) — the
    language-neutral contract a Zig/TS porter builds against without reading
    Python: schema version, always-present `core` keys, full `emit_order`, each
    field's type + omit rule, and the serialisation `rules`.
  * **Golden artifacts shipped in the wheel** —
    `mgf/common/observability/schema/log_record_fixture.jsonl` (one line per
    schema corner) + `log_record_schema.json` (the manifest), generated by
    `tools/gen_log_record_schema.py` and pinned by
    `test_log_record_conformance.py` against the live dataclass (no silent drift).
  * **Reference emitters** under `clients/` — `typescript/logRecord.ts` (+ a
    runnable Node conformance test, ✅ green in mgf-common CI) and
    `zig/log_record.zig` (+ a `test` block, compiled in **mgf-hrb** CI — no Zig
    toolchain here). Each asserts byte-for-byte reproduction of the golden.
* **`LogRecord.to_jsonl_line()` now emits `ensure_ascii=False`** (raw UTF-8, no
  `\uXXXX` escaping) so the bytes match what Zig/TS produce natively. The
  ASCII-only golden is unchanged; non-ASCII messages now serialise identically
  across languages (pre-v1.0 wire refinement).

### Added — unified logging (Wave 4: statistics)

* **Per-app logging statistics (OB-11)** — *how much* an app logs, alongside
  the *what/where/how* of `describe_logging`:
  * **`LogStatsCollector`** — a `logging.Handler` that counts records: total,
    per-level, per-component (logger name), an overall + a recent-window rate,
    and the async-overflow drop count (summed from `AsyncJsonHandler`). Cheap
    (O(1), no I/O in `emit`), **bounded** (per-component map capped → `__other__`
    on overflow), and **fail-safe** (`emit` never raises).
  * **`LogStats`** — a frozen, JSON-able snapshot (`to_dict()`, versioned
    `schema_version`) for the per-app web surface (`/logs/info`) and the
    centralized shipper (Wave 5).
  * **`enable_log_stats()` / `current_log_stats()` / `disable_log_stats()`** —
    a process-wide collector lifecycle (idempotent install, snapshot, detach).
* Standard **OB-11** codifies the cheap/bounded/fail-safe contract.

---

## [0.41.13] — 2026-05-28 — release-gate hardening + §8 checklist brought current

### Changed — release gate (`scripts/gen_mgf_libraries.py`)

* The consistency gate (REL-13) now **lag-checks Python `@ git+…@vX`
  direct refs**, not just Zig `build.zig.zon` hashes — closing a blind
  spot where a git-pinned dependency lagging its published version
  passed the gate. It also nudges toward a PyPI window over a git
  direct ref where the dependency is published. (Caught + fixed
  `mgf-hrb`'s `mgf-test-supervisor @ git+ssh…@v0.1.1` → `>=0.1.6,<0.2`.)
  Tooling-only (`scripts/`, not shipped in the wheel).
* **`RELEASING.md` §8 self-review checklist brought current** — added a
  top-of-section "Release gate (run first): `gen_mgf_libraries.py
  --check` MUST be GREEN" pointer plus the missing checklist items
  (REL-10/11/12 cohort-walk, REL-13 consistency gate, AG-13 governance).
  The consolidated pre-tag checklist now matches the rules box.

---

## [0.41.12] — 2026-05-28 — federation library registry + release-consistency gate

### Added

* **`MGF_LIBRARIES.md`** (repo root) — the authoritative, always-current
  registry of every Magogi Foundation library: language, version,
  dependency graph (depends-on / used-by), and purpose, with **each
  library name hyperlinked to its `DEPS.md`**. Generated, not
  hand-maintained.
* **`scripts/gen_mgf_libraries.py`** — the generator + **federation
  release-consistency gate**. Scans the on-disk federation, regenerates
  `MGF_LIBRARIES.md`, and checks that every `mgf-*` → `mgf-*` dependency
  pin accepts the dependency's current version (`--check` = gate-only,
  exit 1 on RED). Stdlib-only; handles both Python (`pyproject.toml`)
  and Zig (`build.zig.zon`) manifests.

### Changed — standards

* **RELEASING `REL-13`** — every release MUST pass the consistency gate:
  "releasing one or more libraries means the whole federation is left
  consistent and verified." Generalizes REL-10 (mgf-common-minor
  breakage) to any library release; the supervisor's standing duty.
* **AGENTS `AG-13`** — only the mgf-common supervisor changes a library;
  other projects file update requests in the **target library's
  `FEEDBACK.md`**, never edit/PR/version-bump it themselves. Surfaced in
  the `mgf-common agents` briefing (item 7) + `STARTHERE.md` mirror +
  `FEDERATION.md` §1 + the registry header.

### Federation (companion, this wave)

* Gate's first run caught + fixed two pre-existing drifts: `mgf-alembic`
  and `mgf-fastapi` pinned `mgf-sqlalchemy` windows that excluded the
  current 0.5.1; walked both to `>=0.5,<0.6` (mgf-fastapi verified, 159
  tests green; mgf-alembic has no runtime mgf.sqlalchemy import). Gate
  now GREEN across 26 libraries.

---

## [0.41.11] — 2026-05-28 — agent-command consistency + standards-command completeness

### Changed — `mgf-common agents`

* **Cross-project feedback discipline (AG-11)** — the agent briefing's
  feedback item now mandates filing in the **target project's**
  `FEEDBACK.md` (the repo that caused the friction), not the reporter's
  own, and requires triage-ready detail in every paper (what hurts /
  why it matters / concrete evidence — repro, `file:line`, commit SHA,
  version / suggested shape). Mirrored in `STARTHERE.md` (pin-tested).
* **`mgf-common agents --project` (AG-12)** — new project-scoped mode:
  prints the current project's own agent docs (`docs/claude/CLAUDE.md`,
  falling back to `AGENTS.md`). Because `mgf-common` is installed
  wherever it's a dependency, **every** project — including pure
  libraries and non-Python repos that can't ship their own CLI — now
  has the same `agents` capability scoped to itself, via this one
  universal command. `mgf-common agents` = federation-wide;
  `--project` = this project.

### Changed — `mgf-common standards`

* **Alias map completed** — every standards doc is now reachable by a
  short alias (agents, releasing, observability, performance, naming,
  deploy, operability, time, workflows, presentation, docs, …) in
  addition to its full name. Added a completeness test that fails if a
  new standards doc ships without an alias, and a test that every alias
  resolves to a real doc. Help text + topic-index footer corrected
  (they listed a stale subset).

### Changed — standards (AGENTS.md)

* **AG-11** (cross-project feedback routing + triage-ready detail) and
  **AG-12** (project-scoped `agents --project`) added; **AG-04** updated
  to defer routing to AG-11.

---

## [0.41.10] — 2026-05-28 — DOC-01 v2.11/v2.12 standards: uniform root-doc baseline + DEPS.md

### Changed — standards (DOC-01 v2.11: uniform root-doc baseline)

* **`DOCUMENTATION.md` DOC-01 + §2.0.1 matrix** — the universal-MUST
  root-doc set is expanded to a single uniform baseline for **every
  project** (cornerstone, sibling, AND application): `README.md`,
  `LICENSE`, `CHANGELOG.md`, `CONTRIBUTING.md`, `FEEDBACK.md`,
  `SECURITY.md`, `AGENTS.md`, `FEDERATION.md`, `STARTHERE.md`,
  `DEPS.md` — plus a `docs/` directory at the repo root. The old
  shape-dependent (⚠️) rows are now ✅ universal. `PUBLIC_API.md`/`.json`
  stay library-only. Anti-duplication carve-out: non-cornerstone
  `FEDERATION.md` is a pointer stub (DOC-17, not a copy).
* **`DEPS.md` replaces `USERS.md`** (DOC-16 v2.12) — a bidirectional
  dependency manifest: §1 what this project depends on + §2 who depends
  on it (the former `USERS.md` roster). `USERS.md` is removed from the
  requirement and may be deleted. mgf-common's own `USERS.md` →
  `DEPS.md` (this commit).
* **`FEDERATION.md` §3 / §6** + the `mgf-common agents` briefing (item 4)
  + `STARTHERE.md` mirror updated to the new baseline (pin-tested).
* **`mgf-common catch-up`** (`_check_federation_contract`) now gates on
  the full baseline + `docs/` dir for every project (USERS.md promoted
  from sibling-only to universal). Tests updated.

### Added — templates + cornerstone self-audit

* **`docs/recipes/deps-md-template.md`** + **`docs/recipes/federation-pointer-stub-template.md`**
  — fork-and-fill templates for the two new required root files, used
  to roll the baseline across the federation.
* **`docs/inprogress/MGF_STANDARDS_CONFORMANCE.md`** — the cornerstone's
  own **L3** self-conformance tracker (DOC-15). mgf-common defines the
  corpus and is now the worked L3 example, completing federation-wide
  DOC-15 coverage.

### Federation rollout (companion, this wave)

* The DOC-01 v2.11/v2.12 baseline + DEPS.md conversion was rolled out
  across **25 sibling repos** (framework siblings, the 4 newest, the
  mgf-vm family, the hrb cluster, test-supervisor, website-template),
  and lagging `mgf-common` pins were aligned to `>=0.41,<0.42`. Those
  changes live in the sibling repos; this entry records the cornerstone
  side.

---

## [0.41.9] — 2026-05-28 — test-suite hardening (tier-0 smoke + fuzz)

### Added

- A fast tier-0 `smoke` set (build-broken signal in <1s) and Hypothesis fuzz
  over the `Redactor` + config/settings parsing (200 examples) — both held:
  no secret leak, no crash, typed `AppConfigError` on bad input.
- `USERS.md` refreshed with the four extracted `mgf-tele-*`/`mgf-livepush`/
  `mgf-auth` siblings + `mgf-test-supervisor`.
- FEEDBACK PAPER-95 + PAPER-96 (mgf-test-supervisor adoption + class-test bug).

### Notes

- Test + docs only; no public API change.

---

## [0.41.8] — 2026-05-28 — federation registry: 4 new siblings + supervisor feedback (PAPER-95/96)

### Changed — federation registry

* **Four new `mgf-*` siblings registered** in the canonical
  registries so consumers + their AI agents discover them:
  `mgf-auth` (session-token + webhook-signature verification),
  `mgf-livepush` (Server-Sent Events over a pub/sub broker + slot
  caps), `mgf-tele-core` (vendor-neutral telematics seam), and
  `mgf-tele-teltonika` (Teltonika Codec 8/8E/12 decoder). Added to
  `_FEDERATION_SIBLINGS` (the `mgf-common agents` briefing — kept in
  sync with the `STARTHERE.md` mirror, pin-tested) and to the
  `FEDERATION.md` §5 "need X → use Y" table. All four were extracted
  from PlasmaMapper (consumer #1) and approved per its
  sibling-extraction proposal.

### Added — tests

* Fast tier-0 smoke set + `Redactor` / config security fuzz
  (commit `0fff296`).

### Feedback

* **PAPER-95** (filed) — `mgf-test-supervisor`: a tier selection
  matching zero tests reports a passing run (silent adoption cliff)
  + a cluster of safety/UX hardening items.
* **PAPER-96** (SHIPPED in mgf-test-supervisor) — supervisor
  silently quarantined class-based tests (JUnit reconciliation bug →
  false green); found + fixed.

---

## [0.41.7] — 2026-05-27 — docs wave: web-SSE recipe (PAPER-93) + PAPER-94 move

### Added — recipes

* **`docs/recipes/web-sse.md`** — new recipe: Server-Sent Events
  through a dev/prod proxy. Captures the four non-obvious root
  causes behind "SSE buffers through the proxy" — the three
  SSE-only buffering-disable response headers
  (`Cache-Control: no-cache, no-transform`,
  `Content-Encoding: identity`, `X-Accel-Buffering: no`) +
  identity-`TransformStream` flush, the forwarding-`fetch`
  `duplex: 'half'` requirement, and the two Playwright gotchas
  (`networkidle` never fires with an open `EventSource`;
  `webServer` boots before `globalSetup`). Closes **PAPER-93**.
  Indexed in `docs/recipes/README.md` (both tables) and the
  web-standards recipe index (`docs/standards/web/README.md`).

### Changed — feedback

* **PAPER-94** (`tenant_session` + `paginate_cursor` RLS pattern)
  MOVED to `mgf-sqlalchemy` per DOC-13 — the mgf-common entry is
  now a redirect stub. Shipped sibling-side as `paginate_cursor`
  in `mgf-sqlalchemy` 0.5.0 (`tenant_session` already shipped at
  v0.1).
* **PAPER-93** flipped to SHIPPED.

### 2026-05-26 — PRESENTATION_LAYER §8.1 + drift-detector + test-plan template

Companion to mgf-hrb + hrb-app's "hardened pair" lock-in
(commits TBD in those repos). Federation-side additions:

* **`docs/standards/PRESENTATION_LAYER.md` §8.1** — new
  "hardened-pair binding" sub-section: a specific adopter pair
  MAY declare PR-* binding for itself without waiting for
  federation-wide promotion, provided five hardenings ship
  (per-rule conformance tests, adversarial-input pins, import-
  linter meta-test, cross-version feature-detection, conformance-
  tracker drift detector). The reference pair `mgf-hrb` +
  `hrb-app` has done so.
* **`scripts/audit_pr_conformance.py`** — new federation-shared
  drift detector. Parses a project's `MGF_STANDARDS_CONFORMANCE.md`
  §PR section + cross-checks against `lint-imports` live output;
  fails if a row claims `PR-XX ✅` while the matching contract is
  broken. Reusable across siblings.
* **`docs/recipes/pr-standard-test-plan-template.md`** — new
  paste-ready template for the "hardened pair" five items. The
  second-adopter shortcut.

The PR-* standard itself remains DRAFT until a second sibling-
pair adopts. When that happens, the DRAFT marker drops from
PRESENTATION_LAYER.md §8 and the rules become federation-wide
L1-binding.

## [0.41.6] — 2026-05-27 — federation-hygiene wave (PAPER-58 / -62 / -71 / -79-A / -87-B)

Five small fixes from the post-0.41.5 backlog review. All
additive or bug-shape; no breaking change. PAPER-62 ships
in lockstep with `mgf-test-supervisor` 0.1.4 (the MARKER_TIER
alignment pin makes the two packages a coupled pair).

### Fixed (PAPER-58 — `doctor` `[vault]` warning downgraded to ℹ)

`mgf-common doctor` previously emitted ⚠ for a missing
`[vault]` extra on every invocation in any consumer that ships
its own credential layer (vm-vmanager-app, iosRecovery, …). The warning
was correct in the abstract but irrelevant for consumers who
don't use `mgf.common.vault.*`. Severity is now ℹ
(informational) when `[vault]` is missing — the recommended-action
still tells the operator how to install it should they want the
surface. `[observability]` stays at ⚠ — it's the federation
default and a missing observability extra is something the
operator should notice.

Per-extra severity policy added (`missing_severity` table in
`_check_extras`); future extras can opt into the same
"informational when BYO is a normal choice" pattern. 2 new
regression tests under `TestDoctorBundleCheck` (ℹ for vault,
⚠ stays for observability).

### Added (PAPER-62 — `MARKER_TIER` for `integration` + `slow`)

`mgf.common.testing.MARKER_TIER` previously declared 11 of the
13 federation markers. `integration` and `slow` fell through to
`DEFAULT_TIER = 1` via `effective_tier(...)`, getting a tier-1
wall-clock budget regardless of what the tests actually do — an
`integration` test that launches subprocesses doesn't fit
tier-1's `<0.3s` per-test budget. Both now have explicit tiers:

- `integration` → tier-2 (cross-module / may launch subprocesses;
  fits the 2s per-test budget).
- `slow` → tier-3 (long-signal tier alongside `perf` / `stress`
  / `e2e`).

Pure additive change. Released in lockstep with
`mgf-test-supervisor` 0.1.4 (which carries the same additions in
its alignment-pinned mirror); the
`TestMarkerTierAligned::test_marker_tier_mappings_match` pin
stays green on both sides. Updated parametrized pin test in
`TestCanonicalMarkerTier::test_canonical_mapping_pinned`.

### Fixed (PAPER-71 — broker freshness check filters test files)

The federation-recommended `tests/_broker.py` freshness check
(currently in `mgf-hrb`; future daemon-bearing siblings will
inherit the shape) globbed every `*.zig` under the source tree
as a "build input." Editing a `*_test.zig` file would flip the
broker binary to "stale" even though `zig build` doesn't link
tests into the binary. mgf-hrb's `tests/_broker.py` now has a
`_is_build_input(p)` filter that excludes `*_test.zig` files
and anything under a `tests/` path.

**Cross-sibling change:** the fix is in `mgf-hrb` (the only
current daemon-bearing sibling). This release documents the
pattern in mgf-common's FEEDBACK so future siblings adopting
the freshness-check shape don't re-introduce the bug. A
federation-shared recipe codifies post-adoption by a second
daemon-bearing sibling (TESTING.md §19.6 rule of two).

### Added (PAPER-79 Approach A — `sibling_rename.md` recipe)

`docs/recipes/sibling_rename.md` — eight-surface checklist for
renaming a federation sibling (`mgf_x → mgf-x` separator drift,
`mgf-ubs → mgf-hrb` whole-name). Covers: sibling code + PyPI +
Codeberg + mgf-common refs + each consumer's pyproject +
`[tool.uv.sources]` + active venv `_editable_impl_<old>.pth` +
on-disk husks. Replaces the ad-hoc sed scripts that previously
lived in each rename's `.claude/settings.local.json`.

Recipe index updated (`docs/recipes/README.md`) — new
**Federation operations** section also picks up the previously-
unindexed `cohort-release-wave.md`. Approach B (CLI helper:
`mgf-common migrate-sibling <old> <new>`) and Approach C
(federation registry) remain deferred — register as future
structural work once a third rename event lands.

### Added (PAPER-87 Approach B — `migrate` chain composition)

`mgf-common migrate <from> <to> <path>` previously required an
atomic registration for the exact `(from, to)` pair. A consumer
on `mgf-common>=0.37,<0.38` bumping to `0.41` got
`unsupported migration: 0.37 → 0.41` even though a `(0.38, 0.39)`
hop existed in range. The codemod now composes a chain:

1. **Atomic registration first.** `(0.38, 0.39)` is a direct
   match → returns just that hop. Preserves the v0.41.5
   single-hop output shape.
2. **Range composition.** Otherwise, collect every registered
   `(s, t)` where `from_version <= s` AND `t <= to_version`.
   Apply in source-version-ascending order. Concatenated rules
   are safe because identifier anchoring + per-rule import
   gating make each hop idempotent.

A composed request surfaces the chain narrative:

```
mgf-common migrate 0.37 0.41  target: /path
Composed migration chain:
  0.38 → 0.39
```

Closes PlasmaMapper's canonical scenario (bumping 0.37 → 0.41
mechanically). Unsupported requests still produce the
supported-migrations list + a pointer to Approach B for cases
where the range simply has no registered migrations.

New `_parse_minor` + `_resolve_migration_chain` helpers in
`src/mgf/common/cli/_migrate.py`. 6 new tests in
`TestPaper87ApproachBChainComposition` (PlasmaMapper scenario,
three-hop composition, unsupported-out-of-range, atomic-wins-over-
composition, dry-run threading, idempotency).

### Federation test sweep (release verification)

Targeted re-runs after each edit:
- `TestQueryPypiMgfCommonPinTriState` + cross-sibling — unchanged from 0.41.5
- `TestPaper87V038ToV039Migration` + `TestPaper87ApproachBChainComposition` — 11 passed
- `TestCanonicalMarkerTier::test_canonical_mapping_pinned` — 13 markers (was 11)
- `TestMarkerTierAligned::test_marker_tier_mappings_match` — green against `mgf-test-supervisor` 0.1.4
- `Doctor [vault] is ℹ` + `[observability] stays ⚠` — 2 passed

### Cohort note

Ships paired with `mgf-test-supervisor` 0.1.4 (additive
`MARKER_TIER` mirror). The supervisor's dev-extras pin in
`mgf-common/pyproject.toml` stays at `>=0.1.2,<0.2`; 0.1.4 is
in-window. No federation cohort-walk required beyond this pair —
`mgf-common` 0.41.6 doesn't change any sibling's pin upper-bound.

---

## [0.41.5] — 2026-05-27 — catch-up false-positive fix + N818 codemod + hatch-block cleanup (PAPER-86 / PAPER-87 / PAPER-88)

Three consumer-reported papers from the 2026-05-26 iosRecovery /
PlasmaMapper triage cycle. All three are mechanical fixes to the
catch-up + migrate tooling — no public API change, no surface
lockdown impact.

### Fixed (PAPER-86 — cross-sibling pin check false-positive)

PAPER-84's cross-sibling pin-compatibility gate (shipped 0.41.2)
conflated two structurally different "no pin found" cases:
genuinely unreachable (network failure / 404 / parse error) vs.
sibling reachable but with no unconditional `mgf-common` runtime
dependency (e.g., `mgf-test-supervisor` lists `mgf-common` only
behind `extra == "dev"`). Both paths returned `None`, and the
caller's Approach C fallback refused the bump in both. iosRecovery
saw stale refusals against cohorts that were demonstrably safe to
walk forward.

`_query_pypi_mgf_common_pin` now returns one of three states:

- `str` (constraint) — sibling has an unconditional runtime
  `mgf-common` dep; check it.
- `_NO_RUNTIME_PIN` sentinel — sibling reachable on PyPI but has
  no unconditional `mgf-common` dependency (extras-only, or
  absent). Cannot block the bump; cross-sibling check skips
  silently.
- `None` — unverifiable (network / HTTP / parse failure).
  Conservative refuse via Approach C fallback preserved.

Detection scans `requires_dist` for the `; extra ==` environment
marker and treats every extras-gated occurrence as non-binding. If
ANY unconditional `mgf-common` requirement is present (with or
without a constraint), that wins. Bare `mgf-common` (no version
spec) returns an empty constraint that `_version_satisfies_constraint`
accepts vacuously. The cross-sibling gate stays strictly more
conservative than necessary in the unverifiable branch.

10 new tests in `TestQueryPypiMgfCommonPinTriState` (7 probe-shape
pins) + `TestCrossSiblingCheckHandlesNoRuntimePin` (3 gate-shape
pins). Existing 6 `TestCheckCrossSiblingPinCompatibility` tests
pass unchanged — the new sentinel value is opt-in; legacy `str`
and `None` mocks behave identically to before.

### Added (PAPER-87 — `(0.38, 0.39)` N818 codemod)

`mgf-common migrate 0.38 0.39 <path>` now applies the twelve
PEP-8 N818 exception renames that landed in v0.39.0:

```
HttpBadRequest           → HttpBadRequestError
HttpUnauthorized         → HttpUnauthorizedError
HttpForbidden            → HttpForbiddenError
HttpNotFound             → HttpNotFoundError
HttpConflict             → HttpConflictError
HttpGone                 → HttpGoneError
HttpUnprocessable        → HttpUnprocessableError
HttpServiceUnavailable   → HttpServiceUnavailableError
ProcessTimeout           → ProcessTimeoutError
ProcessNonZeroExit       → ProcessNonZeroExitError
ProcessSignaled          → ProcessSignaledError
ServiceNotReady          → ServiceNotReadyError
```

Identifier-anchored (`\b` on both sides) → safe across imports,
isinstance checks, subclass declarations, except-clauses, and
raise sites. Gated on `mgf.common.exceptions` so a consumer's own
`ProcessTimeout` domain class in an unrelated module is NOT
touched. Idempotent: a second pass finds the post-rename names
already present and produces no further changes.

PlasmaMapper this session needed this fix — bumping the pin from
0.37 → 0.41.4 hit TS-04's `filterwarnings = ["error"]` promoting
`MgfDeprecationWarning` to a hard failure across nine files. The
aliases survived v0.39 → v0.40 as deprecation shims; v0.41's
pre-v1.0 cleanup removed them. The codemod is now what every
consumer crossing the rename needs.

Approach B (cross-version composition: `migrate 0.37 0.41` walks
0.38→0.39, 0.39→0.40, 0.40→0.41 in sequence) is the next
structural step; deferred to v0.42.

5 new tests in `TestPaper87V038ToV039Migration`.

### Fixed (PAPER-88 — spurious `[tool.hatch.metadata]` block)

`mgf-common catch-up --apply` previously inserted
`[tool.hatch.metadata] allow-direct-references = true` whenever
hatchling was the build backend, regardless of whether the
inserted dep actually used a PEP 508 direct-reference form. After
the v0.41.1 switch to the plain PyPI form for
`mgf-test-supervisor`, this left every new adopter with a dead,
misleading config block — contradicting catch-up's own narrative
("new adopters get the plain form").

`_apply_supervisor_wiring` now gates the hatch-metadata insertion
on `any("@" in dep for dep in combined_dev_deps)`. The block is
added only when at least one dev dep genuinely uses the direct-
reference form (legacy `mgf-test-supervisor @ git+ssh://...`, or
any other direct-ref dep). Pure-PyPI projects get a clean
pyproject.

Existing `test_full_wiring_from_minimal_hatchling` rewritten to
match the post-PAPER-88 contract (renamed
`test_wiring_from_minimal_hatchling_skips_hatch_block`); new
`test_hatch_block_added_when_direct_ref_dep_present` exercises
the positive case.

### Federation test sweep (release verification)

Targeted PAPER-86/87/88 surfaces re-run after each edit:
- `TestQueryPypiMgfCommonPinTriState` — 7 passed
- `TestCrossSiblingCheckHandlesNoRuntimePin` — 3 passed
- `TestPaper87V038ToV039Migration` — 5 passed
- `TestApplySupervisorWiring` — 8 passed (1 renamed, 1 added)

---

## [0.41.4] — 2026-05-26 — federation cohort-release procedure (REL-10/11/12 + recipe)

PAPER-85's paths #2 (graceful pytest-denial fallback) and #3 (Phase A / Phase B structural split) elevated from "documented as available" to "shipped as federation standards." The maintainer's framing — *"we need to have better procedure to sync and ship this library and all siblings"* — promoted the cohort-walk pattern from ergonomic gap to first-class discipline.

### Added — new RELEASING rules

Three new rows in `docs/standards/RELEASING.md`:

- **REL-10**: a `mgf-common` minor bump that breaks any sibling's pin upper-bound MUST trigger a federation cohort-walk release wave. Every sibling that pins `mgf-common<X.Y` MUST publish a release widening its window BEFORE the new minor is announced to consumers.
- **REL-11**: cohort waves MUST follow the Phase A / Phase B split. Phase A is parallel mechanical edits (per-sibling sub-agents); Phase B is sequential verification + ship driven by the maintainer. Boundary is structural, not accidental.
- **REL-12**: Phase A agents MUST carry a graceful pytest-denial fallback. If pytest is denied (Claude Code permission gate, sandbox restriction, etc.), the agent stops at that step, reports "edits ready for verification" with file diff summary, hands back WITHOUT committing / pushing / publishing. Maintainer runs pytest in parent session and proceeds with Phase B.

### Added — canonical recipe

New file `docs/recipes/cohort-release-wave.md` (~280 lines) — the canonical procedure REL-10/11/12 point at. Sections:

- When to run the recipe (trigger conditions).
- Phase A / Phase B visual model + rationale.
- Pre-Phase-A audit (scan each sibling for usage of mgf-common deletions before launching agents).
- The canonical Phase A agent prompt template with REL-12's pytest-denial fallback clause inline + a 350-word report-back contract.
- Phase B sequential ship script (commit / tag / push / build / publish / verify) using the federation's token-discovery pattern.
- Failure modes + recovery (pytest denial, pytest actual-failure, PyPI direct-ref rejection, PyPI first-time rate-limit, CDN propagation lag).
- Wave-coordination tracker pattern + post-wave PAPER-84 verification.

### Closed

- **PAPER-85** flipped to ✅ SHIPPED (all three paths). The original "path #1 shipped + #2/#3 documented as available" state was upgraded to "all three paths shipped" in this release.

### Consumer impact

For consumers using `mgf-common standards releasing`: three new rules visible in the rules box + cross-references to the new recipe. For maintainers running the next cohort-walk release wave: the recipe is the explicit procedure; the agent template is copy-pasteable. For agents reading the federation's docs corpus: REL-10/11/12 are the binding rules they cite when proposing a cohort wave or refusing one.

## [0.41.3] — 2026-05-26 — PAPER-78/80/82/83 federation hygiene wave

Four open papers from the iosRecovery + hrb-app feedback batch shipped together in one release. All non-blocking improvements composing with PAPER-84's cohort-safety work.

### PAPER-78 — broken-install fallback for `mgf-common agents`

`AGENTS.md` (the file managed by `mgf-common catch-up` in every federation project) now carries a fallback paragraph: when `mgf-common agents` fails to run (typical cause: stale editable install pointing at a renamed source tree), the consumer reads the same text from `mgf-common`'s `STARTHERE.md` directly. The CLI's output and that section are pin-tested for identity. Both the root `AGENTS.md` + the `init-project` scaffolder template were updated; existing consumers get the fallback paragraph on next `catch-up --apply`. Approach A from PAPER-78's three-option recommendation; Approach C (standalone CLI) deferred to a follow-up paper.

### PAPER-80 — auto-derive the federation sibling list

`_agents.py` gains `_FEDERATION_SIBLINGS: tuple[str, ...]` constant; the briefing's `mgf-* sibling libraries (...)` enumeration is rendered from it via f-string interpolation. Adding a new sibling is now a single-line constant update; the rendered briefing + STARTHERE.md mirror both refresh on next test run (the existing pin test catches the diff). 3 new tests in `TestFederationSiblings` pin the constant against the rendered output + the federation baseline. Approach B from PAPER-80's options; Approach A (federation-registry, composes with PAPER-79) deferred.

### PAPER-82 — multi-minor cutover guide for v0.35..v0.41.2

New consolidated cutover doc `docs/cutover/v0.35-through-v0.41.md` covering the 7-minor gap that previously had zero migration documentation. 9 per-version stub files (`v0.35.0.md` through `v0.41.2.md`) cross-reference the span doc. The consolidated guide covers the three reshaping waves (feature surface expansion / stability promotions / cornerstone reshape) + the 7 deletions consumers may need to migrate from + the catch-up-driven recovery path. Future per-minor cutover docs revert to per-version pattern.

### PAPER-83 — catch-up audits `[tool.uv.sources]` paths

New audit row `_check_uv_sources_paths` in catch-up. Resolves each `[tool.uv.sources]` `path = "..."` value; surfaces missing paths with a separator-drift suggestion when the alternate-separator sibling exists (e.g., suggests `../mgf-common` when the entry says `../mgf_common` but only the hyphenated form exists on disk). This is the audit that would have caught the iosRecovery editable-install drift before it became PAPER-78. Read-only — path choice is project-specific (auto-rewrite could fix surface drift but mask deeper path-mismatch). 6 new tests in `TestCheckUvSourcesPaths`.

### Tests

Full pytest count: 1808 → 1817 (+9 net new; PAPER-80 contributed 3, PAPER-83 contributed 6).

### Consumer impact

For consumers using `catch-up --apply`: the new audit rows (PAPER-83) surface drift automatically. PAPER-78's fallback text + PAPER-80's auto-derived sibling list are silent improvements that show up in the next `mgf-common agents` invocation. PAPER-82's cutover doc unblocks consumers crossing more than one minor.

## [0.41.2] — 2026-05-26 — PAPER-84 + PAPER-81: cross-sibling pin consistency check

**🔴 BLOCKER fix.** iosRecovery's adoption session surfaced the
worst failure mode catch-up could produce: `--apply` bumped
mgf-common's pin from `>=0.38,<0.39` to `>=0.41.1,<0.42`, but
iosRecovery also depends on `mgf-http>=0.4,<0.5`. mgf-http 0.4.1
(PyPI latest) pins `mgf-common<0.38,>=0.36` — the new pin is
mutually exclusive with the sibling pin. `uv sync` fails with
"your project's requirements are unsatisfiable." catch-up reported
the project as healthy with 5 fixes successful; reality was the
opposite. This release closes the gap.

### Added

- New function `_check_cross_sibling_pin_compatibility(target_dir,
  proposed_version)` in `src/mgf/common/cli/_catch_up.py`. Scans
  `[project.dependencies]` + `[project.optional-dependencies].*`
  for federation siblings (every `mgf-*` package); queries PyPI
  for each sibling's published `Requires-Dist` line for
  mgf-common; checks whether the proposed bump target satisfies
  each sibling's pin. Returns `(safe_to_bump, [conflict_msgs])`.
- New helper `_query_pypi_mgf_common_pin(package, version=None)`:
  stdlib-only PyPI JSON-API query that extracts a sibling's
  mgf-common Requires-Dist constraint. 5s timeout; returns None
  on any failure (network, parse, package not found).
- New helper `_version_satisfies_constraint(version, constraint)`:
  stdlib-only PEP-440 subset check (`>=`, `<=`, `<`, `>`, `==`,
  `!=`). No new dep (`packaging` would be cleaner but adds a
  runtime dependency for what is a small regex-tier check).

### Changed

- `_check_pyproject_pin` now gates BOTH pin-bump fix-attachment
  sites (editable + non-editable branches) on the cross-sibling
  consistency check. If a conflict is detected:
  - The warn finding is returned WITHOUT a `fix=` callable.
  - The detail surfaces the specific sibling + the specific pin
    constraint that excludes the target version.
  - Plus the workaround prose: "wait for the sibling to publish
    a release that accepts mgf-common <target>."
- Fallback when PyPI lookups fail (Approach C from PAPER-84): the
  check refuses the bump and explains why. Pessimistic — but the
  cost of an unnecessarily-blocked bump is just "run uv sync
  manually" vs the cost of a successful-looking catch-up that
  silently broke `uv sync`.

### Tests

11 new tests across two classes:
- `TestVersionSatisfiesConstraint` × 5 — pins the stdlib version
  comparator against PEP-440 edge cases including the iosRecovery
  canonical scenario.
- `TestCheckCrossSiblingPinCompatibility` × 6 — no-other-siblings
  / sibling-compatible / sibling-conflicting (the iosRecovery
  scenario) / PyPI-fetch-failure-fallback / multi-sibling-all-
  compatible / multi-sibling-one-blocks.

Full pytest: 1808 passed (was 1797; +11 net new).

### Cross-paper coverage

- **PAPER-81** (high-severity follow-up to PAPER-65) was about
  "`catch-up --apply` rewrites the pin without the `--check-pypi`
  the report says is required." This release's gating logic
  composes with the PAPER-65/PAPER-68 PyPI-check: now BOTH the
  PyPI-availability check AND the cross-sibling check must pass
  before a bump is offered. PAPER-81 closes alongside PAPER-84.
- **PAPER-79 / PAPER-80** (sibling-registry concerns) compose
  with this work; the registry would let catch-up avoid the
  per-sibling PyPI round-trip. Future paper, separate session.
- **PAPER-82 / PAPER-83** are separate concerns (cutover docs +
  uv.sources audit). Not in scope for this release.

### Consumer impact

For consumers WITHOUT federation siblings: no behavior change.
catch-up's pin-bump fix continues to work as before.

For consumers WITH federation siblings: bumps are now safety-
gated. A bump is offered only when:
- mgf-common's new version is published on PyPI (PAPER-65/68), AND
- Every federation sibling in the consumer's deps accepts that
  version (PAPER-84).

Federation siblings still pinning `mgf-common<0.41` (mgf-http
0.4.1 + others — see Q-4) need to release versions that accept
0.41 before this gate opens for their consumers. The federation
Q-4 backlog tracks the remaining sibling pin-bumps.

### Acknowledgement

Thanks to the iosRecovery agent for the precise reproducer +
the three-approach analysis in PAPER-84. The implementation lands
Approach A + C composed as recommended.

## [0.41.1] — 2026-05-26 — mgf-test-supervisor PyPI dep cleanup

`mgf-test-supervisor` v0.1.2 landed on PyPI 2026-05-26 (the first
PyPI publish of the extracted sibling library; coordinated with
this release). With the sibling on PyPI, mgf-common can drop the
direct-reference (`@ git+ssh://...`) dev dep + the
`[tool.hatch.metadata] allow-direct-references = true` hatchling
opt-in — both were the v0.41.0 workaround for the Codeberg-only
distribution. The dep simplification ALSO unblocks PyPI publish
for mgf-common: PyPI rejects packages with direct-reference deps
in their metadata.

### Changed

- `pyproject.toml` `[project.optional-dependencies].dev`: replaced
  `mgf-test-supervisor @ git+ssh://git@codeberg.org/magogi-admin/mgf-test-supervisor.git`
  with the plain PyPI dep `mgf-test-supervisor>=0.1.2,<0.2`.
- `pyproject.toml`: removed the `[tool.hatch.metadata]
  allow-direct-references = true` block. No longer needed once the
  dep is plain.
- `_SUPERVISOR_DEV_DEPS` constant in `src/mgf/common/cli/_catch_up.py`:
  updated to the plain PyPI form. Consumers running `catch-up
  --apply` against fresh projects from 0.41.1 onward get the plain
  dep inserted (was git+ssh in 0.41.0).
- `_check_test_supervisor_adoption`'s warn-message remediation
  snippet: same swap (`mgf-test-supervisor>=0.1.2,<0.2`).
- 2 tests in `tests/unit/cli/test_catch_up.py` updated to expect
  the plain dep form.

### Compatibility

Consumers on mgf-common <0.41.1 who ran `catch-up --apply` carry
the legacy `@ git+ssh://...` form + `[tool.hatch.metadata]
allow-direct-references = true` in their pyproject. **Both still
work** — catch-up doesn't auto-rewrite existing dev-dep entries.
To opt into the cleaner form: hand-edit the pyproject (drop the
hatch block + change the dep to `mgf-test-supervisor>=0.1.2,<0.2`),
then re-run `uv sync`.

This release is the v0.41 patch that lets mgf-common itself land
on PyPI (the 0.41.0 publish attempt was rejected by PyPI's
direct-reference policy). Functionally identical to 0.41.0 from
a consumer's API perspective.

## [0.41.0] — 2026-05-26

**Headline: v1.0-readiness pass + federation tool-discoverability + 7 sibling-feedback papers shipped.** A consolidation release: completes the test-supervisor extraction begun at 0.40.0, makes the federation's tool choices (Woodpecker CI + mgf-test-supervisor) discoverable through the agents briefing + catch-up audit, and ships responses to all 7 actionable FEEDBACK papers filed by sibling adopters (hrb-app + mgf-hrb) on 2026-05-26.

### Test supervisor extraction (continued from 0.40.0)

The 0.40.0 work landed the test supervisor in-tree at `scripts/_mtest/`. This release completes the extraction to the published sibling library `mgf-test-supervisor` v0.1.x (Codeberg → PyPI), then propagates the extraction across the federation:

- `scripts/_mtest/` + `scripts/mtest` DELETED (~2,200 LOC). Lives in `mgf-test-supervisor` as `mgf.test_supervisor.*`.
- `tests/unit/testing/test_supervisor_plugin_hygiene.py` DELETED — the plugin-hygiene contract now lives in the supervisor's own test suite.
- `mgf-common test` CLI subcommand repointed via thin shim to `mgf.test_supervisor.__main__.main`.
- `tests/unit/testing/test_supervisor_alignment.py` updated to import from `mgf.test_supervisor` (was `scripts._mtest`).
- `tests/smoke/test_mtest_self_check.py` invokes `mgf-test-supervisor --self-check` (was `./scripts/mtest --self-check`).
- `pyproject.toml` `[project.optional-dependencies].dev` gained `mgf-test-supervisor @ git+ssh://...` + `[tool.hatch.metadata] allow-direct-references = true`. Once PyPI publish lands, the git+ssh ref + hatch opt-in get dropped; for this release the Codeberg source is the dependency form.
- `docs/recipes/test-supervisor-adoption.md` rewritten as a 70-line redirect to mgf-test-supervisor's README.

### Pre-v1.0 surface lockdown (PAPER-67..69 + deletions)

Mechanical breakage allowed pre-v1.0 (per `feedback_pre_v1_break_freely`); v1.0 is the cheap-window threshold per AP-04.

**Deleted (7 deprecation shims):**

- `setup_logging()` / `setup_otel()` / `install_excepthook()` / `install_threading_excepthook()` (4 observability shims; were marked `stable (v0.32.3)` but emit `MgfDeprecationWarning`). Consumers use `bootstrap()` directly; the orchestrator was always the canonical entry. Removed from `__all__`.
- 12 N818 pre-suffix exception aliases (`exceptions/__init__.py:94-128`, marked "removed in v2.0"). v2.0 cost vanishes early.
- `vault` legacy dict-shape config support — replaced by `config_model`-registered factories (one-pass typed coercion at the `VaultSettings` layer).
- `build_parser` backward-compat alias (`cli/_mgfcli.py`) — use `build_cli`.

**PUBLIC_API.md reshape:**

- 5 sibling-extracted subpackages (`mgf.common.fastapi` / `.django` / `.db` / `.http` / `.alembic`) — their docs sections (≈23 names previously listed as if shipped here) replaced with **stub redirects** to the Codeberg siblings. Names actually moved at v0.28–v0.31; the docs were stale.
- Grand-totals math fixed; `PUBLIC_API.json` regenerated; pin test passes.
- 4 observability shim rows removed from observability section; `LogFormat` + `default_log_path` docstrings repointed to `bootstrap()` (replacing the deleted `setup_logging` references).

### Federation tool discoverability (PAPER-67 / PAPER-68)

**PAPER-67** — agents briefing's `WF-XX` rule-range reference derived from `WORKFLOWS.md` at module-import time. `_max_rule_id(doc_stem, prefix)` helper reads the standards doc, regex-matches `id="<prefix>-NN"`, returns max NN. AGENTS_BRIEFING f-string-interpolates `_WF_MAX` so future WF rules auto-bump the briefing's text; the existing STARTHERE.md pin test catches drift as the maintenance signal. TS-22..TS-27 stays a curated subrange with a validation test asserting every rule in the range exists in TESTING.md.

**PAPER-68** — `--check-pypi` flag in `mgf-common catch-up` upgraded to a tristate (`--check-pypi` / `--no-check-pypi` / default `None`). Default behaviour auto-fires the PyPI lookup on editable+stale pin (the most-common case where the lookup pays for itself); explicit `--no-check-pypi` is the air-gapped opt-out. Removes the manual `pip index versions mgf-common` round-trip without compromising air-gapped CI use.

**PAPER-72** — new catch-up audit `_check_claude_md_pin_literals` scans the project-specific (non-auto-managed) prose of `docs/(claude/)?CLAUDE.md` for federation pin literals that contradict pyproject's actual pins. Read-only — surfaces drift with line numbers; consumer fixes by hand or replaces with cross-references.

**PAPER-73** — `_apply_supervisor_wiring` makes the TS-22..27 finding `--apply`-able. Three idempotent TOML inserts (dev deps including `pytest-timeout` per PAPER-77; hatch `allow-direct-references` opt-in when hatchling; pytest timeout config keys). After-apply prints "now run `uv sync` + `mgf-test-supervisor --self-check`" so adopters know the manual follow-up.

**PAPER-74** — `init-project` aligned with DOC-07 canonical: scaffold now creates `docs/claude/CLAUDE.md` (was legacy `docs/CLAUDE.md`). catch-up still accepts both per PAPER-54 (canonical wins; legacy duplicate noted in detail).

**PAPER-75** — federation-wide underscore-URL sweep. 6 stale URLs fixed in `docs/standards/GUI_CONSUMERS.md` (×2), `docs/standards/OPERABILITY.md`, `docs/cutover/v0.30.0.md` (×3). Historical refs in `NAMING.md §4` documenting the cutover itself preserved.

**PAPER-77 part 2** — `mgf-common catch-up`'s TS-22..27 finding now lists `pytest-timeout` as a required dev dep alongside `mgf-test-supervisor`, with a footnote about the silent-CRASHED footgun (the keys are pytest-timeout-specific; adding them without the plugin produces opaque CRASHED outcomes). Audit now has 3 signals: `mgf-test-supervisor` dep, `pytest-timeout` dep, `timeout_method` config.

**PAPER-76** ⏸ DEFERRED — GUI-specific import-linter contract audit. Submitter's own 💭 aspirational classification respected; unblocks when a second GUI consumer lands (vm-vmanager-app is N=1 today).

**PAPER-70 / PAPER-71** — federation-wide audit-sink design + tests/_broker.py freshness check (filed earlier in cycle; full retrospectives in FEEDBACK.md).

### Docs hygiene

- `docs/standards/TESTING.md` §19 (test-supervisor implementation guide) — rewritten to reflect the published library; references repointed from `scripts/_mtest/*.py` to `mgf.test_supervisor.*`.
- `docs/standards/NAMING.md` — NM-03 rationale de-grandfathered (cutover complete; no grandfathered slugs in the federation).
- `docs/standards/PERFORMANCE.md:200` — TBD sentence deleted per the corpus's own no-TBD rule.
- `docs/standards/README.md` — supervisor-adoption row repointed to install + verify flow.
- 8 closed trackers archived from `docs/inprogress/` to `docs/archive/2026/` per DOC-11 (`git mv`, history preserved): SUPERVISOR_ROLLOUT, PROPOSAL_test_supervisor, v1_0_stability_audit, V2_6/7/8_NOTIFICATION, pre_release_check, SECURITY_HARDENING.
- `docs/inprogress/README.md` index rewritten to reflect the 10-file active set.

### Tests

`pytest -q` count: 1771 (0.40.0) → 1797 (this release). +26 net new tests:
- 4 plugin-hygiene regression tests (PAPER A — surface stockdown commit).
- 3 dynamic-rule-range tests (PAPER-67).
- 5 tristate `--check-pypi` tests + companion-test for PAPER-20 (PAPER-68).
- 9 marker-add apply tests (PAPER-69).
- 9 CLAUDE.md pin-literal audit tests (PAPER-72).
- 7 `_apply_supervisor_wiring` tests (PAPER-73).
- 1 new federation-markers self-cross-check (PAPER-66 reinforcement).
- Plus updates to ~12 existing tests for the API surface changes.

### Migration (consumer-side checklist for federation projects)

Walk the mgf-common pin window: `mgf-common>=0.40,<0.41` → `mgf-common>=0.41.0,<0.42`. Then run `mgf-common catch-up --apply` against the project; that handles:

- TS-22..27 supervisor wiring (auto-adds `mgf-test-supervisor @ git+ssh://...`, `pytest-timeout`, hatch opt-in, pytest timeout config).
- TS-25 federation marker taxonomy.
- CLAUDE.md auto-block refresh.

Then `uv sync` and `mgf-test-supervisor --self-check` to verify.

If using bootstrap-helper one-shots:
- `setup_logging(...)` → `bootstrap(setup_logging=True, ...)`
- `setup_otel(...)` / `install_excepthook(...)` / `install_threading_excepthook(...)` → all wired transactionally via `bootstrap(...)`
- N818 alias names → use the `*Error` canonical (alias docstring listed each substitution)
- `Vault*(config={"k": "v"})` dict input → use the typed `*Config` model
- `build_parser(...)` → `build_cli(...)`

### Test supervisor extracted to published sibling `mgf-test-supervisor` v0.1.0 (2026-05-26)

Following the federation rule recorded in TESTING.md §19.6
("extract when ≥2 siblings adopt by copy-paste"), the test
supervisor that landed in 0.40.0 as in-tree `scripts/_mtest/`
has been extracted to a dedicated sibling library
`mgf-test-supervisor` (Codeberg: `magogi-admin/mgf-test-supervisor`,
v0.1.0). All 7 federation siblings adopted the published library
on 2026-05-26; mgf-common is now a consumer rather than the
host of the copy-paste source.

**Removed (in-tree supervisor source — moved to the sibling):**

- `scripts/_mtest/` — entire ~2,200 LOC stdlib supervisor
  package DELETED. Lives in `mgf-test-supervisor` as
  `mgf/test_supervisor/`.
- `scripts/mtest` — venv-aware bootstrap wrapper DELETED.
  Replaced by the published console scripts (`mgf-test-supervisor`
  + the `mtest` alias).
- `tests/unit/testing/test_supervisor_plugin_hygiene.py` — DELETED.
  The plugin-hygiene contract now lives in `mgf-test-supervisor`'s
  own test suite (it pins the published library, not a local copy).

**Repointed (the surface consumers see):**

- `mgf-common test` CLI subcommand now delegates to
  `mgf.test_supervisor` via a thin shim — same UX, no in-tree
  implementation. Discovery via `mgf-common --help` unchanged.
- `tests/unit/testing/test_supervisor_alignment.py` now imports
  from `mgf.test_supervisor` (was `scripts._mtest`). The
  alignment contract — `mgf.common.testing.MARKER_TIER` ↔ the
  supervisor's `MARKER_TIER` + the `TestRecord` schema — still
  pins drift; it just pins against the published library now.
- `tests/smoke/test_mtest_self_check.py` invokes the published
  `mgf-test-supervisor --self-check` console script (was
  `./scripts/mtest --self-check`).

**Added (dev dependency):**

- `pyproject.toml` `[project.optional-dependencies].dev` gains
  `mgf-test-supervisor @ git+ssh://git@codeberg.org/magogi-admin/mgf-test-supervisor.git`
  + `[tool.hatch.metadata] allow-direct-references = true`
  (Hatch requires the opt-in for direct-reference deps).

**Docs:**

- `docs/recipes/test-supervisor-adoption.md` rewritten as a
  short redirect. The 475-line copy-paste walkthrough is
  superseded by `mgf-test-supervisor`'s own README — the
  install + adoption guide now lives next to the code it
  installs. The recipe at this path is preserved as a
  historical pointer.
- `docs/testing/DIAGNOSING_HANGS.md` §7 + §8 invocation snippets
  updated from `./scripts/mtest …` to `mgf-test-supervisor …`
  (canonical) / `mtest …` (alias) / `python -m mgf.test_supervisor …`
  (importable form).

**Importable surface unchanged:** `mgf.common.testing` (the 13
public names that graduated as `stable` in 0.40.0) stays in
mgf-common — it was always the part designed to graduate
independently. The supervisor was the runtime, `mgf.common.testing`
is the schema; the split was planned in TESTING.md §19.6 before
either landed.

**Migration for federation projects:** see
`mgf-test-supervisor`'s README. Short version:

```bash
uv add --dev "mgf-test-supervisor @ git+ssh://git@codeberg.org/magogi-admin/mgf-test-supervisor.git"
rm -rf scripts/_mtest scripts/mtest    # if you adopted by copy-paste
mgf-test-supervisor --self-check       # verify exit 0 + PASS
```

The pre-extraction copy-paste recipe (Federation rollout
May 2026, all 7 siblings adopted) is preserved at
`docs/inprogress/SUPERVISOR_ROLLOUT.md` for historical context.

---

## [0.40.0] — 2026-05-26

**Headline: Federation test supervisor + naming + slug cutover + five new L1 standards.** A heavy cohort: the runtime test infrastructure becomes federation-binding (TS-22..TS-27), the naming convention is codified (NM-01..NM-04 + the Codeberg slug + local-dir cutovers), and five additional L1 topic standards land (NAMING / AGENTS / TIME / PERFORMANCE / OBSERVABILITY + a GUI-consumers standard). Plus three same-cycle FEEDBACK papers ship (PAPER-59 / PAPER-60 / PAPER-61).

### Federation test supervisor — TS-22..TS-27 + `mgf.common.testing` + `mgf-common test` (2026-05-26)

mgf-common becomes the federation's **sibling-#1 adopter** of
the test supervisor originated in mgf-hrb. Three phases, one
release.

**Standards (Phase 1):**

- Six new federation-binding rules in `docs/standards/TESTING.md`:
  - **TS-22** (L1, MUST) — tiered runs: `--tier N`, `--tier N-M`,
    `--budget T`; greedy knapsack; tier-0 always included.
  - **TS-23** (L1, MUST) — supervisor runs pytest as a subprocess,
    pure-stdlib (avoids bootstrap circularity).
  - **TS-24** (L1, MUST) — per-test JSONL outcome at
    `.gates/last-run/run.jsonl`; schema mirrors `DoctorFinding`;
    schema versioning mandatory.
  - **TS-25** (L2, MUST) — auto-quarantine flakes (3-of-5-fails in,
    3-consecutive-passes out under `--quarantine-attempt`).
  - **TS-26** (L1, MUST where applicable) — built-binary
    freshness check; stale = FAIL, missing = SKIP.
  - **TS-27** (L1, MUST) — per-failure detail bundle at
    `.gates/last-run/failures/<id>.txt` carrying everything a
    triager needs in one file.
- New §13.11 — Extended marker taxonomy (the 13-layer federation
  superset: smoke/unit/functional/integration/contract/property/
  regression/fuzz/concurrency/conformance/use_case/stress/perf/e2e).
  Marker → tier mapping is the canonical default.
- New §19 — Federation test-supervisor implementation guide;
  reference implementation pointer; copy-paste checklist for new
  consumer adoption.
- §16 mechanical-enforcement matrix gains TS-22..TS-27 rows.
- `docs/standards/README.md` gains a new "Setting up the federation
  test supervisor" row in the suggested-reading table.

**Importable surface (Phase 2):**

- New subpackage `mgf.common.testing` exports 13 public names
  (`stable (v0.40.0)`):
  - Schema: `TestRecord`, `Outcome`, `Severity`,
    `DEFAULT_SEVERITY`, `SCHEMA_VERSION` ("1.0.0").
  - Markers + tiers: `MARKER_TIER`, `DEFAULT_TIER`,
    `effective_tier()`, `TierBudget`, `TIERS`.
  - Parallel-safe paths: `default_sock_path()`.
  - Failure bundles: `FailureBundle`, `SECTION_ORDER`,
    `SECTION_HEADER_FMT`.
- 92 new unit tests in `tests/unit/testing/`.
- `PUBLIC_API.md` gains a new `## mgf.common.testing` section.

**Supervisor implementation (Phase 3):**

- New `scripts/_mtest/` (~2,200 LOC stdlib-only) — supervisor,
  records writer, quarantine state machine, tier model, budget
  knapsack, durations history, diagnostics capture, self-check.
  Copied from mgf-hrb with four adaptation knobs:
  `DAEMON_PROCESS_NAME = None` (no daemon),
  `env_fingerprint()` swapped `mgf.hrb` → `mgf.common` (drops
  ziglang), module docstring rewritten for federation-neutral
  language, `concurrency: 2` added to `MARKER_TIER`.
- New `scripts/mtest` — venv-aware Python re-exec wrapper.
- New `mgf-common test` CLI subcommand (~60 LOC shim).
  Discovery via the canonical `mgf-common --help`.
- New `tests/smoke/test_mtest_self_check.py` — pins supervisor
  self-check at exit 0 + "PASS" output.
- 11 new contract tests in
  `tests/unit/testing/test_supervisor_alignment.py` pin
  `scripts/_mtest/` schema ↔ `mgf.common.testing` schema
  alignment (drift = test failure).
- New `docs/testing/DIAGNOSING_HANGS.md` — 278-line on-call
  playbook (copied from mgf-hrb, broker-specific §3 dropped).
- `pyproject.toml` gains `pytest-timeout>=2.3,<3` (dev extra),
  `timeout=30` / `timeout_method="thread"` /
  `faulthandler_timeout=60` in pytest config, and the full
  13-marker registration.
- `.gitignore` gains `.gates/` + `.claude/scheduled_tasks.lock`
  + `.claude/worktrees/`.

**Federation discipline:**

- Per the maintainer's stated rule ("extract to a published
  sibling library only when ≥2 siblings adopt by copy-paste"),
  mgf-common is sibling-#1. The supervisor stays copy-paste for
  now; `docs/standards/TESTING.md` §19.6 documents the extraction
  trigger (next sibling adoption fires it). The library-shaped
  types (`mgf.common.testing.*`) graduate immediately so
  downstream tooling (Magogi dashboard, Slack relay) doesn't
  need to wait.

**Tests:** 1,771 unit + 103 testing-module + 1 self-check smoke
all pass. `mgf-common test --self-check` exits 0 with all four
canned scenarios (pass/fail/crash/hang) correctly classified.
`mgf-common test --tier 0` selects 8 smoke tests; passes.

**Backward compatibility:** the legacy `slow` marker
(TS-18) stays registered for one MINOR cycle as a deprecation
alias (mapped to tier-2). New projects SHOULD use the specific
marker (`use_case` / `conformance` / `stress`) that says WHY a
test is slow rather than just THAT it is. Removed in v2.0.

The mgf-hrb originating doc
(`MGF_COMMON_ADOPTION_MESSAGE.md`) is archived as the
historical proposal that motivated this work.

### Codeberg-slug cutover — legacy underscore slugs renamed (2026-05-24)

Two repos predated the NM-03 hyphen-slug convention with
underscore slugs:

- `magogi-admin/mgf_common` → `magogi-admin/mgf-common`
- `magogi-admin/mgf_apiprobe` → `magogi-admin/mgf-apiprobe`

Both renamed in a single session. Federation-wide URL sweep
landed simultaneously across six repos (cornerstone + 5
siblings) — ~480 URL references updated. **Forgejo does NOT
auto-redirect renamed slugs**, so the references had to land
together with the rename rather than as follow-up cleanup
(empirically verified during the cutover; recorded as
forward-looking guidance in `NAMING.md §4`).

Consumer-side URL sweeps (vm-vmanager-app / PlasmaMapper / iosRecovery
/ inthewords / hrb-app / vm-vmanager-app) are deferred to the
next per-consumer catch-up cycle rather than bundled here —
each lands as a coordinated commit in the consumer's own
repo with its maintainer's approval.

`docs/standards/NAMING.md §4` rewritten — the "Legacy
exceptions" subsection is gone (no longer applicable; all
federation slugs now conform to NM-03). Replaced with a
"Slug cutover history" subsection that records the rename
plus the critical learning about Forgejo redirect behaviour.

No code change; pure infrastructure + documentation. The
PyPI distribution names (NM-01) were already hyphen-conformant;
this cutover closes the Codeberg-slug consistency gap that
NAMING.md §4 had previously grandfathered.

### Standards corpus expansion — five new L1 topic docs (2026-05-24)

A documentation-only cohort that codifies conventions previously
held in tribal memory or scattered across other topic docs.
Headline value: the federation now has explicit, citable rules
for naming, AI-agent collaboration, time handling, performance
budgets, and the observability umbrella beyond logging.

- **`docs/standards/NAMING.md`** (**NM-01..NM-04**, L1) —
  federation identity convention. Hyphens for PyPI distribution
  names + Codeberg slugs + local dev dirs; dotted namespace
  (`mgf.<sibling>`) for Python imports. Legacy `mgf_common`
  Codeberg slug grandfathered (rename cost > consistency gain).
  Folds the informal `Codeberg-naming` memory rule from the
  v0.28 cutover into the canonical corpus.

- **`docs/standards/AGENTS.md`** (**AG-01..AG-10**, L1) — the
  federation's actual differentiator. AI-agent collaboration
  discipline: every repo ships `AGENTS.md` as a thin pointer
  to `mgf-common agents` (the canonical briefing); commits end
  with `Signed-off-by` + `Co-Authored-By` trailers; agents file
  reusable friction as FEEDBACK papers rather than per-project
  workarounds; memory writes are federation-scoped, not
  session-scoped; risky operations require operator confirmation;
  standards drift caught by an agent gets filed as a PAPER, never
  silently fixed in either direction.

- **`docs/standards/TIME.md`** (**TM-01..TM-08**, L1) — time
  discipline. UTC-at-boundary (`datetime.now(timezone.utc)`,
  never the naive variants); ISO-8601 wire format; monotonic
  clocks for durations (wall-clock subtraction breaks on NTP
  nudge); inject the clock for tests; IANA timezone names; no
  `time.sleep` in async. Small but L1-binding — time bugs are
  notoriously expensive once they ship.

- **`docs/standards/PERFORMANCE.md`** (**PF-01..PF-08**, L1) —
  perf discipline. %-regression budgets vs a recorded baseline,
  NEVER absolute wall-clock; baselines live in
  `tests/perf/baselines.json` with hardware tier recorded;
  perf tests marked `@pytest.mark.perf` and excluded from the
  default four-gate baseline; failure messages cite numeric
  comparison + recording context. Closes the class of issues
  that PAPER-50 surfaced — LG-12's absolute 100 µs/record
  budget firing on slower-than-modern-laptop hardware regardless
  of code changes.

- **`docs/standards/OBSERVABILITY.md`** (**OB-01..OB-08**;
  OB-01..OB-04 L1, OB-05..OB-08 L2) — three-pillars umbrella
  beyond LOGGING. Metric naming
  (`mgf_<domain>_<unit>_<aggregation>`); canonical label set
  (`app` / `version` / `env`) attached to every metric; OTel-
  shaped traces with operation-named spans (verb-object); SLI
  / SLO / alert / runbook discipline for service-shape
  consumers (L2). The L1/L2 split: OB-01..OB-04 cost nothing
  from day one but are expensive to undo (dashboards, alerts,
  downstream consumers migrate); OB-05..OB-08 need an
  operational context (a service, an on-call rotation).

`docs/standards/README.md` updated — file-tree diagram and
suggested-reading table reflect the five new docs. The
`mgf-common standards` CLI auto-discovers the new topics
(it reads `docs/standards/*.md` rather than a fixed list).

No source-code changes; the rules are L1-binding regardless of
mechanical-enforcement availability. Each doc carries a §"Mechanical
enforcement" matrix tracking what's auto-enforced vs review-only
plus the natural next automation target.

### Federation test sweep (release verification)

1771 tests pass. Supervisor self-check exits 0; tier-0 smoke
(8 tests) passes through the supervisor end-to-end. Working tree
clean; remote synced. mgf-common is sibling-#1 adopter of the
test supervisor — extraction to a published `mgf-test-supervisor`
sibling fires when sibling-#2 adopts by copy-paste.

### Pin recommendation

```toml
mgf-common = ">=0.40.0,<0.41"
```

---

## [0.39.0] — 2026-05-24

**Headline: PEP-8 N818 exception-name rename + alias-deprecation cycle.**
Twelve public exception classes gained the `Error` suffix; old names
remain importable as `MgfDeprecationWarning`-emitting aliases (removed
in v2.0). Also folds in Q-11 stability promotions, the v1.0 doc /
catch-up feedback bundle, and two same-day consumer-reported papers
(PAPER-59 / PAPER-60).

### Added — consumer-reported papers (2026-05-24)

- **PAPER-60** (🟡 High) — `mgf.common.observability.excepthook` and
  `threading_excepthook` now route their `log.error` through a new
  `_safe_emit` helper. The helper short-circuits via
  `sys.is_finalizing()`, wraps the call in
  `try / except (ValueError, OSError)`, and falls back to
  `traceback.print_exception(..., file=sys.__stderr__)`. Closes the
  `ValueError: I/O operation on closed file.` cascade reported by
  vmanager under Python 3.13's stricter unraisable-exception flow.
  Pure additive — happy-path behaviour is byte-identical. Every
  consumer of `mgf.common.bootstrap()` gains the protection
  transparently on upgrade. Four new pins under
  `TestSafeEmitPaper60` exercise all four failure modes.

- **PAPER-59** (🟢 Nice-to-have) — `mgf-common init-project` gained
  the `gui` kind for desktop-GUI consumers (Qt/PySide, Textual,
  pywebview, …). The `_QUICK_START_BLOCKS["gui"]` template carries
  a sync `app.run()` shape inside `mgf.common.bootstrap()` —
  framework-agnostic, no `serve()` async-loop misfit. Deps map
  pins `mgf-common` only; the consumer picks the GUI framework.
  Filed by `hrb-app`, the federation's first GUI consumer; closes
  the "kind = service stand-in" gotcha that would have compounded
  across every future GUI consumer. Options 2 (`docs/recipes/gui.md`)
  and 3 (catch-up GUI-framework detection) deferred until a second
  GUI consumer materialises.

### API stability — experimental → stable promotions (Q-11, 2026-05-20)

The v1.0 stability sign-off (`docs/inprogress/v1_0_stability_audit.md`
Q-11). The audit was regenerated against the current 0.38.0 surface
(the original §3 was a stale 0.37.0 snapshot) and walked module by
module with the maintainer. **50 public names promoted
`experimental` → `stable (v1.0.0)`** across six modules:

- `mgf.common.exceptions` — `HmacVerificationError` (1).
- `mgf.common.config` — loader/path helpers + pydantic re-exports
  (13). The module is now fully stable.
- `mgf.common.observability` — OTel span helpers, crash-sink /
  redaction / log-handler / span-processor registries,
  `AsyncJsonHandler` (23). The `register_log_handler` /
  `register_span_processor` rows were reworded — a `stable` row
  must not reference a not-yet-built `MgfSettings` field;
  settings-driven auto-activation remains planned future work.
- `mgf.common.settings` — `LogLevel`, `LogFormat`, `PresetName` (3).
  Fully stable.
- `mgf.common.vault` — backend-registry trio +
  `BuiltinVaultBackendConfig` (4). Fully stable.
- `mgf.common.pytest_plugin` — the five fixtures + `decode_json_logs`
  (6). The consumer test-harness surface is fully stable.

Modules outside this sign-off (cli federation blocks, db, django,
fastapi, registry, alembic) stay `experimental` for one more
burn-in cycle. The `mgf.common.http` compat shim is slated for
`deprecated` at the v1.0 cut (separate action). Tier changes are
`PUBLIC_API.md`-only — no source or runtime change.

### Feedback closures (v1.0 doc + catch-up bundle, 2026-05-19)

Ten OPEN feedback papers closed; two REJECTED with rationale; one
DEFERRED-affirmed. The full retrospectives live inline in
[`FEEDBACK.md`](FEEDBACK.md); summary below.

**SHIPPED in the v1.0 doc bundle:**

- **PAPER-31** — audit-outcome taxonomy: new
  `docs/standards/principles/audit-outcomes.md` (six-flag
  vocabulary `✅ / ❌ / ⚪ / 🚫 / N/A / ⏳`, decision tree, three
  worked PlasmaMapper examples).
- **PAPER-32** — web-shape inline cross-refs: `**Web-shape note.**`
  paragraphs added at CF-09 (§6 `CONFIGURATION.md`), LG-06 (§8
  `LOGGING.md`), LG-10 (§10 `LOGGING.md`); consumer-application
  carve-out paragraph in `API_DESIGN.md` front matter covering
  AP-10 / AP-11 / AP-12 / AP-15.
- **PAPER-33** — `BaseAppSettings` auto-provision callouts: new
  `CONFIGURATION.md §0b` with one table for CF-02 / CF-03 /
  CF-06 / CF-08 + file:line citations to the reference
  implementation.
- **PAPER-34** — new recipe `docs/recipes/scan-deps.md`
  (pip-audit + pnpm audit CI gate, based on PlasmaMapper SH-25).
- **PAPER-35** — new federation-aligned cutover doc shape:
  `docs/cutover/federation-v0.34.0.md` (retroactive v0.34
  backfill + canonical template inside §3).
- **PAPER-48** (partial) — 176 inline `<a id="XX-NN"></a>` rule
  anchors across 13 topic docs in `docs/standards/` so PR
  comments can cite rules by URL. Full mkdocs/sphinx render
  parked for v1.1.

**SHIPPED in the v1.0 catch-up bundle:**

- **PAPER-51** (partial — wording fix only) — corrected
  "deprecated since v0.39.0" → "deprecated since v0.38.0
  (alias-removal in v2.0)" at
  `src/mgf/common/exceptions/__init__.py:121` + 4 sibling
  docstring corrections + test update. Full audit-row Approach A
  deferred to v1.x.
- **PAPER-52** — `_init_project.py` mgf-common pin now templated
  from `mgf.common.__version__` via the new shared helper
  `mgf.common.cli._version_pin.current_pin_window`. Sibling pins
  remain literal (separate problem; v1.x follow-up).
- **PAPER-54** — `mgf-common catch-up` accepts both
  `docs/claude/CLAUDE.md` (DOC-07 canonical) and `docs/CLAUDE.md`
  (legacy). Canonical wins when both exist; duplicate flagged in
  the finding's detail.
- **PAPER-57** — new subcommand `mgf-common federation-sync`
  prints the federation-sync message with a live pin window
  computed from `__version__` at invocation time. Pasting the
  output replaces the hand-crafted message that went stale at
  every MINOR.

**SHIPPED as an additive API (v1.0 API gate Q-14):**

- **PAPER-45** — `JsonFormatter(field_map={"ts": "timestamp", "msg":
  "message"})` for consumers whose log-aggregator parser expects a
  different canonical-key naming. Default `None`/`{}` is a strict
  no-op (byte-identical to pre-PAPER-45 behaviour). Renames apply
  to canonical mgf fields only — extras keys (caller-controlled
  names) are NOT subject to the map. Approach B / C parked as
  v2.0 candidates.

**REJECTED with rationale (v1.0 API gates Q-12 / Q-13):**

- **PAPER-46** — proposed adding a generic `context` field to
  `AppError`. Rejected: `AppError` has no `context` field today
  (`src/mgf/common/exceptions/_base.py:27` is
  `class AppError(Exception): "..."`); the proposal contradicts
  EH-08's typed-attribute pattern; inthewords' own lint already
  catches the foot-gun. Re-open if a second consumer files the
  same friction AND can't express the case via EH-08 typed
  subclasses.
- **PAPER-49** — proposed splitting pydantic into a
  `mgf-common[settings]` extra. Rejected: pydantic is
  load-bearing across 7 modules / 4 subsystems (vault, versioned,
  config, cli), not isolated to `BaseAppSettings`; the ~3 MB /
  ~100 ms savings don't justify a 4-extra install matrix. Re-open
  on PEP 711 / pydantic 3.x landscape shift OR a second consumer's
  concrete deploy-time pain.

**AFFIRMED DEFERRED at v1.0:**

- **PAPER-10** — entry-point-driven auto-discovery for
  observability / vault plug-ins. None of the three reopen
  criteria have fired since 2026-05-02 (no plugin-framework
  consumer pain; no second consumer's extension-wiring pain
  evidence; no in-tree consumer ships a pip-installable
  extension). The explicit-call API is additive-compatible with
  any future entry-point walk, so reopening later costs no
  migration on existing consumers. Reopen criteria restated:

  1. A plugin-framework consumer files concrete pain from the
     explicit-call shape.
  2. A second independent consumer reports extension-wiring
     evidence (pain OR non-pain — both count).
  3. An in-tree consumer ships a pip-installable extension that
     would benefit from auto-discovery.

  Whichever channel surfaces the strategic-review P1 signal
  surfaces PAPER-10's signal too.

**Governance follow-ups (v1.x):**

- Duplicate-ID guard in `mgf-common feedback` CLI — prevent the
  PAPER-45..49 collision pattern (resolved 2026-05-19 via
  renumber; tracked as v1.x process fix to make recurrence
  impossible).
- Sibling-pin templating in `_init_project.py` (separate from
  PAPER-52's mgf-common-pin fix).
- Full PAPER-51 Approach A — `mgf-common catch-up` audits source
  for deprecated mgf-name use BEFORE the pin bump. The
  registry is already 90% in place
  (`_N818_DEPRECATED_ALIASES` at
  `src/mgf/common/exceptions/__init__.py:100`).
- pydantic-coupling acknowledgment in
  `docs/standards/COMMON_COMPONENTS.md` (PAPER-49 follow-up doc).
- mkdocs/sphinx render of `docs/standards/` (PAPER-48 v1.1).

### Changed (deprecation cycle — closes Q-2 from `docs/inprogress/v1_0_quality_gates.md`)

- **PEP-8 N818 exception-name rename.** Twelve public exception
  classes gained the `Error` suffix: `HttpBadRequest`,
  `HttpUnauthorized`, `HttpForbidden`, `HttpNotFound`, `HttpConflict`,
  `HttpGone`, `HttpUnprocessable`, `HttpServiceUnavailable`,
  `ProcessTimeout`, `ProcessNonZeroExit`, `ProcessSignaled`,
  `ServiceNotReady` → `*Error`. Consumers using the old names
  continue to import them as deprecated aliases that emit
  `MgfDeprecationWarning` on first access (PEP 562 `__getattr__`
  in `mgf.common.exceptions`). **The aliases will be removed in
  v2.0**; migrate by adding `Error` to each affected import,
  except-clause, isinstance-check, and class subclass.

  All 12 internal callers + 8 test files migrated to the new
  names in this same commit. 26 new unit tests in
  `tests/unit/test_n818_deprecation_aliases.py` pin the
  resolve-to-new-class + warning-emit + AttributeError-on-unknown
  behaviour for every alias.

  Ruff `N818` lint now clean (was 12 findings at v0.38.0).

### Federation test sweep (release verification)

1661 passed, 0 failed. No regressions; no public name removed
(twelve N818-renamed names remain importable as deprecation-warning
aliases for the v0.x → v2.0 migration window).

### Pin recommendation

```toml
mgf-common = ">=0.39.0,<0.40"
```

---

## [0.38.0] — 2026-05-18

**Federation contract — the canonical adoption page.** No source-code
changes to the cornerstone library surface; this MINOR is documentation
infrastructure + one new tooling check. Drop-in upgrade from 0.37.0.

### Added

- **`FEDERATION.md`** at repo root (DOC-17, standards v2.10) — the
  single canonical contract every consumer + sibling reads to align
  with the federation. Ten sections covering: what the foundation is,
  what depending on mgf-common commits you to, required docs layout,
  L1/L2/L3 conformance levels, federation-first principle (use mgf-*
  before reaching for alternatives), catch-up cadence, feedback
  discipline, onboarding flows, and a one-screen cheat sheet.
  Authoritative copy lives only here; siblings + consumers link to it.

- **`USERS.md`** at repo root (DOC-16, standards v2.9) — answers
  "who depends on mgf-common?" Lists four consumer projects with
  import-site counts + every federation sibling.

- **DOC-16 standard** (v2.9) — formalises the per-sibling `USERS.md`
  user-roll requirement; inverse of DOC-14 (consumer-side
  `MGF_ADOPTION.md`).

- **DOC-17 standard** (v2.10) — formalises `FEDERATION.md` as the
  canonical contract; siblings + consumers MUST NOT maintain their
  own copy; drift between the file and the rule set is a defect.

- **`mgf-common catch-up` contract enforcement** — new
  `_check_federation_contract` audit step verifies every required
  artifact: universal-5, DOC-15 conformance tracker (everyone),
  DOC-14 adoption tracker (consumers only — ADP-04 carve-out),
  DOC-16 USERS.md (siblings only — detected by `mgf-` / `mgf_`
  package name prefix). Missing required artifacts cause non-zero
  exit so the audit can gate CI. Accepts `MGF_COMMON_ADOPTION.md`
  legacy alias and `LICENSE.md` / `LICENSE.txt` variants. **9 new
  unit tests.**

### Changed

- **`docs/standards/DOCUMENTATION.md`** bumped from v2.8 to v2.10 in
  two steps (DOC-16 + DOC-17). No existing rules modified.
- **README.md** + **STARTHERE.md** carry callouts pointing at
  FEDERATION.md as the canonical contract.

### Federation rollout (companion commits in sibling repos)

Every `mgf-*` sibling ships a repo-local `USERS.md` grounded in a
fresh grep import-site survey. Six code-siblings additionally gained
`STARTHERE.md` + `CONTRIBUTING.md` (DOC-01 sibling MUSTs). The new
`mgf-usb` (v0.0.0 design-lock scaffold) was registered in mgf-common's
USERS.md §2 + FEDERATION.md §5 federation-first table.

### Wave-4 maintainer review docs

Three docs land in `docs/inprogress/` to set up the v1.0 maintainer
review (NO v1.0 cut performed): `v1_0_stability_audit.md`,
`v1_0_quality_gates.md`, `v1_0_handoff.md`. Three blockers identified
for v1.0: Q-2 (N818 rename decision), Q-4 (PyPI backlog drain), Q-11
(promotion sign-off).

### Federation test sweep (release verification)

1596 passed, 0 failed (1587 from 0.37.0 baseline + 9 new tests).
No regressions; no API breakage; no public name removed.

### Pin recommendation

```toml
mgf-common = ">=0.38.0,<0.39"
```

---

## [0.37.0] — 2026-05-18

PyPI: <https://pypi.org/project/mgf-common/0.37.0/> · Tag: `v0.37.0`

> **Wave 3 — second-tier federation primitives.** Four new
> modules ship as a single MINOR. All `experimental` tier per
> AP-11. Drop-in upgrade from 0.36.0 — purely additive.

### Added — `mgf.common.rate_limit` (C-2)

Wave 3 formalisation of the TokenBucket shape that
mgf-fastapi 0.6.0 + mgf-django 0.4.0 both ship locally. Both
siblings rebase on this module in their next minor.

Two public names: **`Bucket`** (Protocol), **`TokenBucket`**
(stdlib-only in-process implementation). Future
Redis-backed impls satisfy the Protocol for multi-replica
deployments. 10 unit tests pin algorithmic correctness +
per-key isolation + reset ergonomics + Protocol conformance.

### Added — `mgf.common.idempotency` (C-3)

The retry-safe POST primitive. Closes the "double-charged
retry" footgun class. Pairs with future middleware in
mgf-fastapi v0.7+ + mgf-django v0.5+.

Four public names: **`IdempotencyStore`** (Protocol),
**`IdempotencyRecord`** (frozen dataclass — get-return shape),
**`InMemoryIdempotencyStore`** (default backend, 24h TTL per
Stripe convention), **`IdempotencyKeyConflictError`** (raised on
same-key-different-body — Stripe convention). 11 unit tests
cover hit / miss / overwrite / TTL eviction / conflict
detection / lifecycle.

### Added — `mgf.common.health` (C-4)

Liveness + readiness + aggregation. Standardises the shape
every web consumer hand-rolls; framework siblings wire the
routes (mgf-fastapi `HealthRouter` v0.7+; mgf-django
`health_view` v0.5+).

Seven public names: **`HealthStatus`** literal,
**`ComponentCheck`** Protocol, **`ComponentResult`** frozen
dataclass, **`LivenessCheck`** + **`ReadinessCheck`** subclass
markers (for `/healthz` vs `/readyz` routing), **`HealthReport`**
+ **`HealthRegistry`**. Worst-case aggregation
(`unhealthy` ≻ `degraded` ≻ `ok`). Auto-populates latency.
Catches per-check exceptions + reports as `unhealthy`. 17 unit
tests pin Protocol conformance, aggregation rules,
liveness/readiness routing, exception-as-unhealthy, JSON shape.

### Added — `mgf.common.cache` (C-5)

Five-method Cache Protocol + stdlib-only LRU+TTL default +
NullCache. Lets consumers start on the in-process default and
swap to Redis later without touching call sites.

Four public names: **`Cache`** (Protocol),
**`CacheMiss`** sentinel (distinguishable from cached
`None`; falsy under `bool()`), **`LRUCache`** (thread-safe
`OrderedDict`-backed; per-entry or default TTL; lazy eviction
on access), **`NullCache`** (no-op — disable caching toggle).
19 unit tests pin basic CRUD, LRU eviction order, TTL
expiry, per-set-overrides-default, no-TTL-no-expiry,
get-bumps-to-MRU, Protocol conformance, CacheMiss sentinel
semantics.

### Notes for consumers

- **Drop-in upgrade from 0.36.0** — purely additive
  (`experimental` tier).
- **Sibling rebase**: mgf-fastapi + mgf-django will adopt
  `mgf.common.rate_limit.TokenBucket` in their next MINOR
  (replacing the sibling-local duplicate). Middleware shells
  stay framework-specific.
- **Multi-replica deployments**: Redis-backed implementations
  of `Bucket` + `IdempotencyStore` + `Cache` ship under each
  consumer's own tree (or as a future `mgf-common-redis`
  sibling). The Protocol surface is the contract.

### Verification

- pytest tests/: 1587 passed (1530 from 0.36.0 + 57 new
  across the four modules).
- mypy --strict: clean on all new modules.
- ruff: clean.

---

## [0.36.0] — 2026-05-18

PyPI: <https://pypi.org/project/mgf-common/0.36.0/> · Tag: `v0.36.0`

> **Wave 1 completion** — two more experimental-tier modules
> ship: plugin auto-discovery (closes PAPER-10, deferred since
> April 2026) and the doctor-bundle primitive (promotes the
> existing `mgf-common doctor` CLI command's logic into a
> reusable scaffold for vm-vmanager-app + iosRecovery).

### Added — `mgf.common.plugins` (C-NEW-2 / PAPER-10 closure)

The federation finally has the 2nd-consumer evidence needed to
ship plugin auto-discovery: PlasmaMapper's `TelematicsProvider`
plugin shape (Teltonika first; Queclink + Ruptela queued for
Phase 8). Federation rule: 2-consumer signal = primitive.

Three public names (all `experimental` tier):

- **`PluginRecord(name, obj, distribution, version)`** — frozen
  dataclass for a successfully-loaded plugin entry.
- **`PluginRejection(name, distribution, reason, detail)`** —
  frozen dataclass for an entry that was found but NOT loaded
  (load failure OR non-conformant). Only surfaces with
  `return_rejections=True`.
- **`discover(group, *, required=None, return_rejections=False)`** —
  wraps `importlib.metadata.entry_points` with Protocol filtering
  + audit emission. Load failures + non-conformant entries are
  SKIPPED with WARN logs (`event="mgf.plugin_rejected"`); one
  bad plugin can't crash the host startup (closed-box
  discipline). Audit-emission per call
  (`event="mgf.plugin_discovery"`, `audit=True`).

9 new unit tests pin the contract (happy paths, load failures,
non-conformance, audit emission).

### Added — `mgf.common.doctor` (C-NEW-3 / doctor-bundle primitive)

Promotes the existing `mgf-common doctor` CLI command's report-
rendering logic into a reusable primitive. Two-consumer pattern
(vm-vmanager-app `doctor bundle` + iosRecovery planned `figrecovery
doctor bundle`). Text + JSON renderers; atomic 0o600 file write
per SC-13.

Three public names (all `experimental` tier):

- **`ReportStatus`** — `Literal["ok", "warn", "fail"]`. The
  section-status taxonomy. JSON-stable.
- **`ReportSection(name, status, details={})`** — frozen
  dataclass — one section. `details` is `Mapping[str, Any]`;
  sensitive values MUST be pre-redacted (the bundle does NOT
  scrub automatically — that would mask consumer bugs).
- **`DoctorBundle(app_name, app_version, when=now)`** —
  composable bundle. `add_section(section)` appends or replaces
  by name. `overall_status` aggregates worst. `render_text()`
  for human report; `render_json()` for SIEM ingest;
  `write_to(path, *, format="text"|"json")` atomic-write with
  0o600 mode.

18 new unit tests pin the contract (composition + idempotent
add + status aggregation + text render + JSON render + 0o600
write + atomic overwrite + missing-parent rejection + unknown-
format rejection).

### Notes for consumers

- **Drop-in upgrade from 0.35.0** — both modules are purely
  additive (`experimental` tier per AP-11).
- **PlasmaMapper**: ready to adopt `mgf.common.plugins` for
  `TelematicsProvider` discovery (replaces ~30 LOC of
  hand-rolled entry-point loop).
- **vm-vmanager-app + iosRecovery**: can wire `DoctorBundle` for
  their doctor-bundle outputs.

### Verification

- pytest tests/: 1530 passed (1503 from 0.35.0 + 27 new).
- mypy --strict: clean on new modules.
- ruff: clean.

---

## [0.35.0] — 2026-05-18

PyPI: <https://pypi.org/project/mgf-common/0.35.0/> · Tag: `v0.35.0`

> **Wave 1 federation primitives + v2.8 standards bump + two
> closed inthewords-filed papers.** Two new modules ship at
> `experimental` tier (AP-11), plus the v2.8 standards-discipline
> rollout, plus PAPER-43 / PAPER-44 closures from inthewords'
> catch-up walk. Drop-in upgrade — no public API removals or
> renames.

### Added — `mgf.common.flags` (C-1 feature-flag primitive)

The federation-wide standardisation of the env-var-feature-gate
pattern every consumer was rolling its own version of (see
`docs/inprogress/pre_release_additions.md` §5, C-1 row —
universal-need; replaces ~80 LOC per consumer).

Six public names (all `experimental` tier):

- **`FeatureFlag`** — `@runtime_checkable` Protocol. One method:
  `is_enabled(name, *, default, context)`. Backends wire it.
- **`EnvVarFlags()`** — default backend. Reads
  `MGF_FLAG_<UPPER_NAME>` env vars per call (always-fresh).
  Truthy values: `1` / `true` / `yes` / `on` / `enabled`. Falsy:
  `0` / `false` / `no` / `off` / `disabled` / empty. Invalid
  values WARN + return the call-site default (deny-by-default).
- **`StaticFlags(initial=None)`** — in-memory backend for tests.
  `.set(name, value)` / `.unset(name)` for fixture ergonomics.
- **`configure_flags(impl)`** — install a backend process-wide.
  Emits structured INFO with `event="mgf.flag_backend_configured"`
  + `audit=True` per LG-04 + SC-17.
- **`get_flags()`** — return the installed backend (test
  fixture convenience).
- **`is_enabled(name, *, default, context)`** — module-level
  convenience. Delegates to the installed backend. Hot-path
  cheap; does NOT audit per-call.

34 new unit tests pin the contract.

### Added — `mgf.common.schedule` (C-NEW-1 scheduler protocol)

The federation-wide standardisation of cron-shape input that
three real consumers (vm-vmanager-app `croniter`, iosRecovery systemd
timer, PlasmaMapper `arq` cron) each handle differently. Three-
consumer signal — the strongest evidence the federation has
collected for any single primitive. **Stdlib-only by design**.

Five public names (all `experimental` tier):

- **`CronExpression`** + **`CronExpression.parse(expr)`** — full
  POSIX-cron 5-field parser + the canonical aliases (`@yearly` /
  `@monthly` / `@weekly` / `@daily` / `@midnight` / `@hourly`).
  Supports `*`, `N`, `N-M`, `*/N`, `N-M/N`, comma-lists. Frozen
  dataclass; safe to share across threads. Does NOT support
  `L` / `W` / `#` / day-name / month-name aliases (deferred to
  v1.1+ if a consumer needs them).
- **`CronExpression.matches(ts)`** + **`CronExpression.next_fire(*, after)`** —
  membership + iteration helpers. Both require tz-aware
  datetimes (per DP-12).
- **`Scheduler`** — `@runtime_checkable` Protocol with `schedule`
  + `tick` methods. Backends decide the execution model.
- **`ScheduledTask`** — frozen dataclass returned by `schedule`.
- **`InProcessScheduler()`** — tick-driven default. Idempotent
  registration (re-scheduling same `name` replaces). Catches
  exceptions in fired tasks (logs with
  `event="mgf.schedule.task_raised"`; never raises out of
  `tick`). Sub-minute tick-calls safe — each task fires at
  most once per minute. NOT suitable for multi-replica without
  an external lock.

49 new unit tests pin the parser + scheduler contract.

### Added — standards v2.8 (DOC-13 + DOC-14 + DOC-15 bundles)

Full details in
[`docs/inprogress/V2_8_NOTIFICATION.md`](docs/inprogress/V2_8_NOTIFICATION.md).

- **DOC-01 amended**: `FEEDBACK.md` becomes the universal-five
  root doc. Every project ships its own — no inheritance
  escape.
- **DOC-13 + FB-01..FB-04** — FEEDBACK discipline. Repo-scoped;
  local-per-repo PAPER-NN numbering; mandatory metadata fields.
- **DOC-14 + ADP-01..ADP-04** — adoption-tracker discipline.
  Every consumer MUST maintain `MGF_ADOPTION.md`. Federation
  siblings exempt per ADP-04.
- **DOC-15 + CFM-01..CFM-04** — conformance-tracker discipline.
  Every project that adopts standards (consumer OR sibling)
  MUST maintain `MGF_STANDARDS_CONFORMANCE.md`. Declared
  level (L1 / L2 / L3 per CFM-02). Per-rule status markers
  per CFM-03. Decline-with-rationale per CFM-04.

Three new fork-and-fill templates in `docs/recipes/`:
`feedback-md-template.md`, `mgf-adoption-md-template.md`,
`mgf-standards-conformance-md-template.md`.

### Added — `mgf-common catch-up` auto-block extended

The `_init_project.py` template's "Reporting feedback" section
renamed to "Federation-discipline reminders" and split into
THREE explicit BEGIN/END marker pairs (one per discipline):

- `<!-- BEGIN mgf-common: feedback-discipline -->`
- `<!-- BEGIN mgf-common: adoption-discipline -->`
- `<!-- BEGIN mgf-common: conformance-discipline -->`

Consumers receive these on their next `mgf-common catch-up --apply`
run. AI agents in consumer sessions see all three on every
session start. New test
`test_v28_three_discipline_blocks_present` pins all six
markers + the rule cross-references + the template paths.

### Changed — `mgf-common catch-up --apply` auto-cleans `.bak.<timestamp>` files on verified success

Closes [PAPER-44](FEEDBACK.md) filed by inthewords during its
mgf-common v0.31 → v0.32 catch-up walk.

`--apply` writes a `<file>.bak.<unix-timestamp>` next to each
rewritten file as a safety net. The previous behaviour was to
leave those backups in place and print a "check the .bak.*
backups if anything looks wrong" hint — but on a happy-path
run the safety net never served its purpose, and consumers
running `git add -A` after a catch-up bump risked accidentally
committing the backup files.

New behaviour: after the apply loop completes, the catch-up
runner re-executes the six audit checks. If the re-audit
reports no remaining mechanical findings, the `.bak.<timestamp>`
files written by THIS run are deleted (pre-existing backups
with the same shape are left untouched — only the diff is
swept). If the re-audit still has fixable findings, the
backups are retained with their paths printed for inspection.

Either way, a `.gitignore` tip prints recommending
`**/*.bak.[0-9]*` so future catch-up backups can't be
accidentally committed even on the retain path.

3 new unit tests in `tests/unit/cli/test_catch_up.py`:
`test_apply_refreshes_claude_md_and_auto_cleans_backup` (the
auto-clean happy path), `test_apply_does_not_clean_pre_existing_backups`
(diff-based cleanup must not touch pre-existing backups from
older catch-up runs or other tools). One existing test
(`test_apply_creates_backup_for_refreshed_claude_md`) renamed
+ assertion inverted to reflect the new behaviour; its
underlying invariant (the rewrite happens) is preserved.

---

### Fixed — `mgf-common migrate` v0.32 → v0.33 codemod registration

Closes [PAPER-43](FEEDBACK.md) filed by inthewords during its
v0.32 → v0.33 catch-up walk.

The v0.33.0 cutover guide
(`docs/cutover/archive/v0.33.0.md`) documents `mgf-common migrate`
as the codemod path for the two AP-12 deprecation removals
(`EnvironmentError` → `HostEnvironmentError`,
`make_settings_config` → `settings_config`). The rules existed in
`src/mgf/common/cli/_migrate.py` as `_RULES_0_1_TO_0_3` —
identifier-anchored renames that don't care which release cycle
removed the alias — but the version-pair dispatch table only
registered `("0.1", "0.3")`. A consumer running
`mgf-common migrate 0.32 0.33 .` got `✗ unsupported migration`
and fell back to a manual grep + edit pass.

Fix: register `("0.32", "0.33")` in `_MIGRATIONS` pointing at
the same rules tuple. The codemod is idempotent — a consumer
who ran the v0.1 → v0.3 codemod historically sees a no-op on
re-run for v0.32 → v0.33; a consumer catching up directly from
pre-v0.3 to v0.33+ gets the renames applied for the first time.

Five new tests in `tests/unit/test_phase7_migrate.py` under
`TestPaper43V032ToV033Migration` (rename on the new pair,
idempotency after v0.1 → v0.3, dry-run threading, per-rule
import-gate semantics, plus a strengthened existing test
asserting both pairs appear in the "Supported migrations:"
listing).

### Changed — FEEDBACK.md cleanup

Six misfiled inthewords-filed papers (PAPER-36, 37, 39, 40, 41,
42) replaced with redirect stubs; full content moved to
mgf-http + mgf-apiprobe FEEDBACK files with local-per-repo
renumbering. See v2.8 standards bump for the structural rule
(DOC-13) that prevents the next misfile.

### Notes for consumers

- **Existing consumers (vm-vmanager-app, PlasmaMapper, inthewords,
  iosRecovery)**: receive the v2.8 auto-block on next
  `mgf-common catch-up --apply` run. If you don't have
  `MGF_ADOPTION.md` + `MGF_STANDARDS_CONFORMANCE.md` yet,
  create from the new templates in `docs/recipes/`.
- **Sibling pin bump**: existing siblings still on
  `mgf-common>=0.34,<0.35` need to widen to `>=0.35,<0.36` in
  their next release. Transitive consumer impact is zero —
  v0.35.0 is purely additive (new module names; no
  behavioural changes to existing surface).

### Verification

- pytest tests/: 1505+ passed (1413 from 0.34.0 + 34 flags +
  49 schedule + 7 misc new tests + the catch-up + migrate
  PAPER-43/44 closures).
- mypy --strict: clean on new modules.
- ruff: clean.

---

## [0.34.0] — 2026-05-17

PyPI: <https://pypi.org/project/mgf-common/0.34.0/> · Tag: `v0.34.0`

> **Internal-only release.** No public API additions / removals /
> renames since 0.33.0. Consumers can upgrade without code changes;
> the existing `mgf-common>=0.33,<0.34` pin in sibling pyprojects
> should be widened to `>=0.34,<0.35` (the sibling-pin update ships
> in each sibling's next release).

### Added — regression-protection tests

P1 perf gates (close the "fast-on-paper, slow-in-prod" risk class):

- **PC-02** — sustained-throughput gate
  (`tests/unit/test_sustained_throughput.py`). 50 000 paced records
  through `AsyncJsonHandler` over ~5 s asserts zero drops, drain-
  completion < 1 s, RSS growth < 5 MB.
- **PC-03** — `Redactor` large-payload scaling (3 new tests in
  `test_perf_pins.py`'s `TestRedactorPerf`). Pins linear scaling in
  dict size + string length; catches O(n²) regressions.
- **PC-04** — `report_now()` end-to-end latency
  (`test_crash_reporter.py`). Median < 10 ms / p99 < 25 ms across
  100 calls with realistic cause chain.
- **PC-05** — bootstrapped-process memory baseline
  (`tests/unit/test_memory_footprint.py`). Subprocess RSS after
  `bootstrap` + 10 000 log records + 10 `report_now()` < 80 MB.

P2 perf + security gates:

- **PC-07** — `operation_span` no-op cost pin (~1-2 µs; budget 10 µs).
- **PC-09** — PBKDF2 iteration **floor** test. Asserts
  `_DEFAULT_KDF_ITERATIONS >= 600_000` (OWASP 2023 baseline / SC-05)
  AND the first vault `set()` with default iterations takes ≥ 50 ms.
  A fast KDF is the suspicious signal here.
- **PC-10** — `AsyncJsonHandler.emit` perf pin + **ratio** gate.
  Absolute < 18 µs; async/sync ratio < 0.5 (catches the
  regress-together case the absolute budget can mask).

### Changed — internal-only

- **Standards v2.6** — project-shape taxonomy added to
  `docs/standards/DOCUMENTATION.md` (DOC-01 / §2.0). `LICENSE` is
  now a MUST in the universal-four root docs. Federation siblings
  declare their shape in README (rolled out across all 7 siblings
  in their respective v0.x.y release cycles).
- **Standards v2.7** — new
  [`docs/standards/DEPLOYMENT.md`](docs/standards/DEPLOYMENT.md)
  (DEP-01..DEP-11). Applies to stateful web-service consumers
  (NOT to federation siblings, which ship to PyPI). Cloud-agnostic;
  Infomaniak is the foundation's reference platform.
- **Closed-by-docs:** RA-19 + RA-21 (lifecycle / span-processor
  documentation).
- **Internal cleanup:** removed vm-vmanager-app / PlasmaMapper project-name
  leakage across all 14 standards documents. Standards now read
  generically; per-consumer cross-references live in
  `docs/cutover/<version>.md` and `docs/reference-consumers/`.
- **STANDARDS_AUDIT trackers archived** under `docs/archive/2026/`
  per **DOC-11** (the audit closed clean; archive instead of leaving
  closed-out trackers in `docs/inprogress/`).

### Fixed

- *(Corrigendum 2026-05-19: the original release notes claimed
  "PAPER-31..34 shipped here as standards / docs refinements with no
  consumer-visible code change." That was anticipatory — the
  v0.34.0 release shipped only the perf gates + standards v2.6 /
  v2.7 docs listed above. The PlasmaMapper-filed papers
  PAPER-31 / 32 / 33 / 34 / 35 + the inthewords-filed PAPER-48
  all actually closed in the v1.0 doc bundle on 2026-05-19. The
  PyPI-published `mgf-common 0.34.0` wheel bundles the original
  wording; repo readers from 2026-05-19 forward see this
  corrigendum.)*

### Notes for consumers

- **Sibling pin bump:** the existing `mgf-common>=0.33,<0.34` pin
  must move to `>=0.34,<0.35`. Each sibling will ship that pin
  update shortly after this release.
- **No codemod needed** — no public-API renames or removals.
- **`mgf-common migrate`** is unchanged.

---

## [0.33.0] — 2026-05-11

PyPI: <https://pypi.org/project/mgf-common/0.33.0/> · Tag: `v0.33.0`

> **Breaking minor — AP-12 deprecation removals.** Three deprecated
> aliases reach end-of-cycle and are removed. Each had at least one
> MINOR release with `MgfDeprecationWarning` per rule AP-12; consumers
> who heeded the warnings need no code changes. The companion
> `mgf-common migrate` codemod still applies the safe renames for
> consumers catching up.

### Removed (breaking)

- **`mgf.common.configure(app_name, app_version)`** — superseded by
  `mgf.common.bootstrap(app_name=..., app_version=...)`, which wires
  identity + logging + OTel + crash hooks transactionally with LIFO
  rollback. `configure()` only set identity, leaving the consumer to
  call `setup_logging` / `setup_otel` / `install_excepthook` manually.
  Tests and scripts that just need identity isolation may use the
  internal `mgf.common._identity._set_identity(...)` helper, or
  prefer `mgf.common.bootstrap_for_test(...)`.
- **`mgf.common.config.make_settings_config(...)`** — renamed to
  `mgf.common.config.settings_config(...)`. Pure rename; same return
  type, same behaviour. Codemod rule `make_settings_config →
  settings_config` ships in `mgf-common migrate` for mechanical
  fixes.
- **`mgf.common.exceptions.EnvironmentError`** — the deprecated
  alias that shadowed Python's built-in `EnvironmentError` (= `OSError`)
  is removed. Use `mgf.common.exceptions.HostEnvironmentError` for
  host pre-flight failures. Codemod rule `EnvironmentError →
  HostEnvironmentError` (gated on `mgf.common.exceptions` import)
  ships in `mgf-common migrate`.

### Migration

- Most consumers: run `mgf-common migrate` from the project root.
  The codemod handles the two pure renames mechanically. `configure()`
  → `bootstrap()` is NOT codemodded — it benefits from human review
  per case (rule §1 of `docs/MIGRATION_0.1_TO_0.3.md`).
- See [`docs/cutover/v0.33.0.md`](docs/cutover/v0.33.0.md) for the
  worked migration recipe.

### Internals

- Examples under `examples/` updated to the `bootstrap()` shape so
  every layered demo reflects the post-removal canonical surface.
- Test suite reshaped: tests that need only identity isolation now
  call the private `_set_identity()` helper; tests that exercise
  the full wiring stack use `bootstrap_for_test()`.

---

## [0.32.3] — 2026-05-11

PyPI: <https://pypi.org/project/mgf-common/0.32.3/> · Tag: `v0.32.3`

> **Doc-only patch release — omnibus stable walk.** Fifth and largest
> experimental → stable walk in the v0.32+ chapter, covering the
> remaining submodule surfaces with multi-cycle / multi-consumer
> evidence (observability, process, progress, vault, versioned, cli,
> typing, providers) plus a re-evaluation of two v0.32.2-held items.
> 69 names promoted. No runtime change.

### Public-API stability tiers — fifth walk

**By module:**

| Module | Names promoted |
|---|---|
| `mgf.common.exceptions` (process-launch + held re-evaluation) | `ProcessError`, `ProcessTimeout`, `ProcessNonZeroExit`, `ProcessSignaled`, `ServiceNotReady`, `GuestUnreachableError`, `GuestCommandError` (7) |
| `mgf.common.settings` (held re-evaluation) | `VaultSettings` (1) |
| `mgf.common.observability` | `setup_logging`, `setup_otel`, `operation_span`, `report_now`, `install_excepthook`, `install_threading_excepthook`, `Redactor`, `DEFAULT_REDACTION_KEYS`, `LogFormat`, `default_log_path`, `default_crash_dir`, `default_state_dir`, `get_logger`, `JsonFormatter` (14) |
| `mgf.common.typing` | `CancellationProtocol`, `ReporterProtocol`, `LogSinkProtocol`, `VaultProtocol` (4) |
| `mgf.common.progress` | `CancellationToken`, `ProgressReporter`, `NullReporter`, `RecordingReporter` (4) |
| `mgf.common.progress.asyncio` | `AsyncCancellationToken` (1) |
| `mgf.common.vault` | `EnvVault`, `KeyringVault`, `EncryptedFileVault`, `EnvVaultConfig`, `KeyringVaultConfig`, `EncryptedFileVaultConfig` (6) |
| `mgf.common.versioned` | `VersionedFileStore`, `MigrateFn` (2) |
| `mgf.common.cli` | `EXIT_SUCCESS`, `EXIT_CONFIG_ERROR`, `EXIT_OPERATION_ERROR`, `EXIT_GUEST_ERROR`, `EXIT_ENVIRONMENT_ERROR`, `EXIT_PROVIDER_ERROR`, `EXIT_VAULT_ERROR`, `EXIT_UNKNOWN`, `run_and_translate`, `translate_exceptions`, `main_entry`, `field_provenance`, `env_vars_known_to_mgf`, `FieldSource`, `SourceKind` (15) |
| `mgf.common.process` | `run`, `ProcessResult`, `Service`, `Service.start/stop` (row), `Service.wait`, `Service.is_running/pid/returncode` (row), `ReadinessProbe`, `PortReady`, `HttpReady`, `LogLineReady`, `CallableReady`, `AllOf`, `AnyOf` (13) |
| `mgf.common.providers` | `ProviderABC`, `translate_at_seam` (2) |

**Total in this walk: 69 names** (the per-table arithmetic comes
to 69; PUBLIC_API.md's promoted-row count matches exactly).

### Criteria satisfied (per name)

- **observability** core (14): every consumer wires `bootstrap()`
  which calls into these; `setup_logging` / `setup_otel` /
  `report_now` / `Redactor` shape has been stable since v0.10ish.
  The default-path helpers (`default_log_path` /
  `default_crash_dir` / `default_state_dir`) closed PAPER-07 in
  v0.13.0 and have been unchanged since.
- **typing protocols** (4): all four are stable contracts
  consumers implement (vm-vmanager-app's GUI for `LogSinkProtocol`,
  every consumer's vault for `VaultProtocol`, every CLI for
  `CancellationProtocol`/`ReporterProtocol`).
- **progress** (5): `CancellationToken` + the reporter shape have
  been in use since v0.10; `AsyncCancellationToken` is the
  asyncio sibling; both have stable shape.
- **vault** (6): concrete backends + typed configs. Shape has
  been stable since the typed-config landing (which closed the
  config-leak per PAPER-12).
- **versioned** (2): `VersionedFileStore[T]` is used by vm-vmanager-app
  for every per-VM config file + by inthewords for hook specs;
  shape unchanged since v0.13.
- **cli** (15): exit codes are stable since v0.10 and the
  translator family + provenance helpers are equally settled.
- **process** (13): `run` / `Service` / `ReadinessProbe` family
  is vm-vmanager-app's bread-and-butter; the readiness probes
  (`PortReady`, `HttpReady`, `LogLineReady`, `CallableReady`,
  `AllOf`, `AnyOf`) have been shipping since v0.22.
- **providers** (2): `ProviderABC` + `translate_at_seam` is the
  DP-02 seam pattern documented in the standards docs; shape
  has been stable since v0.10.

### Re-evaluation of v0.32.2-held items

- **`GuestUnreachableError` + `GuestCommandError`**: held in v0.32.2
  because they're vm-vmanager-app-only at the moment. On reconsideration,
  the shape is stable (bare subclasses inheriting from
  `GuestError`), and any future Guest-shaped consumer would
  receive them at the right tier without a tier bump. Promoted.
- **`VaultSettings`**: held in v0.32.2 because the typed/untyped
  config split looked unfinished. On reconsideration, the
  field-wrapper itself (`backend: str` + `config: dict | typed`)
  is stable; what's still settling is the *typed-union variant*
  (`BuiltinVaultBackendConfig`) which stays experimental. The
  wrapper promotes; the typed union holds one more cycle.

### Explicitly NOT promoted (still held)

- **OpenTelemetry re-exports** (`Span`, `SpanContext`, `SpanKind`,
  `SpanStatus`, `StatusCode`) + the span-helper wrappers
  (`get_current_span`, `set_attribute`, `add_event`, `set_status`)
  — held as a coherent block; the re-export contract has its own
  questions (what happens if OTel SDK changes shape?). Walk together
  later.
- **Registry-based extension API** — `register_crash_sink` /
  `register_log_handler` / `register_span_processor` /
  `register_redaction_pattern_group` / `register_vault_backend` /
  `register_cli_subcommand` plus factory type aliases
  (`CrashSink`, `LogHandlerFactory`, `PatternGroupFactory`,
  `SpanProcessorFactory`, `VaultBackendFactory`) and
  `RedactionGroup` enum. Held: PAPER-10 (entry-point auto-discovery,
  currently DEFERRED) may reshape this surface.
- **Subcommand registry** (`register_cli_subcommand`, `subcommand`,
  `unregister_cli_subcommand`, `CliSubcommand`, `build_cli`,
  `dispatch`) — recent addition; held one more minor for evidence.
- **Generic registry primitive** (`Registry`, `make_registry`) —
  future-use only; held until a real internal use case migrates to it.
- **`BuiltinVaultBackendConfig`** — discriminated-union helper;
  paired with the typed-config promotion above but a half-step
  newer in shape.
- **`AsyncJsonHandler`** — recent addition; held one more minor for
  evidence.
- **`HostEnvironmentError`** — `EnvironmentError` deprecated alias
  still in play. AP-12 cycle finishes in v0.33.
- **`make_settings_config`** — already `deprecated` tier; removal
  scheduled for v0.33.
- **`configure`** — already `deprecated` tier; removal scheduled
  for v0.33.

### Promotion tally after v0.32.3

  v0.32.0:  12 names  (core foundation + 6 category bases)
  v0.32.1:  19 names  (HTTP-01 hierarchy + MgfSettings family)
  v0.32.2:  11 names  (concrete exception leaves + small helpers)
  v0.32.3:  69 names  (omnibus walk across remaining surfaces)
  -------
  Total:   111 stable / 97 experimental

The stable surface now exceeds the experimental surface — a
qualitative milestone in the v0.32+ stabilization chapter.
Remaining experimental falls into three buckets: extension API
(needs PAPER-10 resolution), OpenTelemetry re-exports (coherent
block), and recent additions (need one more minor of evidence).

### Federation note

mgf-common is in its v0.32+ stabilization window — no new public
surface, only tier flips. Patch number is now at v0.32.3; this is
the fifth doc-only patch in the chapter. v0.33 is the natural
landing for the AP-12 deprecation removals (`configure`,
`make_settings_config`, `EnvironmentError` alias) — that's a MINOR
release because the removals are breaking by semver.

---

## [0.32.2] — 2026-05-11

PyPI: <https://pypi.org/project/mgf-common/0.32.2/> · Tag: `v0.32.2`

> **Doc-only patch release.** Fourth experimental → stable walk:
> 11 names across the concrete-exception-leaves layer + two small
> helpers. No runtime change.

### Public-API stability tiers — fourth walk (11 names)

Continuing the v0.32+ stabilization-window walk. This pass moves
beyond the category bases (locked in v0.32.0/v0.32.1) into the
concrete leaves that consumers raise and catch directly.

**Promoted to `stable` (v0.32.2):**

| Module | Names |
|---|---|
| `mgf.common` | `UnconfiguredWarning`, `bootstrap_for_test`, `add_help_note` |
| `mgf.common.exceptions` | `SchemaValidationError`, `AppConfigError`, `AppConfigErrorKind`, `CommandError`, `ResourceNotFoundError`, `ResourceAlreadyExistsError`, `BootstrapError`, `CancellationError` |

Criteria:

- All eight concrete exception leaves have been raised by mgf-common
  itself for 12+ minor cycles. Consumer `except` clauses depend on
  them.
- `AppConfigErrorKind` is the StrEnum whose string values were
  pinned in PAPER-11 as machine-readable; locking the enum is the
  natural follow-on.
- `BootstrapError`'s `.failed_step` attribute is the transactional-
  bootstrap diagnostic contract — already documented in
  `docs/standards/ERROR_HANDLING.md`.
- `CancellationError` subclasses `OperationError` so existing
  category-catch clauses already handle it; the rename / addition
  shape has been stable through 10+ minor cycles.
- `UnconfiguredWarning` is the warning every test suite catches /
  asserts against when running tests without `bootstrap()`.
- `bootstrap_for_test` is in every consumer's test setup.
- `add_help_note` is a small PEP 678 helper with no shape questions.

### Explicitly NOT promoted (still held)

- `GuestUnreachableError` + `GuestCommandError` — concrete leaves
  in the `GuestError` category. Used heavily by vm-vmanager-app but no
  cross-consumer evidence yet (inthewords / PlasmaMapper don't
  raise them). The category base `GuestError` is stable (v0.32.0);
  the concretes can wait.
- `HostEnvironmentError` — still has the deprecated
  `EnvironmentError` alias in play. Wait one more minor per AP-12.
- `VaultSettings` + `BuiltinVaultBackendConfig` + the typed vault
  config union — still split between untyped `dict` and discriminated
  union; the consolidation isn't done.
- `register_crash_sink` / `register_log_handler` /
  `register_span_processor` / `register_redaction_pattern_group` +
  the `LogHandlerFactory` / `SpanProcessorFactory` type aliases —
  extension API still evolving; PAPER-10 (entry-point auto-discovery)
  is deferred and may reshape this.
- Everything in `mgf.common.cli`, `mgf.common.process`,
  `mgf.common.versioned`, `mgf.common.vault`, `mgf.common.progress`,
  `mgf.common.observability` — deserves dedicated walks once
  another minor cycle of evidence accumulates.

### Promotion tally after v0.32.2

  v0.32.0 promoted: 12 (core foundation + 6 category bases)
  v0.32.1 promoted: 19 (HTTP-01 hierarchy + MgfSettings family)
  v0.32.2 promoted: 11 (concrete exception leaves + 3 small helpers)
  --------
  Total stable:     42
  Remaining experimental: 166

### Federation note

mgf-common is in its v0.32+ stabilization window — no new public
surface, only tier flips. Three sequential patch releases (0.32.0,
0.32.1, 0.32.2) on the same day reflects the accumulated incubation
depth of the existing surface; the v0.32+ chapter is about closing
the gap between "could be stable" and "is stable" before v1.0 lock.

---

## [0.32.1] — 2026-05-11

PyPI: <https://pypi.org/project/mgf-common/0.32.1/> · Tag: `v0.32.1`

> **Doc-only patch release.** No runtime change. Two more stable-tier
> walks landed: the HTTP-01 web-exception hierarchy (12 names) and
> the `MgfSettings` configuration family (7 names). Plus a small
> internal cleanup (ruff SIM warnings in `_catch_up.py`) that
> predates the v0.32+ stabilization window.

### Public-API stability tiers — second + third walks (19 names)

Per the v0.32+ stabilization roadmap, walking public names that
have survived multiple minor releases AND have multi-consumer use
from `experimental` to `stable`. Two batches in this patch:

**HTTP-01 hierarchy (12 names)** — promoted to `stable` (v0.32.1):

| Module | Names |
|---|---|
| `mgf.common.exceptions` | `HttpError`, `HttpClientError`, `HttpServerError`, `HttpBadRequest`, `HttpUnauthorized`, `HttpForbidden`, `HttpNotFound`, `HttpConflict`, `HttpGone`, `HttpUnprocessable`, `HttpInternalServerError`, `HttpServiceUnavailable` |

Shipped in v0.18.0 (HTTP-01 filing); 14 minor cycles of incubation;
used by mgf-fastapi's `ExceptionTranslationMiddleware`,
PlasmaMapper's domain exceptions (subclass HTTP-01 leaves directly),
inthewords' DRF integration, and mgf-fastapi v0.3's `bearer_session`
(which raises `HttpUnauthorized`). The three-level category-catch
property (`except HttpError:` / `except HttpClientError:` /
`except HttpServerError:`) and the `http_status: int` class
attribute contract are now locked.

**`MgfSettings` family (7 names)** — promoted to `stable` (v0.32.1):

| Module | Names |
|---|---|
| `mgf.common.config` | `BaseAppSettings`, `settings_config` |
| `mgf.common.settings` | `MgfSettings`, `LoggingSettings`, `OtelSettings`, `CrashSettings`, `RedactionSettings` |

`MgfSettings` is the typed root every consumer instantiates;
`BaseAppSettings` is the base every consumer's own settings class
inherits from. The four promoted sub-settings dataclasses
(`LoggingSettings`, `OtelSettings`, `CrashSettings`,
`RedactionSettings`) have had stable shapes for 12+ minor cycles.

### Explicitly NOT promoted in this round (hold for next walk)

- `VaultSettings` + `BuiltinVaultBackendConfig` + the typed vault
  config union — discriminated-union shape is more recent; the
  pattern itself is fine but consumers have less exposure to it.
- `bootstrap_for_test`, `add_help_note` — test/helper shape;
  smaller exposure surface.
- `UnconfiguredWarning`, `HostEnvironmentError` — deprecation /
  rename cycles still in play; wait one more minor per AP-12.
- Concrete exception leaves (`SchemaValidationError`,
  `AppConfigError`, `CommandError`, etc.) — used but more granular;
  one more cycle for shape evidence.
- Extension API (`register_crash_sink`, `register_log_handler`,
  `register_span_processor`, `register_redaction_pattern_group`,
  the factory type aliases) — recent additions; the registry shape
  is still evolving.
- Everything in `mgf.common.cli`, `mgf.common.process`,
  `mgf.common.versioned`, `mgf.common.vault`, `mgf.common.progress`,
  `mgf.common.observability` — deserves dedicated walks once
  another minor cycle of evidence accumulates.

### Other changes

- **chore(catch-up):** cleared two pre-existing ruff SIM warnings
  in `src/mgf/common/cli/_catch_up.py` (`_has_pip_tools_layout` +
  `_is_private_module`). Also refreshed the stale "deferred
  follow-up" line in `_has_pip_tools_layout`'s docstring that
  PAPER-15 v0.32.0 invalidated. No behaviour change.

### Promotion tally after v0.32.1

  v0.32.0 promoted: 12 (mgf.common + exception category bases)
  v0.32.1 promoted: 19 (HTTP-01 hierarchy + MgfSettings family)
  --------
  Total stable:    31
  Remaining experimental: 177

### Federation note

mgf-common is in its v0.32+ stabilization window — no new public
surface, only tier flips + behaviour fixes to existing tooling.
This patch is doc-only on the runtime side; consumers can `pip
install --upgrade mgf-common` to refresh their installed
`PUBLIC_API.md` view of the tier landscape, but no runtime
behaviour changes.

---

## [0.32.0] — 2026-05-10

PyPI: <https://pypi.org/project/mgf-common/0.32.0/> · Tag: `v0.32.0`

> **First v0.32+ stabilization-window release.** Per the federation
> roadmap, v0.32+ adds NO new mgf-common public surface — this release
> closes FEEDBACK [PAPER-15](FEEDBACK.md) by extending the existing
> `mgf-common catch-up` audit to recognise pip-tools-shaped projects
> as first-class manifests. Cutover guide:
> [`docs/cutover/v0.32.0.md`](docs/cutover/v0.32.0.md).

### Changed — `mgf-common catch-up`

- **Pip-tools manifest audit (PAPER-15).** Projects that declare deps
  via `requirements/*.{txt,lock}` (no `pyproject.toml`) are now
  audited as first-class manifests, not skipped with an info-level
  workaround note. The pin row reads `mgf-common` from
  `requirements/base.txt` (with fallbacks: `main.txt`, `runtime.txt`,
  then flat `requirements.txt` at the project root). Staleness +
  auto-rewrite + editable-install guard all apply uniformly with
  the pyproject path; backups land in the same file's directory.
- **Four-gate audit reads standalone configs.** For pip-tools
  projects, the four gates are read from standalone config files:
  - **ruff**: `ruff.toml` or `.ruff.toml`
  - **mypy**: `mypy.ini` / `.mypy.ini` (or `setup.cfg [mypy]`) with
    `strict = True`
  - **pytest**: `pytest.ini` / `setup.cfg [tool:pytest]` / `tox.ini
    [pytest]` with `filterwarnings = error` (single-line or
    multi-line forms both accepted)
  - **coverage**: `.coveragerc` or `coverage.ini`
- **Audit row labels.** Pip-tools projects see audit rows titled
  `requirements/* — mgf-common pin` and `requirements/* — four gates`
  (was `pyproject.toml — …` before, even though they couldn't
  actually be audited).

### Migration

No migration needed for pyproject-based projects; the pyproject path
is unchanged. Pip-tools projects that previously saw the
"deferred follow-up" info message now see real audit rows — re-run
`mgf-common catch-up` to see them.

### Engineering

- New `_check_pin_in_requirements`, `_check_four_gates_standalone`,
  and four small `_has_*_standalone` helpers in
  `src/mgf/common/cli/_catch_up.py`. The historical
  `_check_pyproject_pin` / `_check_four_gates` names are preserved
  as the public entry points (they dispatch internally) so the
  existing import surface holds.
- 17 new unit tests under `tests/unit/cli/test_catch_up.py`:
  `TestPipToolsPinCheck` (9 tests) and `TestPipToolsFourGates`
  (8 tests). The historical TestPipToolsLayoutDetection retains the
  layout-detection unit tests + the "no-manifest" warning case; the
  old "v0.17.2 info-level workaround" tests are removed since the
  workaround is no longer the answer.

### Federation note

mgf-common is in its v0.32+ stabilization window — no new mgf-common
public surface. The change is internal to the catch-up CLI's behaviour
and visible only to consumers running `mgf-common catch-up`. Sibling
public surface additions (e.g. mgf-fastapi v0.3's `bearer_session`,
PAPER-29) happen in the siblings under their independent semver.

### Public-API stability tiers — first experimental → stable walk

Per the federation roadmap, v0.32+ stabilization includes promoting
public names that have survived multiple minor releases AND have
real consumer use from `experimental` to `stable`. This release ships
the **first such walk**, deliberately conservative: only the load-
bearing foundation surface that every consumer touches.

**Promoted to `stable` (12 names):**

| Module | Names |
|---|---|
| `mgf.common` | `bootstrap`, `current_context`, `AppContext`, `app_name`, `app_version` |
| `mgf.common.exceptions` | `AppError`, `ConfigError`, `ProviderError`, `OperationError`, `GuestError`, `VaultError`, `SchedulerError` |

These names have existed in their current shape since v0.13.0 or
earlier (12+ minor cycles of incubation) and form the contract every
mgf-common consumer (vm-vmanager-app, inthewords, PlasmaMapper, every sibling
package) builds against. `AppError` and its seven category bases are
the typed-exception hierarchy roots that every consumer's `except`
clauses depend on — locking them down now (with `AP-12` deprecation
discipline applying to any future removal) is a v1.0-readiness move.

**Explicitly NOT promoted in this pass** (held experimental for at
least one more minor):

- The HTTP-01 hierarchy (`HttpError`, `HttpClientError`,
  `HttpServerError`, and the per-status leaves like `HttpUnauthorized`):
  added in v0.18.0; multi-consumer use is real but young. Promotion
  candidate for v0.33 or v0.34.
- Concrete exception leaves (`SchemaValidationError`, `AppConfigError`,
  `CommandError`, `ResourceNotFoundError`, `ResourceAlreadyExistsError`,
  `BootstrapError`, `CancellationError`, `GuestUnreachableError`,
  `GuestCommandError`): used but more granular; one more cycle for
  shape evidence.
- `HostEnvironmentError`: recent rename from `EnvironmentError`; the
  deprecated alias is still in play. Wait one cycle.
- `MgfSettings` family (`MgfSettings`, `BaseAppSettings`,
  `settings_config`, the sub-settings dataclasses): large surface area
  that deserves its own dedicated walk.
- `bootstrap_for_test`, `add_help_note`, `UnconfiguredWarning`: recent
  additions that haven't accumulated cross-consumer evidence yet.
- Everything in newer submodules (`db`, `alembic`, `django` — now
  parallel siblings; the mgf-common-side aliases are deprecation shims
  where any remain).
- `configure` stays `deprecated` (PAPER-25 migration to `bootstrap`).
- `EnvironmentError` (the legacy shadowing-builtin alias) stays
  `experimental` — it's path-to-removal, not path-to-stable.

### `PUBLIC_API.md` rendering

The new stable rows are rendered as `**stable** (v0.32.0)` so the
release in which each name was promoted is searchable from the
contract document.

### Reference-consumer profiles — PAPER-06 full restructure

Per [FEEDBACK PAPER-06](FEEDBACK.md), consumer-specific evidence
moves out of `docs/standards/*.md` into a new
[`docs/reference-consumers/`](docs/reference-consumers/) folder.
Three profile files ship with this release:

- [`vm-vmanager-app.md`](docs/reference-consumers/vm-vmanager-app.md) — desktop /
  Qt VM-management; the original reference consumer.
- [`inthewords.md`](docs/reference-consumers/inthewords.md) — Django
  web app; closed strategic-review Priority 1 (second consumer).
- [`PlasmaMapper.md`](docs/reference-consumers/PlasmaMapper.md) —
  FastAPI SaaS; the trigger for PAPER-26/27/28/29.

Each profile documents the consumer's project type, shape-at-a-
glance, pattern-by-pattern evidence, known non-adoptions, and the
friction it surfaced as papers. The folder's
[`README.md`](docs/reference-consumers/README.md) documents the
intake flow for a fourth or fifth profile.

`docs/standards/README.md`'s caveat banner generalises from
"reference consumer = vm-vmanager-app" to "three reference consumers; see
reference-consumers/". Inline consumer mentions in the standards
docs themselves stay (they're one-clause evidence groundings, not
the heavy "AS-IS in X" paragraphs the paper targeted) — the full
restructure preserves "grounded in real code" while moving the
detailed shape next door.

---

## [0.31.0] — 2026-05-10

PyPI: <https://pypi.org/project/mgf-common/0.31.0/> · Tag: `v0.31.0`

> **Cutover guide:** [`docs/cutover/v0.31.0.md`](docs/cutover/v0.31.0.md)
> — fourth and final framework-adapter sibling extraction per
> [ASPIRE-06](FEEDBACK.md). The entire `mgf.common.django`
> subpackage moves to a new sibling distribution **`mgf-django`**
> (import path `mgf.django`). 🔴 **breaking** for any consumer
> importing from `mgf.common.django.*` or installing the
> `mgf-common[django]` extra; the migration is a rename + dependency
> swap PLUS a Django app-label rename (`"mgf_common"` →
> `"mgf_django"`).
>
> After this release, mgf-common settles into its v1.0 target
> shape: pure-Python infrastructure + standards source-of-truth.
> The v0.32+ stabilization window opens.

### Removed (BREAKING)

- **`mgf.common.django.*`** — every name (`MgfCommonConfig`,
  `MgfRequestIdMiddleware`, `MgfContextMiddleware`,
  `JsonFormatter`, `DjangoSettingsBridge`) moves to `mgf.django.*`
  in the new **`mgf-django`** sibling distribution.
- **`[django]` and `[django-test]` extras** in `mgf-common` are
  deleted. Consumers install `mgf-django` directly.
- **App label `"mgf_common"` → `"mgf_django"`** — the
  `MgfCommonConfig.label` rebrands at the sibling rename. Consumer
  config hardcoding `"mgf_common"` (typically
  `apps.get_app_config("mgf_common")` or migration table-prefix
  configs) MUST update.

### Migration

| Was | Now |
|---|---|
| `from mgf.common.django import …` | `from mgf.django import …` |
| `INSTALLED_APPS = [..., "mgf.common.django"]` | `INSTALLED_APPS = [..., "mgf.django"]` |
| `MIDDLEWARE = ["mgf.common.django.MgfRequestIdMiddleware", ...]` | `MIDDLEWARE = ["mgf.django.MgfRequestIdMiddleware", ...]` |
| `LOGGING = {"formatters": {"json": {"()": "mgf.common.django.JsonFormatter"}}}` | `LOGGING = {"formatters": {"json": {"()": "mgf.django.JsonFormatter"}}}` |
| `apps.get_app_config("mgf_common")` | `apps.get_app_config("mgf_django")` |
| `pip install 'mgf-common[django]'` | `pip install mgf-django` |

The new `mgf-django` sibling pins to `mgf-common>=0.31,<0.32`. The
v0.31 cutover guide details the dependency-pin and import-rewrite
steps per consumer (only inthewords is affected; their live config
doesn't actually use mgf.common.django so they only bump the
pin — doc cosmetics included).

### Engineering

- 18 unit tests removed from mgf-common (the `tests/unit/django/`
  suite moved to `mgf-django/tests/unit/`).
- PUBLIC_API.json: modules 16 → 15, names 175 → 170.
- `_init_project.py` scaffold: `[django]` kind now emits
  `mgf-common + mgf-django` (no more `mgf-common[django]`); pin
  floors bump from `>=0.30,<0.31` to `>=0.31,<0.32` across all
  five kinds.
- Version bump: 0.30.0 → 0.31.0. Parallel bumps in pyproject.toml
  + `src/mgf/common/__init__.py` keep the AP-13 version-sync test
  green.
- Federation milestone: this is the FOURTH and FINAL framework-
  adapter extraction (mgf-fastapi v0.28, mgf-http v0.29,
  mgf-sqlalchemy + mgf-alembic + mgf-fastapi v0.2 v0.30,
  mgf-django v0.31). mgf-common is now the federation leaf with
  pure-Python infrastructure only.

### Documentation

- `docs/cutover/v0.31.0.md` — full migration map.
- `docs/recipes/django.md` removed from mgf-common; the canonical
  recipe now lives at `mgf-django/docs/recipes/django.md`.
- `docs/release/federation_roadmap.md` retrospective row filled
  for v0.31.0; v1.0 target architecture table now reflects the
  fully-extracted shape.
- `docs/standards/web/README.md`, `docs/qa/coverage.md`,
  `docs/recipes/README.md`, `docs/reference/README.md` updated.

---

## [0.30.0] — 2026-05-10

PyPI: <https://pypi.org/project/mgf-common/0.30.0/> · Tag: `v0.30.0`

> **Cutover guide:** [`docs/cutover/v0.30.0.md`](docs/cutover/v0.30.0.md)
> — third federation extraction per [ASPIRE-06](FEEDBACK.md). The
> entire `mgf.common.db` and `mgf.common.alembic` subpackages move to
> two new sibling distributions **`mgf-sqlalchemy`** (import path
> `mgf.sqlalchemy`) and **`mgf-alembic`** (import path
> `mgf.alembic`), plus the FastAPI-shaped session/lifespan helpers
> migrate to **`mgf-fastapi`** v0.2.0 as `mgf.fastapi.db`. 🔴
> **breaking** for any consumer importing from `mgf.common.db.*` or
> `mgf.common.alembic.*` or installing the `[db]`/`[db-test]`/
> `[alembic]`/`[alembic-test]` extras.

### Removed (BREAKING)

- **`mgf.common.db.*`** — every name. The pure-SQLAlchemy helpers
  (`create_engine`, `create_sessionmaker`, `tenant_session`) move to
  `mgf.sqlalchemy.*` in the new **`mgf-sqlalchemy`** sibling. The
  FastAPI-shaped helpers (`get_session`, `setup_db`) move to
  `mgf.fastapi.db.*` in **`mgf-fastapi`** v0.2.0 (gated behind the
  new `mgf-fastapi[sqlalchemy]` extra). Splitting by shape keeps
  mgf-sqlalchemy framework-agnostic per the federation roadmap.
- **`mgf.common.alembic.*`** — `configure_env` (with its PAPER-22
  `include_object` / `include_schemas` knobs) moves to
  `mgf.alembic.*` in the new **`mgf-alembic`** sibling.
- **`[db]`, `[db-test]`, `[alembic]`, `[alembic-test]` extras** in
  `mgf-common` are deleted. Consumers install
  `mgf-sqlalchemy[test]` / `mgf-alembic[test]` directly, plus
  `mgf-fastapi[sqlalchemy]` for the request-scoped helpers.

### Migration

| Was | Now |
|---|---|
| `from mgf.common.db import create_engine` | `from mgf.sqlalchemy import create_engine` |
| `from mgf.common.db import create_sessionmaker` | `from mgf.sqlalchemy import create_sessionmaker` |
| `from mgf.common.db import tenant_session` | `from mgf.sqlalchemy import tenant_session` |
| `from mgf.common.db import get_session` | `from mgf.fastapi.db import get_session` |
| `from mgf.common.db import setup_db` | `from mgf.fastapi.db import setup_db` |
| `from mgf.common.alembic import configure_env` | `from mgf.alembic import configure_env` |
| `pip install 'mgf-common[db]'` | `pip install mgf-sqlalchemy` |
| `pip install 'mgf-common[db-test]'` | `pip install 'mgf-sqlalchemy[test]'` |
| `pip install 'mgf-common[alembic]'` | `pip install mgf-alembic` |
| `pip install 'mgf-common[alembic-test]'` | `pip install 'mgf-alembic[test]'` |

The three new sibling pieces pin to `mgf-common>=0.30,<0.31`. The
v0.30 cutover guide details the dependency-pin and import-rewrite
steps per consumer.

### Engineering

- 26 unit tests removed from mgf-common (the `tests/unit/db/` +
  `tests/unit/alembic/` suites moved to the new siblings:
  `mgf-sqlalchemy/tests/unit/` carries 13, `mgf-alembic/tests/unit/`
  carries 16, and `mgf-fastapi/tests/unit/test_db.py` carries 6 from
  the FastAPI-shape coverage).
- PUBLIC_API.json: modules 18 → 16, names 181 → 175 (`mgf.common.db`
  + `mgf.common.alembic` removed).
- `_init_project.py` scaffold: `[fastapi]` kind now emits
  `mgf-fastapi[sqlalchemy] + mgf-sqlalchemy + mgf-alembic +
  mgf-http`; pin floors bump from `>=0.29,<0.30` to `>=0.30,<0.31`.
- Version bump: 0.29.0 → 0.30.0. Parallel bumps in pyproject.toml +
  `src/mgf/common/__init__.py` keep the AP-13 version-sync test
  green.

### Documentation

- `docs/cutover/v0.30.0.md` — full migration map with sed-friendly
  rewrites for the import + extra changes.
- `docs/recipes/db.md` and `docs/recipes/alembic.md` removed from
  mgf-common; the canonical recipes now live at
  `mgf-sqlalchemy/docs/recipes/sqlalchemy.md` and
  `mgf-alembic/docs/recipes/alembic.md`.
- `docs/release/federation_roadmap.md` retrospective row filled for
  v0.30.0.

---

## [0.29.0] — 2026-05-09

PyPI: <https://pypi.org/project/mgf-common/0.29.0/> · Tag: `v0.29.0`

> **Cutover guide:** [`docs/cutover/v0.29.0.md`](docs/cutover/v0.29.0.md)
> — second federation extraction per [ASPIRE-06](FEEDBACK.md). The
> entire `mgf.common.http` subpackage moves to a new sibling
> distribution **`mgf-http`** (import path `mgf.http`). 🔴
> **breaking** for any consumer importing from `mgf.common.http.*`
> or installing the `mgf-common[http]` extra; the migration is a
> rename + dependency swap PLUS one renamed class
> (`HttpClientError` → `HttpTransportError`).

### Removed (BREAKING)

- **`mgf.common.http.*`** — every name (`AsyncHttpClient`,
  `RetryPolicy`, the transport-failure `HttpClientError`) moves
  to `mgf.http.*` in the new **`mgf-http`** sibling distribution.
- **`[http]` and `[http-test]` extras** in `mgf-common` are
  deleted. Consumers install `mgf-http` (or `mgf-http[test]`)
  directly.

### Renamed (BREAKING — sibling-side)

- **`HttpClientError` → `HttpTransportError`** — the
  transport-failure leaf renames at the cutover to disambiguate
  from `mgf.common.exceptions.HttpClientError` (the HTTP-01
  4xx-class hierarchy parent, which **stays in mgf-common**
  unchanged). Renaming was deferred until the extraction since
  fixing the collision in mgf-common alone would have churned
  every consumer twice. Now both classes have unambiguous names:
  - `mgf.common.exceptions.HttpClientError` (status-class
    hierarchy parent, 4xx-class) — **kept**.
  - `mgf.http.exceptions.HttpTransportError` (transport-layer
    failure leaf) — **renamed**.

### Migration

| Was | Now |
|---|---|
| `from mgf.common.http import AsyncHttpClient` | `from mgf.http import AsyncHttpClient` |
| `from mgf.common.http import RetryPolicy` | `from mgf.http import RetryPolicy` |
| `from mgf.common.http import HttpClientError` | `from mgf.http import HttpTransportError` |
| `pip install 'mgf-common[http]'` | `pip install mgf-http` |
| `pip install 'mgf-common[http-test]'` | `pip install 'mgf-http[test]'` |

The new `mgf-http` sibling pins to `mgf-common>=0.29,<0.30`. The
v0.29 cutover guide details the dependency-pin and import-rewrite
steps per consumer (PlasmaMapper + mgf-apiprobe migrate; vm-vmanager-app
+ inthewords have no http surface and need only the
`mgf-common = ">=0.29,<0.30"` pin bump).

### Documentation

- **`docs/cutover/v0.29.0.md`** (new) — full rename map +
  sed-friendly mechanical rewrite for the
  `HttpClientError` → `HttpTransportError` rename + per-consumer
  migration notes.
- **`docs/recipes/http.md`** — moved to `mgf-http/docs/recipes/`
  per **D5**. The mgf-common recipes README keeps the entry as a
  `(sibling)` pointer.
- **`docs/recipes/README.md`**, **`docs/reference/README.md`**,
  **`docs/reference/full_app.md`**, **`docs/recipes/{db,
  notebook,multi_tenant_settings,providers}.md`**,
  **`docs/audience/security.md`**, **`docs/qa/coverage.md`**,
  **`docs/internal/lift_checklist.md`** — cross-references
  rewritten to point at the new `mgf.http` sibling import path
  and to use the renamed `HttpTransportError` class.

### Engineering

- **`tools/generate_public_api_json.py`** — `PUBLIC_MODULES` no
  longer includes `mgf.common.http`. PUBLIC_API.json regenerated.
- **`src/mgf/common/cli/_init_project.py`** — scaffolded
  pyproject snippet emits sibling deps (`mgf-fastapi`,
  `mgf-http`) instead of the legacy `mgf-common[fastapi,http]`
  extras shape. Per-kind dep lists updated for the two
  extractions to date; pin bumped from the stale `0.9.0,<0.10`
  to `0.29,<0.30`. EH-11 retry-policy reference points at
  `mgf.http.RetryPolicy` (sibling) instead of the deleted
  `mgf.common.http.RetryPolicy`.
- **`tests/unit/cli/test_init_project.py`** — `test_pin_includes_extras_for_kind`
  renamed to `test_pin_includes_siblings_for_kind` and rewritten
  to assert the new sibling-dep emission. Negative assertions
  enforce that `mgf-common[fastapi,...]` / `mgf-common[http,...]`
  do NOT reappear (regression guard for the federation rule).

### Engineering note — what changed internally

Two non-breaking improvements landed during the v0.29 extraction:

1. **Closed-box leak fix.** `mgf.common.http._client` previously
   reached into `mgf.common._identity._APP_NAME` /
   `_APP_VERSION` to compute the User-Agent — a private import
   from another module within the same distribution, but still
   a closed-box-rule violation. The sibling now uses the public
   `mgf.common.app_name()` / `mgf.common.app_version()` accessors.
   Behaviour is identical.
2. **User-Agent attribution.** The library suffix in the
   User-Agent header changed from `mgf-common/<libver>` to
   `mgf-http/<libver>`. Accurate now that `AsyncHttpClient`
   ships from the sibling. Consumers that match on
   `mgf-common/<libver>` for HTTP traffic specifically should
   update to `mgf-http/<libver>`.

---

## [0.28.0] — 2026-05-08

PyPI: <https://pypi.org/project/mgf-common/0.28.0/> · Tag: `v0.28.0`

> **Cutover guide:** [`docs/cutover/v0.28.0.md`](docs/cutover/v0.28.0.md)
> — first federation extraction per [ASPIRE-06](FEEDBACK.md). The
> entire `mgf.common.fastapi` subpackage moves to a new sibling
> distribution **`mgf-fastapi`** (import path `mgf.fastapi`). This
> is a 🔴 **breaking** ship for any consumer importing from
> `mgf.common.fastapi.*` or the `mgf-common[fastapi]` extra; the
> migration is a rename + dependency swap (no code-shape changes).

### Removed (BREAKING)

- **`mgf.common.fastapi.*`** — every name (`setup_lifespan`,
  `RequestIdMiddleware`, `ExceptionTranslationMiddleware`,
  `current_request_id`, `REQUEST_ID_HEADER`, `DEFAULT_STATUS_MAP`,
  `get_app_context`, `get_settings`, `get_request_id`,
  `IpAllowlist`, `LOOPBACK_CIDRS`, `run_test_app`,
  `HmacWebhookVerifier`, `verify_request`, `WebhookHeaderSchema`,
  `SVIX_SCHEMA`) moves to `mgf.fastapi.*` in the new
  **`mgf-fastapi`** sibling distribution.
- **`mgf.common.exceptions.HmacVerificationError`** — moves to
  `mgf.fastapi.exceptions` per **D3** (sibling-domain concretes
  travel with their sibling). HTTP-01 hierarchy roots
  (`HttpUnauthorized`, `HttpForbidden`, …) stay in `mgf-common`.
- **`[fastapi]` and `[fastapi-test]` extras** in `mgf-common` are
  deleted. Consumers install `mgf-fastapi` (or
  `mgf-fastapi[testing]`) directly.

### Migration

| Was | Now |
|---|---|
| `from mgf.common.fastapi import X` | `from mgf.fastapi import X` |
| `from mgf.common.fastapi.webhooks import …` | `from mgf.fastapi.webhooks import …` |
| `from mgf.common.fastapi.security import IpAllowlist` | `from mgf.fastapi.security import IpAllowlist` |
| `from mgf.common.fastapi.testing import run_test_app` | `from mgf.fastapi.testing import run_test_app` |
| `from mgf.common.exceptions import HmacVerificationError` | `from mgf.fastapi.exceptions import HmacVerificationError` |
| `pip install 'mgf-common[fastapi]'` | `pip install mgf-fastapi` |
| `pip install 'mgf-common[fastapi-test]'` | `pip install 'mgf-fastapi[testing]'` |

The new `mgf-fastapi` sibling pins to `mgf-common>=0.28,<0.29`. The
v0.28 cutover guide details the dependency-pin and import-rename
steps per consumer (PlasmaMapper is the migration consumer; vm-vmanager-app
+ inthewords have no FastAPI surface and need only the
`mgf-common = ">=0.28,<0.29"` pin bump).

### Documentation / design

- **`docs/release/federation_roadmap.md`** (new) — load-bearing
  tracking document for the v0.28 → v1.0 federation split. Per-
  sibling specs, quality gates, locked decisions (D1–D15), open
  questions, future improvements, retrospective log. The single
  source of truth for the staged extraction plan; FEEDBACK papers
  cross-reference here.
- **`FEEDBACK.md ASPIRE-06`** — federated package architecture
  approved 2026-05-08. v0.28 extracts `mgf-fastapi`; v0.29 extracts
  `mgf-http`; v0.30 extracts `mgf-sqlalchemy` + `mgf-alembic`; v0.31
  extracts `mgf-django`; v1.0 locks the federated surface. Sibling-
  domain concrete exceptions (`HmacVerificationError` etc.) move with
  their sibling; HTTP-01 hierarchy roots stay in mgf-common.
- **`FEEDBACK.md ASPIRE-05`** (filed) — long-horizon: mgf-common as
  cross-language engineering-process source-of-truth. Triggered by
  PAPER-30 (first non-Python sibling). Decision deferred until 2+
  non-Python Magogi projects exist.
- **`FEEDBACK.md PAPER-30`** (filed) — `mgf-website-template`
  cookiecutter sibling repo for Magogi marketing sites (Astro +
  Infomaniak deploy). Parallel to the Python-sibling federation, not
  in the dependency graph.
- **`FEEDBACK.md ASPIRE-04`** — restored missing `#### ASPIRE-04`
  heading dropped accidentally in commit `3c12f21`. The entry's body
  was always there; the heading was lost in a copy-edit.
- **`docs/recipes/{fastapi,webhooks}.md`** moved to
  `mgf-fastapi/docs/recipes/`. `docs/recipes/README.md`,
  `docs/standards/web/README.md`, `docs/reference/README.md`,
  `docs/reference/full_app.md`, `docs/recipes/{db,django,http,
  providers,framework-owned-logging}.md`, and
  `docs/design/multi_tenancy.md` updated to reference the new
  sibling import path.
- **`docs/internal/lift_checklist.md`** — adapter-vs-sibling
  guidance updated: framework-shaped adapters now ship as siblings
  by default (the v0.28 federation decision). Stateless,
  primitive-reusing adapters (`mgf.common.{db,http,alembic}`,
  `mgf.common.django` until extracted) remain extras.

### Engineering

- **`tests/unit/django/conftest.py`** — adds a package-scoped
  finalizer that closes Django's open `_BOOTSTRAP_STACK` at the end
  of the django package's tests. Without this, identity (`_APP_NAME =
  "test-app"`) leaked into later test files (the fastapi tests
  previously masked the leak by re-bootstrapping with their own
  identity in their lifespan tests; with those tests gone, the leak
  surfaced as crash-reporter / excepthook failures).
- **`tools/generate_public_api_json.py`** — `PUBLIC_MODULES` no
  longer includes `mgf.common.fastapi.*`. Module count: 23 → 19;
  total names: 201 → 184.

### Engineering note — first federation extraction

v0.28.0 is the **first execution** of the federation split per
ASPIRE-06. The shape this extraction proves out is the template for
v0.29 (`mgf-http`), v0.30 (`mgf-sqlalchemy`, `mgf-alembic`), and
v0.31 (`mgf-django`):

- Federation leaf (`mgf-common`) deletes the subpackage entirely
  in the same release that the sibling ships v0.1.0 — no
  deprecation cycle, no `.fastapi` stub re-exporting from
  `mgf.fastapi`. The 0.x window discipline absorbs the breakage.
- Sibling-domain concrete exceptions move with their sibling per
  **D3**; the HTTP-01 hierarchy roots (`HttpUnauthorized`,
  `HttpForbidden`, etc.) stay in `mgf-common`.
- Framework-specific recipes (`fastapi.md`, `webhooks.md`) move
  with their sibling per **D5**; cross-cutting standards
  (`docs/standards/web/`) stay in `mgf-common` per **D4**.

---

## [0.27.0] — 2026-05-08

PyPI: <https://pypi.org/project/mgf-common/0.27.0/> · Tag: `v0.27.0`

> **Cutover guide:** [`docs/cutover/v0.27.0.md`](docs/cutover/v0.27.0.md)
> — closes [PAPER-27](FEEDBACK.md) (`IpAllowlist` FastAPI dependency)
> + [PAPER-28](FEEDBACK.md) (`run_test_app` async context manager).
> Same-day batch follow-up to v0.26.0; both 🟢 nice-to-haves filed
> by PlasmaMapper in the same Phase-3 paper batch.

### Added

- **`mgf.common.fastapi.security.IpAllowlist`** (closes
  [PAPER-27](FEEDBACK.md)) — FastAPI dependency that 403s requests
  whose client IP isn't in a configured CIDR allowlist. Concentrates
  the **proxy-trust footgun** in one knob (`trust_proxy: bool = False`
  default — explicit deploy decision required to honour
  `X-Forwarded-For`). IPv4 + IPv6 dual-stack with cross-version
  matches deliberately rejected. Raises `HttpForbidden` (HTTP-01
  leaf, 403 via PAPER-24's `http_status` resolution). Default
  `cidrs=LOOPBACK_CIDRS` so tests work out-of-the-box; consumers
  override for production. Misconfigured CIDRs raise at
  construction, not first request.
- **`mgf.common.fastapi.testing.run_test_app`** (closes
  [PAPER-28](FEEDBACK.md)) — async context manager that starts
  uvicorn for a `FastAPI` app on a free port and yields the base
  URL. Replaces ~120 LOC per consumer (free-port discovery +
  race-free `server.started` await + clean-shutdown choreography
  with `CancelledError` suppression). Surfaces server-startup
  exceptions instead of timing out silently. `TimeoutError` if the
  app's lifespan blocks beyond `startup_timeout_seconds` (default
  5 s).

### Changed

- **`pyproject.toml [project.optional-dependencies] fastapi-test`**
  — adds `uvicorn>=0.30` for `run_test_app`. Existing consumers of
  the `[fastapi-test]` extra automatically pick it up on next
  `uv sync`.

### Engineering

- 24 new tests across `tests/unit/fastapi/test_security.py` (16) +
  `tests/unit/fastapi/test_testing.py` (8). Total suite: 1495
  passing.
- Both helpers follow the v0.26.0 webhooks pattern: closed-box
  module under `mgf.common.fastapi.*`, explicit `from
  mgf.common.fastapi.security import …` (not re-exported at the
  top of `mgf.common.fastapi`). Encourages discoverable
  consumer code.
- The `Request` import in `security.py` is intentionally NOT
  inside `TYPE_CHECKING` — FastAPI's dependency wiring evaluates
  the parameter's annotation at registration time via
  `get_type_hints`, so the name MUST be importable at runtime.
  `# noqa: TC002` makes the intent explicit.

---

## [0.26.0] — 2026-05-08

PyPI: <https://pypi.org/project/mgf-common/0.26.0/> · Tag: `v0.26.0`

> **Cutover guide:** [`docs/cutover/v0.26.0.md`](docs/cutover/v0.26.0.md)
> — closes [PAPER-26](FEEDBACK.md): generic Svix-shape HMAC webhook
> verification primitive. Plus the v0.25.0-deferred recipe index.

### Added

- **`mgf.common.fastapi.webhooks`** (closes [PAPER-26](FEEDBACK.md))
  — Svix-shape HMAC webhook verification. Pulls the per-consumer
  verification dance (read `<id>` / `<ts>` / `<sig>` headers,
  validate replay window, compute `HMAC-SHA256(<id>.<ts>.<body>)`,
  base64-encode, constant-time-compare against multi-version
  signature header) into one tested primitive. Public surface:
  `WebhookHeaderSchema` (frozen dataclass; case-folds header
  names), `SVIX_SCHEMA` (Svix preset; used verbatim by Clerk),
  `HmacWebhookVerifier` (transport-agnostic core; thread-safe;
  `verify(headers, body) -> (event_id, ts)`), `verify_request`
  (async FastAPI request adapter). The verifier is intentionally
  transport-agnostic so it tests cleanly without FastAPI and
  composes from non-FastAPI code paths (CLI, batch jobs).
- **`mgf.common.exceptions.HmacVerificationError`** — new HTTP-01
  leaf inheriting `HttpUnauthorized` so PAPER-24's `http_status`
  resolution maps it to 401 in
  `ExceptionTranslationMiddleware` without an explicit
  `status_map` entry.
- **`docs/recipes/webhooks.md`** — full walkthrough including
  the consumer pattern for replacing per-provider verifiers,
  key rotation, and custom schemas.
- **`docs/recipes/README.md`** — canonical recipe index (v0.25.0
  truth-source-consolidation deliverable). Kind-organised section
  + A–Z lookup table with per-recipe paper closures + brief recipe
  / standard / reference disambiguation. `docs/audience/users.md`
  now points at the README as the complete index; the audience
  page remains the adoption-order reading list.

### Engineering

- 25 new tests in `tests/unit/fastapi/test_webhooks.py` covering
  every failure mode (missing header, unparseable timestamp, replay
  window in both directions, tampered body, wrong secret, wrong
  prefix, no prefix, multi-version-sig key rotation, case-
  insensitive header lookup, custom schema, custom signature
  prefix) plus the FastAPI request-adapter integration path
  through `ExceptionTranslationMiddleware`. Total suite: 1471
  passing.
- Stripe / GitHub / Slack signing schemes are deliberately NOT
  shipped at v0.26 — their conventions differ enough (combined
  headers, hex encoding, alternate prefixes) that one
  `WebhookHeaderSchema` doesn't fit. Adding presets is tracked
  for follow-up papers when a second consumer surfaces friction.

---

## [0.25.0] — 2026-05-07

PyPI: <https://pypi.org/project/mgf-common/0.25.0/> · Tag: `v0.25.0`

> **Cutover guide:** [`docs/cutover/v0.25.0.md`](docs/cutover/v0.25.0.md)
> — **documentation restructure**. Audience-routing front
> door + flat-content directories + cutover archive policy.
> **No Python-surface change. No behaviour change.**
> 100% documentation reorganisation.

### Added

- **`docs/audience/` — 8 new files** (master `README.md` + 7
  reading lists for `shoppers / users / developers / qa /
  release / security / ai-agents`). Each ~80–150 lines, pure
  navigation, no duplicated content. The new front door for
  every kind of reader.
- **`docs/README.md`** — the documentation index that routes
  to audience pages OR content directories by kind.
- **Top-level `README.md`** — rewritten as audience router
  (~100 lines, mostly links). Replaces the previous 374-line
  verbose form.
- **Cookiecutter template `docs/` overlay** — new project
  scaffolding now ships the same audience-routing layer +
  `standards/PROJECT_OVERLAY.md` for project-specific rule
  exceptions + `qa/coverage.md` skeleton. Sibling Magogi
  libraries inherit the layout via the cookiecutter.

### Changed (file moves; `git mv` preserved history)

| Old path | New path | Why |
|---|---|---|
| `docs/public/recipes/*` | `docs/recipes/*` | Drop `/public/` intermediate; flatter |
| `docs/public/01_lifecycle.md`, `02_..10_*.md` | `docs/reference/lifecycle.md`, `exceptions.md`, etc. | Semantic names instead of numbered prefixes |
| `docs/public/library_common.md` | `docs/reference/README.md` | "reference" is the right word |
| `docs/public/tutorial.md` | `docs/tutorial.md` | One file, no `/public/` wrapper |
| `docs/public/multi_tenancy.md` | `docs/design/multi_tenancy.md` | Design paper, not how-to |
| `docs/public/federation.md` | `docs/design/federation.md` | Same |
| `docs/internal/ARCHITECTURE.md` | `docs/design/architecture.md` | Promoted to public |
| `docs/internal/STRATEGIC_REVIEW.md` | `docs/design/strategic_review.md` | Same |
| `docs/internal/EXTRACTION_HISTORY.md` | `docs/design/extraction_history.md` | Same |
| `docs/internal/TEST_COVERAGE.md` | `docs/qa/coverage.md` | New `qa/` directory |
| `docs/internal/CI_STARTHERE.md` | `docs/release/ci.md` | New `release/` directory |
| `docs/internal/BENCHMARKS.md` | `docs/ops/benchmarks.md` | New `ops/` directory |
| `docs/internal/LIFT_CHECKLIST.md` | `docs/internal/lift_checklist.md` | Stays internal; lowercase |
| `docs/cutover/v0.18.0–v0.21.0.md` | `docs/cutover/archive/v*.md` | Last-3-minor-versions live policy |

### Truth-source consolidation

| Topic | Old (multi-source) | New canonical |
|---|---|---|
| What's tested | `tests/` + `internal/TEST_COVERAGE.md` + `TESTING.md §13.10` | `docs/qa/coverage.md` (narrative); other paths now point here |
| Recipe index | recipes scattered + per-module READMEs | `docs/recipes/README.md` (planned for v0.26+) |
| Per-release migration | `CHANGELOG.md` + `cutover/*` overlap | CHANGELOG = canonical short; cutover = canonical long; cross-linked |

### Deferred to v0.26+

- **Splitting `PUBLIC_API.md`** into per-module
  `docs/reference/<module>.md` files auto-generated from
  docstrings. The split was scoped out of v0.25 to keep this
  release purely structural.
- **Auto-generation of `docs/qa/coverage.md`** from `pytest --co`
  output.

### Engineering

- **Backwards-compat stub:** `docs/internal/TEST_COVERAGE.md`
  ships as a 1-line redirect to `docs/qa/coverage.md` for
  this release; removed in v0.26.
- **179 broken links found** by an ad-hoc link checker, fixed
  in batched sed/Python passes; final state: 0 broken links
  in non-archive content (modulo placeholder example syntax
  in `tools/docs_explorer/README.md`).
- **All 1445 tests pass unchanged** — the docs restructure
  doesn't touch any code surface.
- **Wheel content unchanged.** Only `docs/standards/` ships
  via `[tool.hatch.build.targets.wheel.force-include]`; no
  `docs/standards/*` files moved, so the wheel data is
  identical except for the version bump.

### Why this round

After 12 releases (v0.13 → v0.24), the docs accumulated
multiple truth-sources, fuzzy `public`/`internal` boundaries,
and no audience-routing front door. With three real consumers
in production and the cookiecutter template extending to
sibling Magogi libraries, a clean restructure was overdue.
The shape (audience-as-navigation + flat-content) is now the
**golden-standard layout** that sibling libraries inherit.

The full design rationale + question/answer log lives in the
[v0.25.0 cutover guide](docs/cutover/v0.25.0.md).

---

## [0.24.0] — 2026-05-07

PyPI: <https://pypi.org/project/mgf-common/0.24.0/> · Tag: `v0.24.0`

> **Cutover guide:** [`docs/cutover/v0.24.0.md`](docs/cutover/v0.24.0.md)
> — codify the four-layer test pyramid as a standard so other
> projects adopt the same shape. **Documentation-only release;
> zero Python-surface change.**

### Added

- **Three new TS-* rules in `docs/standards/TESTING.md`:**
  - **TS-19** — test layer markers MUST be registered in
    `pyproject.toml::[tool.pytest.ini_options].markers`. Combined
    with `--strict-markers` (TS-05) this prevents silent typos.
  - **TS-20** — tests MUST be partitioned across
    `tests/unit/` (mirrors `src/`), `tests/integration/`
    (cross-module), `tests/e2e/` (full bootstrap-to-output),
    `tests/smoke/` (post-install sanity).
  - **TS-21** — every published library MUST have a smoke
    layer in `tests/smoke/` running in <100 ms total. CLI/desktop
    apps without a wheel surface MAY skip.
- **`docs/standards/TESTING.md` §13 — the operational layer-marker
  pattern.** New section codifying the four-layer pyramid as a
  standard: directory shape, marker registration, runtime
  budgets, the smoke-layer minimum-viable shape, the e2e
  bootstrap-fixture pattern, the integration cross-module
  pattern, and a decision tree for "which layer for a new
  test?". Section §13.10 includes mgf-common's own per-layer
  counts as a reference.
- **`docs/qa/coverage.md`** — reference document
  showing what mgf-common tests at each layer plus per-module
  coverage breakdown plus coverage gaps as a roadmap. Consumer
  projects can copy the shape for their own
  `docs/qa/coverage.md`.
- **Section renumbering in `TESTING.md`:** new §13 "Layer
  markers" pushes the original §13–§17 to §14–§18 (Anti-patterns,
  Self-review, Mechanical enforcement, AS-IS, Cross-references).

### Engineering

- v0.24.0 is the **second docs-only release** for `mgf-common`
  (after v0.20.0). The pattern: when a feature shape stabilises
  and proves itself across multiple polish rounds (here:
  v0.21 → v0.22 → v0.23 for `mgf.common.process`), the next
  step is **codifying the pattern as a standard so other
  projects adopt the same shape**. This is the load-bearing
  part of the "cornerstone library" framing — the standards
  drive consistency across the ecosystem, not just the wheel.

---

## [0.23.0] — 2026-05-07

PyPI: <https://pypi.org/project/mgf-common/0.23.0/> · Tag: `v0.23.0`

> **Cutover guide:** [`docs/cutover/v0.23.0.md`](docs/cutover/v0.23.0.md)
> — second `mgf.common.process` polish round. Quality + comprehensive
> test layer expansion. **No behaviour breaks** for v0.22.0 consumers;
> existing `run()` / `Service` / readiness predicates / Process*
> exceptions all unchanged.

### Added

- **Public `redactor` property on `JsonFormatter` and `TextFormatter`.**
  Replaces the fragile private-attribute introspection that
  `mgf.common.process._streamer.get_active_redactor` was doing
  (the `formatter._redactor` lookup). The private attribute stays
  as a back-compat alias; new callers should use the public property.
- **`Service.__repr__`** for debugging. Shape:
  `Service(name='<n>', command=[...], status=<state>, pid=<pid|->)`
  where status is `not-started` / `running` / `exited(<rc>)`.
- **Best-effort OTel span wrapping** for `run()` and
  `Service.start()`. When `mgf.common.bootstrap()` is active AND
  the OpenTelemetry SDK is installed, each launch opens an
  `operation_span("process.run.<name>")` / `operation_span(
  "service.start.<name>")` with `command_id` as a span attribute.
  Failed launches (`ProcessTimeout` / `ServiceNotReady`) are
  recorded on the span. **Gated on bootstrap state** — when no
  `AppContext` is active, the call short-circuits to
  `contextlib.nullcontext()`, avoiding the `UnconfiguredWarning`
  the unguarded `operation_span` would emit (since it reads
  `app_name()` to name the tracer). Test suites with
  `filterwarnings = error` continue to work.
- **Tuple commands accepted** — `run(("echo", "hi"))` works
  alongside `run(["echo", "hi"])`. UX paper-cut for tests/scripts
  that build command tuples. Internal validation (`_validate_command`)
  normalizes to `list[str]` so the downstream `Popen` always sees
  a list and the `ProcessResult.command` field is always a list.
- **Pytest layer markers** (`smoke`, `e2e`, `integration`)
  registered in `pyproject.toml`. Run a layer in isolation with
  `pytest -m smoke` etc.

### Test coverage expansion

- **Smoke tier** (`tests/smoke/`) — 7 tests, < 100 ms total. Minimal
  post-install sanity (imports, exception MRO, `run()` happy path,
  Service no-launch-on-init, tuple acceptance, shell-string
  rejection).
- **End-to-end tier** (`tests/e2e/`) — 8 tests:
  - **Redaction E2E** (5 tests) — bootstrap + `run()` + child
    emitting a Bearer token → assert scrubbed in BOTH captured
    `stdout` and the streamed log record. Includes `redact=False`
    opt-out, `redact=True` requires-bootstrap raise, no-bootstrap
    silent-no-redaction.
  - **Correlation E2E** (3 tests) — `command_id` consistency
    across captured records, distinct ids across concurrent
    `run()` calls in threads, Service lifecycle records share
    the same id with the streamer's records.
- **Integration tier** (`tests/integration/process/`) — 5 tests:
  - **Multi-service via `ExitStack`** (2 tests) — declared-order
    start, LIFO teardown, no-leak when the dependent's start
    fails.
  - **Concurrent `run()`** (3 tests) — 16 parallel calls produce
    16 distinct `command_id`s, captured stdout is per-call
    isolated, log records don't cross processes.
- **Unit tier additions** (`tests/unit/process/test_polish_v023.py`) —
  9 tests: restart-after-failure pattern, OTel span gating
  (no `UnconfiguredWarning` without bootstrap; nullcontext path
  vs real-span path), public-redactor property on both
  formatters, `Service.__repr__` shape across lifecycle states.

**75 → 104 tests in the process subtree (+29)**. Full repo:
1416 → 1445 (+29). Zero regressions.

### Changed (internal — no surface impact)

- **`get_active_redactor()`** prefers the new public `redactor`
  property; falls back to `_redactor` for older formatters
  (back-compat).
- **Extracted `_validate_command(command)`** helper from
  duplicated validation code in `run()` and `Service.__init__`.
  Single source of truth for "list-or-tuple of strings, non-empty,
  no shell strings."

### Engineering

- **Stability tier:** every name in `mgf.common.process` stays
  at `experimental` per AP-11. Promotion still requires one full
  MINOR cycle without a 🔴 Blocker filed.
- **Test pyramid is now well-formed:** unit (84 process module
  tests) → integration (5) → e2e (8) → smoke (7). Each layer
  catches a different class of failure; the layer markers
  encourage the right CI shape (smoke pre-flight, full suite on
  PR, e2e on nightly).

---

## [0.22.0] — 2026-05-07

PyPI: <https://pypi.org/project/mgf-common/0.22.0/> · Tag: `v0.22.0`

> **Cutover guide:** [`docs/cutover/v0.22.0.md`](docs/cutover/v0.22.0.md)
> — `mgf.common.process` polish round. Reliability fixes + simplicity
> + one small additive method. **No behaviour breaks** for v0.21.0
> consumers; existing `run()` / `Service` / readiness predicates
> continue to work unchanged.

### Fixed

- **`Service.start()` early-exit detection.** Pre-fix: a child that
  crashed during startup (typo in command, missing dependency,
  segfault) caused `start()` to burn the full readiness `timeout_s`
  before raising a useless "probe X did not resolve" error. Post-fix:
  `start()` polls `proc.poll()` alongside the readiness probe and
  raises `ServiceNotReady` immediately when the child has exited
  before becoming ready. The returncode + stderr tail land in
  `last_error` for diagnosis. **Resolution time: from full timeout
  (e.g., 30s) to ~250ms** for the failure case.
- **Thread-safe `LogLineReady` snapshot.** Pre-fix: under high-volume
  child stdout (≥ thousands of lines per second), `LogLineReady`
  iterating its capture deque could race the streamer thread's
  `append`, raising `RuntimeError: deque mutated during iteration`.
  Post-fix: snapshots use `list(deque)` — atomic against the
  appending thread under CPython's GIL. Pinned by a 2000-line +
  1ms-poll-interval stress test.
- **Setup-failure rollback.** Pre-fix: any failure between `Popen`
  and the readiness-resolved log line could leak the launched
  process. Post-fix: `Service.start()` wraps streamer init +
  readiness wait in a `try/except BaseException` that calls the
  full teardown path before re-raising. The "no leaked process on
  failure" contract now covers every code path, including
  `KeyboardInterrupt`.

### Added

- **`Service.wait(timeout=None) -> int | None`** — block until the
  child exits naturally OR `timeout` elapses. Returns the
  `returncode` on natural exit, `None` on timeout (process still
  running). The escape hatch from `with Service(...)` for "start
  this service, let it run until it crashes or completes naturally"
  — without `wait()`, the only escape was `stop()`, which kills
  the child even if it was about to exit cleanly.

### Changed (internal — no surface impact)

- **Extracted `_streamer.resolve_redactor()` and `make_streamers()`
  helpers.** Deduplicates the parallel logic between
  `mgf.common.process.run()` and `Service.start()`. Both consumers
  now resolve redaction + create streamers through one call site
  each. ~30 LOC removed.
- **`_LineSnapshot` class → closure.** The single-method
  callable wrapper became a closure (3 lines vs 25). Behaviour
  identical; mutation surface smaller.
- **Tightened `get_active_redactor()`.** Single pass over
  `logging.root.handlers`; removed the try/except chain around the
  `_GLOBAL_CONTEXT` import path that was hiding logic — formatter
  introspection IS the discovery path; the AppContext walk added
  no value.
- **Replaced bespoke `_suppress_close_errors` class with stdlib
  `contextlib.suppress(Exception)`.** Three lines deleted; behaviour
  identical.
- **Dropped unused `Service._recent_lines` deque** (leftover from
  an earlier design that pre-dated the `_StreamCapture.snapshot`
  approach).

### Engineering

- **+11 new tests in `tests/unit/process/test_polish.py`** covering:
  early-exit detection (3 tests — fast resolution, stderr-tail
  diagnostics, normal-timeout still works), thread-race resilience
  under load (1 stress test), no-leak rollback regression
  (1 test), `Service.wait()` (3 tests — natural exit, timeout,
  before-start), shared helper sanity (3 tests). All v0.21.0
  tests continue to pass unchanged.
- **64 → 75 process tests; 1405 → 1416 total. Zero regressions.**
- **Stability tier:** every name in `mgf.common.process` stays at
  `experimental` per AP-11. The polish round does not promote;
  promotion still requires one full MINOR cycle without a 🔴
  Blocker filed.

---

## [0.21.0] — 2026-05-07

PyPI: <https://pypi.org/project/mgf-common/0.21.0/> · Tag: `v0.21.0`

> **Cutover guide:** [`docs/cutover/archive/v0.21.0.md`](docs/cutover/archive/v0.21.0.md)
> — process management. New top-level surface
> `mgf.common.process` for unified subprocess + service-lifecycle
> orchestration in tools, scripts, and tests.

### Added

- **`mgf.common.process` package** (10 names; all `experimental`
  per AP-11) — unified subprocess + service-lifecycle orchestration:
  - **`run(command, *, ...)` → `ProcessResult`** — one-shot
    subprocess wrapper. List-only command (eliminates command-
    injection at the API surface), structured result with
    `command_id` correlation, timeout-with-grace SIGTERM→SIGKILL
    escalation, redaction-aware line-by-line log streaming
    through `mgf.process.<name>` (stdout=INFO, stderr=WARNING),
    POSIX process-group cascade by default. Default `check=True`
    raises typed errors on non-zero / signal exit.
  - **`Service(command, *, ready, ...)`** — long-lived process
    with a readiness predicate + clean shutdown. Context-manager
    protocol; `start()` blocks until ready or raises
    `ServiceNotReady`. **No leaked process on failure** — service
    is fully torn down before the readiness exception is raised.
    Idempotent `start()` / `stop()`.
  - **Readiness predicates** — `PortReady(host, port)`,
    `HttpReady(url, expected_status=200)`, `LogLineReady(pattern,
    stream)`, `CallableReady(predicate)`, `AllOf([...])`,
    `AnyOf([...])`. `ReadinessProbe` Protocol for custom
    predicates. `LogLineReady` peeks into `Service`'s captured
    line history (last 1000 per stream).
  - **POSIX-first.** `process_group=True` (default) launches in a
    new session group via `start_new_session=True`; SIGTERM
    cascades via `os.killpg`. Windows is best-effort — silent
    downgrade to per-PID kill; signal-cascade semantics differ.
- **`mgf.common.exceptions` Process-launch category** — 5 new
  names, all subclasses of `OperationError` so existing
  EH-04-shaped CLI top-level handlers catch them unchanged:
  - `ProcessError` (base) — carries `.command` + `.command_id`.
  - `ProcessTimeout` — carries `.timeout`.
  - `ProcessNonZeroExit` — carries `.returncode` + `.stderr_tail`
    (last 10 lines of stderr for diagnostics).
  - `ProcessSignaled` — POSIX-only; carries `.signal`.
  - `ServiceNotReady` — carries `.probe_description`,
    `.timeout`, `.last_error`.

### Changed

- **`mgf.common.exceptions.__all__`** grew from 31 → 36 names.
  Additive — every existing import keeps working.

### Documentation

- **`docs/recipes/process-management.md`** (~370 lines) —
  comprehensive recipe covering `run()` patterns (timeout,
  redaction, log correlation, env_extra, capture-output),
  `Service` patterns (Postgres-for-test with composite
  PortReady+LogLineReady, Uvicorn smoke, multi-service via
  `ExitStack`, custom readiness via `CallableReady`,
  composite-`AnyOf` for platform-dependent ready signals),
  failure modes, testing notes, cross-references, and
  out-of-scope reminders.
- **`PUBLIC_API.md`** — new `mgf.common.process` section
  (10 names) + 5 new ProcessError leaves in the exceptions
  table. Total surface: 178 → 193 names.

### Engineering

- **65 new tests** in `tests/unit/process/` covering:
  - `test_errors.py` — taxonomy + attribute pinning.
  - `test_run.py` — basic run, error paths, log streaming
    (per-line records, level mapping, command_id/pid extras),
    name option, duration accuracy.
  - `test_readiness.py` — every predicate (PortReady,
    HttpReady, LogLineReady, CallableReady, AllOf, AnyOf) +
    composite short-circuit + `wait_for_ready` semantics.
  - `test_service.py` — context-manager + explicit start/stop
    + idempotency + readiness-failure with no-leak guarantee
    + LogLineReady stdout/stderr matching + composite probes
    + property accessors. Uses Python one-liner subprocesses as
    "services" — zero external dependencies.
- **Stability tier:** every name ships `experimental` per AP-11.
  Promotion to `stable` after one full MINOR cycle without a 🔴
  Blocker filed against any name (rule AP-11). Two consumers
  exercising real workloads is the practical bar.
- **Out of scope (deliberate):** restart-on-crash supervision
  (use systemd / supervisord), backoff machinery, multi-service
  declarative orchestration (compose `Service` context managers
  via `ExitStack`), shell-string commands (lists only —
  command-injection-safe by construction), async API
  (`mgf.common.process._aio` deferred to future when a consumer
  needs it).

---

## [0.20.0] — 2026-05-07

PyPI: <https://pypi.org/project/mgf-common/0.20.0/> · Tag: `v0.20.0`

> **Cutover guide:** [`docs/cutover/archive/v0.20.0.md`](docs/cutover/archive/v0.20.0.md)
> — documentation polish release. Three closures, all docs-only:
> framework-owned-logging recipe, `docs/standards/web/` subtree
> opening, and the documented-non-adoption principle.

**Code change:** none. `__version__` bumps from 0.19.0 → 0.20.0
to ship the new docs through the wheel's `_standards/` data
(per `[tool.hatch.build.targets.wheel.force-include]`). Standards
docs reach consumers via `mgf-common standards <topic>` so the
new paths need to land in a real release.

### Documentation

- **`docs/recipes/framework-owned-logging.md`** (closes
  [PAPER-25](FEEDBACK.md)) — promotes the
  `bootstrap(setup_logging=False)` pattern from v0.19.0's
  cutover guide to a permanent recipe with Django + FastAPI +
  Flask sections, a what-`setup_logging=False`-preserves table,
  and an explicit "what you give up" list. Cutover guides are
  time-bounded; framework-owned logging is the durable shape
  every web/API consumer needs on day one.
- **`docs/standards/web/README.md`** (closes
  [ASPIRE-03](FEEDBACK.md), Approach A — incremental) — opens
  the web-domain narrative subtree as a navigation page. Lists
  the cross-cutting recipes (currently scattered across
  cutover guides + `docs/recipes/`) and gives a
  read-in-order narrative for a new web consumer. Per-recipe
  promotion (cloud-native logging, framework config, http
  exceptions, bootstrap, stub-gaps, adoption-walk) trickles in
  over subsequent releases — each as one focused PR.
- **`docs/standards/principles/non-adoption-is-valid.md`**
  (closes [ASPIRE-04](FEEDBACK.md), "Magogi-foundation
  principle") — codifies "documented non-adoption is a
  first-class outcome of standards conformance." The framing
  behind every shipped carve-out (LOG-01, CONFIG-01,
  BOOTSTRAP-01); naming it gives newer contributors cover to
  decline a primitive when adoption would harm their context,
  AND nudges them to file the rationale upstream so the
  carve-out can be generalised. Three components: conscious
  decline, documented rationale, feedback if generalizable.
  Plus three worked examples (inthewords' LOG-01 / BOOTSTRAP-01
  pre-v0.19.0; PlasmaMapper's "small consumer" scope
  discipline).
- **`docs/standards/README.md`** — Conformance section grew a
  "Documented non-adoption preserves conformance" paragraph
  pointing at the new principle doc; "Per-kind subtrees"
  pointer to `web/README.md` added.

### Engineering

- The "incremental Approach A" structuring for ASPIRE-03 means
  v0.20.0 ships the front door; per-recipe promotion lands as
  small PRs across subsequent releases, tracked in ASPIRE-03's
  Retrospective field. This is the first release that uses an
  explicit promotion-roadmap checklist as a release artifact.
- v0.20.0 is the first **docs-only** release for `mgf-common`.
  It tests the release pipeline's tolerance for zero
  Python-surface-change releases.

---

## [0.19.0] — 2026-05-07

PyPI: <https://pypi.org/project/mgf-common/0.19.0/> · Tag: `v0.19.0`

> **Cutover guide:** [`docs/cutover/archive/v0.19.0.md`](docs/cutover/archive/v0.19.0.md)
> — composition + framework-native batch. Closes the v0.18.0
> HTTP-01 keystone (PAPER-24) and the Django adoption blocker
> (BOOTSTRAP-01).

### Fixed

- **`ExceptionTranslationMiddleware` now reads `HttpError.http_status`**
  (closes [PAPER-24](FEEDBACK.md)) — the v0.18.0 architectural
  batch shipped HTTP-01 (`HttpError` + 9 status-bearing concretes,
  each with `http_status: int` class attribute) AND the FastAPI
  middleware, but they didn't compose: a consumer rooting a domain
  exception in `HttpNotFound` still had to register every leaf in
  `status_map` for the middleware to find the 404. Resolution
  order is now: `status_map` (non-`AppError` ancestors) →
  class-declared `http_status` → `status_map[AppError]` catch-all
  → 500. Backward compatible — explicit `status_map` entries still
  win as the override seam. Regression introduced in v0.18.0;
  unblocks PlasmaMapper's `TenantNotFoundError(HttpNotFound)`
  rebase.

### Added

- **`bootstrap(setup_logging=True)` keyword-only flag** (closes
  [BOOTSTRAP-01](FEEDBACK.md) — Django adoption blocker, part 2)
  — when `False`, the StreamHandler + RotatingFileHandler install
  + per-component `level_overrides` are skipped. Identity, OTel,
  excepthooks, AppContext registration, and the bootstrap-log
  buffer replay still happen. For consumers whose framework owns
  the logging stack: Django (`LOGGING` dictConfig), FastAPI/
  uvicorn, Flask (`app.logger`), Pyramid. Default `True`
  preserves v0.18 behaviour for every existing consumer.
- **`mgf.common.alembic.configure_env` accepts `include_object=`
  + `include_schemas=`** (closes [PAPER-22](FEEDBACK.md)) —
  pass-through to `alembic.context.configure(...)`. Unblocks
  PostGIS / TimescaleDB / pgvector consumers who need to filter
  extension-owned tables (`spatial_ref_sys`,
  `_timescaledb_internal`, …) from `alembic revision
  --autogenerate`. Defaults `None` / `False` match alembic's own
  defaults — no behaviour change for current users.

### Changed

- **`settings_config()` defaults `dotenv_filtering="match_prefix"`**
  (closes [BOOTSTRAP-01](FEEDBACK.md) — Django adoption blocker,
  part 1) — `BaseAppSettings` subclasses now silently ignore
  `.env` keys that don't match their `env_prefix`, instead of
  routing them to `extra="forbid"` and raising `ValidationError`.
  This lets a `.env` be shared between mgf-common (`MGF_*`) and
  the consumer's framework (Django `SECRET_KEY`/`DB_*`,
  SQLAlchemy `DATABASE_URL`, Celery `CELERY_BROKER_URL`, …).
  **Behaviour change for consumers with `extra="allow"`** that
  were relying on the legacy permissive dotenv source to surface
  unprefixed keys onto the model — pass `dotenv_filtering=None`
  in your `model_config` override to restore the old behaviour.
  Every other `extra` mode is unaffected (`forbid` was raising
  before, `ignore` was already silent).

### Documentation

- **Editable-install caveat for `pytest_plugin`** (closes
  [PAPER-23](FEEDBACK.md)) — note added to
  `mgf.common.pytest_plugin` docstring + `docs/recipes/
  testing.md` explaining that `uv sync` against a sibling editable
  source target does not write the dist-info `entry_points.txt`,
  so the `pytest11` auto-registration is silently skipped. Opt
  in via `pytest_plugins = ["mgf.common.pytest_plugin"]` in
  consumer `tests/conftest.py`. PyPI-installed consumers are
  unaffected — the entry point fires during `pip install` /
  `uv pip install`.

### Engineering

- 4 new entries triaged + closed in one batch (PAPER-22, PAPER-23,
  PAPER-24, BOOTSTRAP-01); two from PlasmaMapper, one composite
  from inthewords. v0.19.0 is the first release that closed a
  same-release-introduced regression (PAPER-24 was a v0.18.0
  miss).

---

## [0.18.0] — 2026-05-06

PyPI: <https://pypi.org/project/mgf-common/0.18.0/> · Tag: `v0.18.0`

> **Cutover guide:** [`docs/cutover/archive/v0.18.0.md`](docs/cutover/archive/v0.18.0.md)
> — first release using the cutover-guide pattern (REL-09
> delivery mechanism). Read it for migration instructions per
> closed entry.

### Added

- **`HttpError` category in `mgf.common.exceptions`** (closes
  [HTTP-01](FEEDBACK.md)) — eighth category in the typed-
  exception taxonomy. `HttpError` base + `HttpClientError`
  (4xx) + `HttpServerError` (5xx) + nine concrete leaves
  (`HttpBadRequest` 400, `HttpUnauthorized` 401, `HttpForbidden`
  403, `HttpNotFound` 404, `HttpConflict` 409, `HttpGone` 410,
  `HttpUnprocessable` 422, `HttpInternalServerError` 500,
  `HttpServiceUnavailable` 503). Each carries `http_status: int`
  as a class attribute. 12 new names in
  `mgf.common.exceptions.__all__`, all stability tier
  `experimental`. Distinct from `ResourceNotFoundError`
  (provider-domain) by deliberate non-inheritance.
- **Framework-native config carve-out in `CONFIGURATION.md`
  §0a** (closes [CONFIG-01](FEEDBACK.md)) — explicit
  recognition that Django (and Rails-equivalent) consumers
  can't simultaneously be CF-* compliant and use
  `from django.conf import settings`. CF-01 / CF-04 / CF-08 /
  CF-09 declared N/A under framework-native; CF-02 / CF-05 /
  CF-10 / CF-12 still apply with framework-native mechanics.
  Worked example for `inthewords` (Django) included.
- **Per-rule applicability column on standards docs** (closes
  [LOG-02](FEEDBACK.md), [CONFIG-01](FEEDBACK.md)) — `Applies
  to` column on every LG-* and CF-* rule with values from
  `{all, gui-app, service, fastapi, django, cli, library,
  consumer-owned-config}`. Per-kind compliance matrix added at
  the top of `LOGGING.md`'s Rules box. `mgf-common catch-up`
  and future audit tooling filter by detected kind so
  non-applicable rules don't surface as violations.
- **Cloud-native logging recipe** (closes
  [LOG-01](FEEDBACK.md)) — new doc at
  `docs/recipes/cloud-native-logging.md` walks the
  stdout-only `MgfSettings` shape, the JSON-formatter choice,
  the deployment-time check ("the platform IS my rotating
  file sink"), and the `inthewords` worked example
  (Daphne → systemd journal → Promtail → Grafana Cloud).
- **Cutover-guide pattern** (maintainer-initiated process
  improvement) — new directory `docs/cutover/` with one
  file per release that closes ≥1 FEEDBACK entry. The first
  cutover guide is `docs/cutover/archive/v0.18.0.md` (this release's
  artifact). `RELEASING.md` §7.0 + REL-09 updated to make
  cutover guides the canonical migration source; per-PAPER
  notification paragraphs become pointers into them.

### Changed

- **LG-06 wording** (closes [LOG-01](FEEDBACK.md)) — revised
  to parameterise on deployment topology. Three first-class
  branches: rotating local file (desktop / single-server),
  process stdout/stderr (cloud-native / container), or remote
  sink (OTLP / syslog / HTTP webhook). "Console only, no
  aggregation" is non-compliant in production regardless of
  branch. The desktop branch's prescription is unchanged
  (no regression for vm-vmanager-app-shape consumers).
- **`STRATEGIC_REVIEW.md` Priority 1 marked ✅ ACHIEVED**
  (closes [ASPIRE-02](FEEDBACK.md)) — `inthewords` and
  `PlasmaMapper` arrived as the second + third independent
  consumers. PAPER-06 (reference-consumer restructure)
  unblocks for follow-up. PAPER-10 (entry-point auto-discovery)
  receives "non-pain" evidence: `inthewords` reports they
  don't need entry-point discovery; PAPER-10 stays DEFERRED.

### Fixed

- **FastAPI kind template's quick-start no longer imports
  from a private module** (closes [PAPER-21](FEEDBACK.md)) —
  one-line fix: `from mgf.common._lifecycle import bootstrap`
  → `from mgf.common import bootstrap`. The same audit that
  flags private imports on consumer src/ now also lints every
  rendered kind-template (cli / fastapi / django / library /
  service) via the new `TestQuickStartBlocksNoClosedBoxLeaks`
  regression guard. Audit-and-template asymmetry can't recur.

### Migration

See [`docs/cutover/archive/v0.18.0.md`](docs/cutover/archive/v0.18.0.md) — one
§ per closed entry with concrete steps. Headlines:

- **Pin bump:** `mgf-common = ">=0.18,<0.19"`.
- **HTTP consumers:** lift exception hierarchies onto the new
  `HttpError` category (Approach A). Existing project-level
  `AppError` shims keep working unchanged; the migration is
  structural and lands when you're ready.
- **Django consumers:** re-read `CONFIGURATION.md` §0a; declare
  CF-* applicability per the worked example.
- **Cloud-native consumers:** read the new
  `cloud-native-logging.md` recipe.
- **FastAPI consumers scaffolded against 0.17.2:** re-run
  `mgf-common catch-up --apply` to refresh the auto-block; if
  you copy-pasted the v0.17.2 quick-start, replace the
  private-module import.

---

## [0.17.2] — 2026-05-05

PyPI: <https://pypi.org/project/mgf-common/0.17.2/> · Tag: `v0.17.2`

### Added

- **`mgf-common catch-up` kind detection** (closes
  [PAPER-13](FEEDBACK.md) + [PAPER-17](FEEDBACK.md)).
  `_resolve_project_identity` now infers `--kind` heuristically
  when not supplied:
  - **With `pyproject.toml`:** parse `[project] dependencies`
    + `[project.scripts]`. `fastapi` → `fastapi`; `django` →
    `django`; `flask` → `service` (closest existing template
    kind); non-empty scripts without framework → `cli`;
    otherwise → `library`.
  - **Without `pyproject.toml`:** `manage.py` at root → `django`
    (Django's canonical layout marker). Other no-pyproject
    shapes fall through to `library`.
  - Default still `library` when no marker fires.
- **`mgf-common catch-up --apply` bootstraps `docs/CLAUDE.md`
  BEGIN/END markers** when the file exists but lacks them
  (closes [PAPER-16](FEEDBACK.md)). `_apply_bootstrap_claude_md_markers`
  reads the existing CLAUDE.md, backs it up to
  `.bak.<timestamp>`, writes a new file with the auto-block
  at the top + the original content verbatim below. The
  destructive `init-project --force` workaround is no longer
  the documented path; `catch-up --apply` is non-destructive.

### Changed

- **`mgf-common catch-up` pin audit emits a tailored info-level
  message for pip-tools layouts** (partial close of
  [PAPER-15](FEEDBACK.md)). When `pyproject.toml` is absent
  but `requirements/*.{txt,lock}` or a top-level
  `requirements.txt` is present, the audit acknowledges the
  layout and recommends the minimal-pyproject.toml workaround
  inline. Full pip-tools-manifest support (reading the pin
  from `requirements/base.txt`, four-gate audit from standalone
  config files) is deferred to a follow-up PAPER.

### Migration

None — every change is additive to existing CLI behaviour. No
public-Python-surface change. Re-run `mgf-common catch-up`
after upgrading to see the kind detection in action; existing
`--kind`-on-the-CLI invocations still take precedence over the
heuristic.

---

## [0.17.1] — 2026-05-05

PyPI: <https://pypi.org/project/mgf-common/0.17.1/> · Tag: `v0.17.1`

### Fixed

- **`mgf-common catch-up` v0.1-pattern audit no longer walks
  `.venv/`** (closes [PAPER-14](FEEDBACK.md)). The audit's
  `_count_matches` walk now applies the same skip-dirs filter
  (`tests/`, `.venv/`, `__pycache__/`, `.pytest_cache/`,
  `.mypy_cache/`, `.ruff_cache/`, etc.) that the closed-box-leak
  audit already used. Without the filter, audits on Django-shape
  projects (no `src/`) walked into `.venv/` and counted
  mgf-common's own deprecated shim definitions as user-source
  v0.1 call sites — dozens of false positives.
- **`mgf-common doctor` first-line banner reflects the imported
  package's `__version__`** (closes [PAPER-18](FEEDBACK.md)),
  not `importlib.metadata.version()`. The metadata path goes
  stale during editable-install dev workflows; reading
  `__version__` from the imported package always matches the
  live source tree per AP-13's sync rule.
- **`mgf-common <subcommand>` no longer emits
  `UnconfiguredWarning`** (closes [PAPER-19](FEEDBACK.md)). The
  CLI's `main()` calls `_set_identity("mgf-common-cli",
  __version__)` before dispatch, so subcommands that read
  identity (`explain`, `doctor`, future settings-aware
  surfaces) get the right values without firing the
  warning chain or printing `vunknown` placeholders. Lighter
  than the filing's suggested `bootstrap_for_test` because
  diagnostic CLIs MUST stay filesystem-side-effect-free.
- **`mgf-common catch-up --apply` no longer rewrites the
  pyproject pin against an editable install** (closes
  [PAPER-20](FEEDBACK.md)). Editable installs (e.g., `pip
  install -e ../mgf_common`, `[tool.uv.sources]` overrides) can
  run AHEAD of the latest PyPI-published version; rewriting a
  consumer's pin to the editable `__version__` produced an
  unsatisfiable pin on plain-PyPI resolvers. The audit row
  still surfaces staleness; the consumer bumps the pin by hand
  against `pip index versions mgf-common`.

### Migration

None — every change is a fix-in-place to existing CLI
behaviour. Re-run `mgf-common catch-up` after upgrading; you
may see fewer false positives on the v0.1-pattern row, a
correct version string on `doctor`'s banner, and clean stderr
on `explain`. Editable-install consumers see the audit warn
without auto-rewriting the pin.

---

## [0.17.0] — 2026-05-05

PyPI: <https://pypi.org/project/mgf-common/0.17.0/> · Tag: `v0.17.0`

### Added

- **`mgf-common catch-up` closed-box-leak audit** — a new audit
  row that AST-walks the consumer's `src/` and flags imports of
  `mgf-common`'s private surface. Closes [PAPER-08](FEEDBACK.md).

  Three leak shapes detected:

  1. `from mgf.common._x import …`     — module itself private
  2. `from mgf.common.X import _y`     — public module, private name
  3. `import mgf.common._x`            — direct private-module import

  The consumer's `tests/` directory is exempt (legitimate
  test-helper imports inside `_internal_test_helpers/` style
  directories aren't violations). Caches and vendored
  directories (`.venv/`, `__pycache__/`, `.pytest_cache/`,
  `.mypy_cache/`, `.ruff_cache/`, `node_modules/`, `dist/`,
  `build/`, `.tox/`) are skipped.

  Known private→public migrations are named inline in the
  warning so the audit doesn't just say "stop" — it says "do
  this instead." Today's mapping:

  | Private | Public (since) |
  |---|---|
  | `_default_log_path` | `mgf.common.observability.default_log_path` (v0.13.0) |
  | `_crash_dir` | `mgf.common.observability.default_crash_dir` (v0.13.0) |

  Extend `_PRIVATE_TO_PUBLIC` in `_catch_up.py` whenever a
  future PAPER-07-shaped private→public promotion happens.

### Migration

Consumers running `mgf-common catch-up` after upgrading will
see one new audit row. If you have any closed-box leaks in
your `src/` (you might — vm-vmanager-app had one until v0.13.0),
they surface now with the file:line and the suggested public
replacement. Like the existing `v0.1-pattern usage` row, the
fix is structural — `catch-up --apply` does NOT auto-rewrite
import statements; the consumer migrates by hand.

---

## [0.16.0] — 2026-05-02

PyPI: <https://pypi.org/project/mgf-common/0.16.0/> · Tag: `v0.16.0`

### Changed

- **Renamed** `docs/standards/SECURITY.md` →
  `docs/standards/SECURITY_STANDARD.md` to remove the
  duplicate-name collision with the repository-root
  `SECURITY.md` (vulnerability-reporting policy). The two files
  serve genuinely different purposes — SC-12 mandates the root
  policy file; the engineering standard now has a name that
  reflects what it is. The wheel's bundled-data path changes
  in lockstep: `mgf/common/_standards/SECURITY.md` →
  `mgf/common/_standards/SECURITY_STANDARD.md`.

### Migration

The CLI alias `security` keeps working unchanged — typing
`mgf-common standards security` still resolves to the engineering
standard. Both lower- (`security`) and upper-case (`SECURITY`)
inputs route through the alias map to the new canonical filename.

If your tooling parses `mgf-common standards --paths security`
output, the printed path now ends in `SECURITY_STANDARD.md`
rather than `SECURITY.md`. Update any consumer scripts that
hard-coded the old filename.

```bash
# Before — outputs .../SECURITY.md
$ mgf-common standards --paths security

# After (0.16+) — outputs .../SECURITY_STANDARD.md
$ mgf-common standards --paths security
```

The `mgf-common standards security` (no `--paths`) print-content
behaviour is unchanged.

---

## [0.15.0] — 2026-05-02

PyPI: <https://pypi.org/project/mgf-common/0.15.0/> · Tag: `v0.15.0` · SHA256 (wheel): `ad14d4ed17ca…`

### Added

- **`mgf.common.exceptions.AppConfigErrorKind`** — `StrEnum`
  discriminator for `AppConfigError` failure modes. Members:
  `FILE_NOT_FOUND`, `IO_READ_FAILED`, `JSON_PARSE_FAILURE`,
  `YAML_PARSE_FAILURE`, `NON_DICT_ROOT`, `MISSING_VERSION`,
  `UNSUPPORTED_VERSION`, `MIGRATE_FAILED`, `SCHEMA_VALIDATION`.
  String values are stable contract — usable as a machine-readable
  kind in serialised crash reports.
- **`AppConfigError.kind: AppConfigErrorKind | None`** — class-level
  attribute (default `None`). Every load-path raise in
  `mgf.common.versioned._store` now sets `kind`. Consumers stop
  string-matching on private message markers and discriminate via
  `match e.kind:`. Closes [PAPER-11](FEEDBACK.md).

### Migration

For consumers translating `AppConfigError` to project-flavoured
wording — replace string-match discriminators with
pattern-matching on `kind`:

```python
# Before — fragile to upstream rewording
except AppConfigError as e:
    raw = str(e).lower()
    if "is malformed (json)" in raw:
        raise OperationError("... is unreadable") from e
    elif "has version=" in raw or "is missing a typed" in raw:
        raise OperationError("... has unsupported schema_version") from e
    else:
        raise OperationError(f"... is malformed: {e}") from e

# After — typed, exhaustive, stable
except AppConfigError as e:
    match e.kind:
        case AppConfigErrorKind.JSON_PARSE_FAILURE | AppConfigErrorKind.YAML_PARSE_FAILURE:
            raise OperationError("... is unreadable") from e
        case AppConfigErrorKind.UNSUPPORTED_VERSION | AppConfigErrorKind.MISSING_VERSION:
            raise OperationError("... has unsupported schema_version") from e
        case _:
            raise OperationError(f"... is malformed: {e}") from e
```

Backward-compatible: every existing `except AppConfigError:` clause
keeps catching unchanged. Save-path raises don't yet opt in — file a
`PAPER-NN` if you hit pain there.

---

## [0.14.0] — 2026-05-02

PyPI: <https://pypi.org/project/mgf-common/0.14.0/> · Tag: `v0.14.0` · SHA256 (wheel): `5c172712f454…`

### Added

- **`VersionedFileStore[T](..., tolerate_missing_version=False)`** —
  new constructor flag. Default `False` keeps today's
  strict-by-default contract: missing `version:` field raises
  `AppConfigError`. `True` opts in to a grace mode for consumers
  with **pre-versioning legacy files**: a missing field logs one
  `WARNING` via the `mgf.common.versioned` logger and is treated as
  `current_version` (the migrate callback is NOT called — there is
  no version to migrate from). The next `save()` stamps the file,
  so the warning fires at most once per file. Files where the field
  is *present but not an int* still raise (corruption signal, not
  a legacy file). Closes [PAPER-09](FEEDBACK.md).

### Migration

Consumers with bespoke "missing means current" handling for
pre-versioning YAML/JSON satellite files can replace it with the
opt-in flag:

```python
store = VersionedFileStore(
    path,
    schema=ProfileFile,
    current_version=2,
    migrate=migrate,
    tolerate_missing_version=True,   # missing → v2 with WARNING
)
```

The grace-mode warning from `mgf.common.versioned` replaces any
hand-rolled warning the consumer was emitting.

---

## [0.13.0] — 2026-05-02

PyPI: <https://pypi.org/project/mgf-common/0.13.0/> · Tag: `v0.13.0` · SHA256 (wheel): `02d8ed8bc4b1…`

### Added

- **`mgf.common.observability.default_log_path`** — public helper
  that resolves to `<state>/<app>/logs/<app>.log`, where
  `<state>` honours `XDG_STATE_HOME` and falls back to
  `~/.local/state`. This is where `setup_logging` writes when
  called without an explicit `log_file=`. Surface in your UI for
  "where do my logs live?" diagnostics.
- **`mgf.common.observability.default_crash_dir`** — public helper
  resolving to `<state>/<app>/crashes/`. Where `report_now` writes
  serialised crash reports.
- **`mgf.common.observability.default_state_dir`** — public helper
  resolving to `<state>/<app>/`, the parent of both. The directory
  is NOT created — callers `mkdir` themselves if needed.

All three are lazy (resolve at call time, rule **CF-04**); per-test
isolation when monkeypatching `HOME` / `XDG_STATE_HOME` keeps
working. Closes [PAPER-07](FEEDBACK.md).

### Deprecated

- **`mgf.common.observability.logging_setup._default_log_path`** —
  was a closed-box leak (consumers had no public alternative). Now
  a deprecated shim that delegates to `default_log_path` and emits
  `MgfDeprecationWarning`. Removed in a future release.
- **`mgf.common.observability.crash_reporter._crash_dir`** — same
  shape; deprecated shim delegating to `default_crash_dir`.

### Migration

```python
# Before — reaching into the private surface (closed-box leak)
from mgf.common.observability.logging_setup import _default_log_path
from mgf.common.observability.crash_reporter import _crash_dir

# After — public API
from mgf.common.observability import default_log_path, default_crash_dir
```

The underscored names continue to work for one cycle but emit
`MgfDeprecationWarning` per call.

---

## Pre-`0.13.0`

Earlier releases (`0.1.0`, `0.3.0`, `0.4.0`, `0.4.1`, `0.4.2`,
`0.11.1`, `0.11.3`, `0.12.0`) were cut before this CHANGELOG was
introduced. The audit trail is in `git log` and the
[PyPI release pages](https://pypi.org/project/mgf-common/#history).

Notable ones (most relevant for consumers upgrading):

- **`0.12.0`** (2026-05-01) — final release before the CHANGELOG.
  Public surface that ships in this release matches the wheel on
  PyPI (`pip show mgf-common` against an installed `0.12.0`); note
  that **`mgf.common.versioned`** was added to master *after* the
  `0.12.0` PyPI cut — consumers reading `PUBLIC_API.md` from a
  master checkout may see entries that 0.12.0 doesn't ship. (This
  asymmetry is the root cause of [PAPER-12](FEEDBACK.md); the
  `PUBLIC_API.md` banner and the doc at
  [`docs/standards/RELEASING.md`](docs/standards/RELEASING.md)
  prevent its recurrence going forward.)

[Unreleased]: https://codeberg.org/magogi-admin/mgf-common/compare/v0.18.0...HEAD
[0.18.0]: https://codeberg.org/magogi-admin/mgf-common/releases/tag/v0.18.0
[0.17.2]: https://codeberg.org/magogi-admin/mgf-common/releases/tag/v0.17.2
[0.17.1]: https://codeberg.org/magogi-admin/mgf-common/releases/tag/v0.17.1
[0.17.0]: https://codeberg.org/magogi-admin/mgf-common/releases/tag/v0.17.0
[0.16.0]: https://codeberg.org/magogi-admin/mgf-common/releases/tag/v0.16.0
[0.15.0]: https://codeberg.org/magogi-admin/mgf-common/releases/tag/v0.15.0
[0.14.0]: https://codeberg.org/magogi-admin/mgf-common/releases/tag/v0.14.0
[0.13.0]: https://codeberg.org/magogi-admin/mgf-common/releases/tag/v0.13.0
